从数据库读取整数返回浮点数

时间:2012-05-22 12:12:18

标签: oracle delphi floating-point

我们在Oracle DB中有一个表定义为:

CREATE TABLE AVALUES
(
  ACODE   VARCHAR2(4) NOT NULL,
  ATYPE   NUMBER NOT NULL,
  ANAME   VARCHAR2(50),
  CREATED DATE DEFAULT SYSDATE
)

在Delphi中,我们在一个类似于此的ADOQuery组件中有一个查询,它将值返回给我们的应用程序:

with qryComp do
begin
  Close;
  SQL.Text := 
    'SELECT ATYPE FROM AVALUES ORDER BY CREATED';
  Open;
  while not EOF do
  begin
    AddComponents('NAME' + FieldByName('ATYPE').AsString);
    Next;
  end;
  Close;
end;

部署在许多不同的客户端PC上,这已经好多年了,我们的代码中没有任何内容发生变化。在一些客户端PC上,它最近开始返回,例如, 1.999999999969 而不是 2 ,这会导致应用程序崩溃。我们已经尝试寻找问题,但它非常间歇性 - 通过远程桌面连接到客户端计算机,我们根本无法复制它。

我可以采取哪些建议来进一步调查此事?因为它是间歇性的,只发生在几台计算机上,所以很难调试。我认为这可能是Oracle客户端的问题,但我不确定我们如何才能真正验证它。

感谢您的帮助。

3 个答案:

答案 0 :(得分:13)

我唯一能看到的可能是客户机上FPU控制字之间的差异导致精度处理方式不同,因为处理浮点类型存在固有的舍入问题。 (请参阅Delphi文档中的Set8087CW;该链接适用于XE2的文档,但最近我没有发现任何重大更改,因此它们应该有效。)

有四种方法可以解决它(一种方法不太可能,三种方法很容易):

  • 将数据库列更改为integer类型,而不是NUMBER

  • 直接询问integer值,并自行转换

    AddComponent('Name' + IntToStr(FieldByName('ATYPE').AsInteger));

  • 在使用地点更改使用该列的代码:

    AddComponents(Format('NAME%d', [FieldByName('ATYPE').AsInteger]));

AddComponents本身:

procedure AddComponents(Prefix: string; Value: Integer);
begin
  DoWhateverIDo(Prefix + IntToStr(Value));
end;

// calling code
AddComponents('Name', FieldByName('ATYPE').AsInteger);
  • 在使用数据库之前显式设置8087CW的值,并在完成后将其设置回来。这对我来说似乎是最糟糕的选择;在我上面发布的文档链接中有一个这样做的例子。

答案 1 :(得分:2)

我遇到类似数据类型货币的问题(博客文章:"Why 1.99 suddenly no longer equals 1.99")。看起来有些DLL会修改FPU(处理器)控制字。这解释了为什么问题与机器有关。 我还发现Delphi包含一个SafeLoadLibrary函数,它可以恢复控制字。但它不是一个很大的帮助,因为加载DLL后,仍然可以调用DLL函数再次搞乱控制字。

答案 2 :(得分:1)

Oracle NUMBER数据类型是浮点数据类型,因此您有浮点/双精度近似问题。请务必阅读What Every Computer Scientist Should Know About Floating-Point Arithmetic