4

我们在 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 客户端的问题,但我不确定我们如何才能真正验证这一点。

谢谢你的帮助。

4

3 回答 3

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 的值,并在完成后将其设置回来。这对我来说似乎是最糟糕的选择;在我上面发布的文档链接中有一个这样做的例子。
于 2012-05-22T12:33:57.600 回答
2

我在数据类型Currency上遇到了类似的问题(博客文章:Why 1.99 突然不再等于 1.99)。看起来有些 DLL 修改了 FPU(处理器)控制字。这就解释了为什么问题与机器有关。我还发现Delphi包含一个SafeLoadLibrary函数,可以恢复控制字。但这并没有太大的帮助,因为在加载 DLL 之后,任何 DLL 函数的调用仍然可能再次弄乱控制字。

于 2012-05-22T15:20:40.567 回答
1

Oracle NUMBER 数据类型是浮点数据类型,所以你有浮点/双近似的问题。请务必阅读每个计算机科学家都应该了解的关于浮点运算的知识

于 2012-05-22T12:27:34.753 回答