Visual C++ math.h错误

f0b*_*b0s 4 c++ floating-point math.h

我正在调试我的项目,但找不到错误.最后我找到了它.看看代码.你认为一切都好,结果将是"好的!好的!好的!",不是吗?现在用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; 
}
Run Code Online (Sandbox Code Playgroud)

神奇的双常数是90112.0.当x <90112.0一切正常时,当x> 90112.0时 - 不!你可以将cos改为罪.

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

Rog*_*mbe 36

可能是这个:http://www.parashift.com/c++-faq-lite/newbie.html#faq-29.18

我知道这很难接受,但浮点运算根本不像大多数人预期的那样有效.更糟糕的是,一些差异取决于您的特定计算机的浮点硬件和/或您在特定编译器上使用的优化设置的详细信息.你可能不喜欢这样,但事实就是如此."得到它"的唯一方法就是抛开关于事物应该如何表现的假设,接受事物的确如此行为......

(强调"经常"一词;行为取决于您的硬件,编译器等):浮点计算和比较通常由通常包含特殊寄存器的特殊硬件执行,而这些寄存器通常具有比a更多的位double.这意味着中间浮点计算通常具有更多位sizeof(double),并且当浮点值写入RAM时,它经常被截断,通常会丢失一些精度......

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


Ste*_*non 10

正如其他人所指出的那样,VS数学库正在x87 FPU上进行计算,即使类型为double,也会生成80位结果.

从而:

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

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


Dav*_*eas 5

在大多数情况下,你绝不应该将双打比较为平等.你可能得不到你的期望.

浮点寄存器可以具有与存储器值不同的大小(在当前的intel机器中,FPU寄存器是80位对64位双倍).如果编译器生成计算第一个余弦的代码,则将该值存储到存储器中,计算第二个余弦并将存储器中的值与寄存器中的值进行比较,然后值可能不同(由于80到64位的舍入问题) .

浮点值有点棘手.谷歌的浮点比较.


Mar*_*uła 5

在发布模式下生成的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.比较这两个值 - 我们得到舍入误差.