2012-05-22 81 views
4

我们已经有了一个表中的Oracle数据库定义为:阅读整数浮点

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

在德尔福我们在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而不是,这会导致应用程序崩溃。我们尝试过寻找问题,但它非常间歇 - 通过远程桌面连接到客户端计算机,我们无法复制它。

对于我可以做的事情有什么建议可以进一步调查?由于它是间歇性的,只发生在几台电脑上,所以很难调试。我认为这可能是Oracle客户端的问题,但我不确定我们如何才能真正验证这一点。

感谢您的任何帮助。

+1

整数在IEEE754浮点中完全可表示。必须对这样的值进行计算才能返回。似乎很奇怪。你如何将这些值放入数据库中?你是否在执行浮点计算?如果你认为这是一个整数,你为什么要作为浮点存储?如果它确实是一个整数,那么解决方案就是以整数形式存储。 –

+0

ATYPE是否真的需要Oracle NUMBER全浮点精度?或者你能否声明一些类似NUMBER(6)的东西? NUMBER根据您声明的方式来处理不同的情况。使用适当的声明还可以节省列大小的空间,由公式ROUND((length(p)+ s)/ 2))+ 1给出,其中p是精度,s取值为0或1,取决于数字标志。如果您只是使用NUMBER,p = 38。另外NUMBER不会以IEEE754格式存储数据。 BINARY_FLOAT和BINARY_DOUBLE做。有关详细信息,请参阅“Oracle数据库SQL参考”。 –

回答

13

我可以看到的唯一可能是客户端计算机上的FPU控制字之间的差异导致精度的处理方式不同,因为处理浮点类型时存在固有舍入问题。 (见Set8087CW在Delphi的文档;链接指向XE2的文档,但最近没有显著的变化,我所知道的,所以他们应该工作。)

有四种方法来解决它(一个可能性不大, 3相当容易):

  • 更改数据库列实际上是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); 
  • 明确设置808的值7CW在使用数据库之前,并在完成后将其设置回来。这似乎是我最糟糕的选择;在我上面发布的文档链接上有这样做的例子。
+1

嗨!感谢指向Set8087CW的指针 - 事实证明,问题在于第三方DLL在某些情况下摆弄了这个值,现在我们知道,我们可以阻止它。 :) – Kieran

+2

优秀!很高兴我能帮上忙。:) –

+4

如果问题出在控制字上,那么你必须做浮点计算。但如果是这样,并且你的应用程序对2和1.999999999之间的差异很敏感,那么你有一个问题。如果敏感度导致崩溃,那么我真的认为你正在做一些严重错误的事情。仅仅运行在不同的硬件上可能会导致类似的差异,并且难以维持对FP控制字的控制。如果这真的是一个整数,那么你必须从源头上处理这个问题,而不是试图对这些问题进行论证。 –

1

Oracle NUMBER数据类型是浮点数据类型,所以您有float/double近似的问题。请务必阅读What Every Computer Scientist Should Know About Floating-Point Arithmetic

+0

我知道这是一个浮点数据类型。看起来很奇怪的是,在100个中有99个返回了我们想要存储的值,并且在这种情况下它不是,而是不是所有的时间。如果它与近似有关,我认为它是一致的。 – Kieran

+0

你设置FPU控制字吗? (http://docwiki.embarcadero.com/Libraries/en/System.Set8087CW) –

+0

它应该是一致的。控制字不应该像这样读取完全可表示的值。 –

2

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

+0

谢谢,这实际上是问题所在。 – Kieran