我正在调试我的项目,但找不到错误。最后我找到了它。看一下代码。你认为一切都好,结果就会是“好!好的!好吧!”,不是吗?现在用VC编译它(我试过vs2005和vs2008)。

#include <math.h>
#include <stdio.h>


int main () {
    for ( double x = 90100.0; x<90120.0; x+=1 )
    {
        if ( cos(x) == cos(x) )
            printf ("x==%f  OK!\n", x);
        else
            printf ("x==%f  FAIL!\n", x);
    }

    getchar();
    return 0; 
}

神奇的双常数是 90112.0。当 x < 90112.0 时一切正常,当 x > 90112.0 时 -- 不!您可以将 cos 更改为 sin。

有任何想法吗?不要忘记 sin 和 cos 是周期性的。

有帮助吗?

解决方案

可能是这样的: HTTP://www.parashift的.com / C ++ - FAQ-精简版/ newbie.html#FAQ-29.18

  

我知道这很难接受,但浮点运算根本不起作用像大多数人期望的那样。更糟的是,有些差异是依赖于您的特定计算机的浮点硬件和/或您在特定的编译器使用优化设置的细节。你可能不喜欢,但它的事情是这样的。只有这样,才能“了解”就是放下你的假设的事情是如何的应该的表现和接受的事情,因为他们实际上的的循规蹈矩......

     

(强调词“经常”;行为取决于硬件,编译器,等等):浮点计算和比较通常是由通常含有特殊寄存器特殊的硬件进行的,以及那些寄存器往往具有更多的位比double。这意味着,中间浮点运算通常比sizeof(double)更多的比特,并且当一个浮点值被写入到RAM,它经常被截断,常常失去精度一些比特...

     

请记住这一点:浮点比较是棘手和微妙的,充满了危险。小心。路浮点的真正的工作原理是从不同的方式,大多数程序员倾向于认为它的应该的工作。如果你打算使用浮点数,你需要了解它是如何实际工作...

其他提示

正如其他人所指出的,VS 数学库在 x87 FPU 上进行计算,并生成 80 位结果,即使类型是 double 也是如此。

因此:

  1. cos( ) 被调用,并以 80 位浮点形式返回 x87 堆栈顶部的 cos(x)
  2. cos(x) 从 x87 堆栈中弹出并作为双精度值存储到内存中;这会导致它四舍五入为 64 位浮点数,这 改变它的值
  3. cos( ) 被调用,并以 80 位浮点形式返回 x87 堆栈顶部的 cos(x)
  4. 舍入后的值从内存加载到 x87 堆栈上
  5. cos(x) 的舍入值和未舍入值比较不相等。

许多数学库和编译器通过在可用的 SSE 寄存器中以 64 位浮点数进行计算,或者在比较之前强制存储和舍入值,或者在实际计算中存储并重新加载最终结果来保护您免受这种情况的影响的 cos( ) 。您碰巧使用的编译器/库组合并不那么宽容。

在COS(X)== COS(x)的在释放模式生成过程:

00DB101A  call        _CIcos (0DB1870h) 
00DB101F  fld         st(0) 
00DB1021  fucompp 

的值被计算一次,然后克隆,然后用本身相比 - 的结果将是确定

在调试模式下相同的:

00A51405  sub         esp,8 
00A51408  fld         qword ptr [x] 
00A5140B  fstp        qword ptr [esp] 
00A5140E  call        @ILT+270(_cos) (0A51113h) 
00A51413  fld         qword ptr [x] 
00A51416  fstp        qword ptr [esp] 
00A51419  fstp        qword ptr [ebp-0D8h] 
00A5141F  call        @ILT+270(_cos) (0A51113h) 
00A51424  add         esp,8 
00A51427  fld         qword ptr [ebp-0D8h] 
00A5142D  fucompp          

现在,奇怪的事情发生了。结果 1. X被装载在fstack(X,0),点击 2. X被存储在正常栈(截断),点击 3.余弦计算,导致上浮动堆结果 4. X装载再次点击 5. X被存储在正常栈(截短,如现在,我们是“对称的”),点击 6.第一余弦的,这是在堆栈上的结果被存储在存储器中,现在,另一个截断时为第一值,点击 7.余弦计算,第二结果如果浮子堆栈上,但该值被截断只有一次点击 8.第一值被加载到fstack,但该值被截断两次(一次计算余弦,后一次之前),点击 9.这些2个值进行比较 - 我们得到的舍入误差

您应该<击>从不比不上在大多数情况下,平等的双打。你可能不会得到你所期望的。

浮点寄存器可以具有不同的尺寸比存储器中的值(在当前Intel机器,FPU寄存器是80位和64位加倍)。如果编译器产生,其计算所述第一余弦代码,然后存储值到存储器中,计算第二余弦和的值在存储器中与在寄存器进行比较,则值可以不同(由于80至64位的舍入问题) 。

浮点值多少有些复杂。谷歌浮点comparissons。

编译器可能已经生成结束了比较的一个64位双精度值的代码 80位的内部浮点寄存器。测试相等浮点值 很容易出现这类错误的 - 你几乎总是更好做像一个“模糊”的比较(晶圆厂(VAL1 - VAL2)

递增和测试的浮点值作为循环控制变量通常是非常糟糕的主意。只是循环上创建一个单独的INT LCV,如果你不得不这样做。

在这种情况下,它更容易:

for ( int i = 90100; i<90120; i+=1 )    {
    if ( cos(i) == cos(i) )
        printf ("i==%d  OK!\n", i);
    else
        printf ("i==%d  FAIL!\n", i);
}

如何围绕问题?修改如果块:结果

if ( (float)cos(x) == (float)cos(x) )
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top