C++ 整型到浮点型到整型转换精度损失

C++ 整型到浮点型到整型转换精度损失,c++,assembly,floating-point,C++,Assembly,Floating Point,最近,我编写了一个小程序,并使用两个不同版本的mingw32(在Windows8上)进行编译。令人惊讶的是,我得到了两个不同的结果。我试着解开它,但没发现什么特别的。有人能帮我吗?多谢各位 exe文件: 结果:720720(gcc版本4.5.2),720719(gcc版本4.7.0) 编译器标志:-lstdc++-static 代码截取如下: #include <iostream> #include <cmath> using namespace std; int

最近,我编写了一个小程序,并使用两个不同版本的mingw32(在Windows8上)进行编译。令人惊讶的是,我得到了两个不同的结果。我试着解开它,但没发现什么特别的。有人能帮我吗?多谢各位

exe文件:

结果:720720(gcc版本4.5.2),720719(gcc版本4.7.0)

编译器标志:-lstdc++-static

代码截取如下:

#include <iostream>
#include <cmath>

using namespace std;

int main()
{
    int a = 55440, b = 13;
    a *= pow(b, 1);
    cout << a << endl;
    return 0;
}
#包括
#包括
使用名称空间std;
int main()
{
INTA=55440,b=13;
a*=功率(b,1);

我已经能够用单一版本的编译器重现这个问题

我的是MingWg++4.6.2

当我将程序编译为
g++-g-O2 bugflt.cpp-o bugflt.exe
时,我得到了
720

这是对
main()
的反汇编:

如您所见,该值是在编译时计算的

当我将它编译为
g++-g-O2-fno inline bugflt.cpp-o bugflt.exe时,我得到了
720719

这是对
main()
的反汇编:

如果我将调用
exp()
替换为像这样加载13.0:

_main:
        pushl   %ebp
        movl    %esp, %ebp
        andl    $-16, %esp
        subl    $32, %esp
        call    ___main
        movl    $1, 4(%esp)
        movl    $13, (%esp)

//        call    __ZSt3powIiiEN9__gnu_cxx11__promote_2INS0_11__enable_ifIXaasrSt15__is_arithmeticIT_E7__valuesrS3_IT0_E7__valueES4_E6__typeES6_E6__typeES4_S6_
        fildl    (%esp)

        fmuls   LC1
        fnstcw  30(%esp)
        movw    30(%esp), %ax
        movb    $12, %ah
        movw    %ax, 28(%esp)
        fldcw   28(%esp)
        fistpl  4(%esp)
        fldcw   30(%esp)
        movl    $__ZSt4cout, (%esp)
        call    __ZNSolsEi
        movl    $__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, 4(%esp)
        movl    %eax, (%esp)
        call    __ZNSolsEPFRSoS_E
        xorl    %eax, %eax
        leave
        ret
_main:
        pushl   %ebp
        movl    %esp, %ebp
        andl    $-16, %esp
        subl    $32, %esp
        call    ___main
        movl    $1, 4(%esp)
        movl    $13, (%esp)

        fnstcw  30(%esp)
        movw    30(%esp), %ax
        movb    $12, %ah
        movw    %ax, 28(%esp)
        fldcw   28(%esp)

        call    __ZSt3powIiiEN9__gnu_cxx11__promote_2INS0_11__enable_ifIXaasrSt15__is_arithmeticIT_E7__valuesrS3_IT0_E7__valueES4_E6__typeES6_E6__typeES4_S6_

        fldcw   30(%esp)

        fmuls   LC1
        fnstcw  30(%esp)
        movw    30(%esp), %ax
        movb    $12, %ah
        movw    %ax, 28(%esp)
        fldcw   28(%esp)
        fistpl  4(%esp)
        fldcw   30(%esp)
        movl    $__ZSt4cout, (%esp)
        call    __ZNSolsEi
        movl    $__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, 4(%esp)
        movl    %eax, (%esp)
        call    __ZNSolsEPFRSoS_E
        xorl    %eax, %eax
        leave
        ret
我得到了
720720

如果我为
exp()
期间的x87 FPU控制字设置了与
fistpl 4(%esp)
指令相同的舍入和精度控制字段,如下所示:

_main:
        pushl   %ebp
        movl    %esp, %ebp
        andl    $-16, %esp
        subl    $32, %esp
        call    ___main
        movl    $1, 4(%esp)
        movl    $13, (%esp)

//        call    __ZSt3powIiiEN9__gnu_cxx11__promote_2INS0_11__enable_ifIXaasrSt15__is_arithmeticIT_E7__valuesrS3_IT0_E7__valueES4_E6__typeES6_E6__typeES4_S6_
        fildl    (%esp)

        fmuls   LC1
        fnstcw  30(%esp)
        movw    30(%esp), %ax
        movb    $12, %ah
        movw    %ax, 28(%esp)
        fldcw   28(%esp)
        fistpl  4(%esp)
        fldcw   30(%esp)
        movl    $__ZSt4cout, (%esp)
        call    __ZNSolsEi
        movl    $__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, 4(%esp)
        movl    %eax, (%esp)
        call    __ZNSolsEPFRSoS_E
        xorl    %eax, %eax
        leave
        ret
_main:
        pushl   %ebp
        movl    %esp, %ebp
        andl    $-16, %esp
        subl    $32, %esp
        call    ___main
        movl    $1, 4(%esp)
        movl    $13, (%esp)

        fnstcw  30(%esp)
        movw    30(%esp), %ax
        movb    $12, %ah
        movw    %ax, 28(%esp)
        fldcw   28(%esp)

        call    __ZSt3powIiiEN9__gnu_cxx11__promote_2INS0_11__enable_ifIXaasrSt15__is_arithmeticIT_E7__valuesrS3_IT0_E7__valueES4_E6__typeES6_E6__typeES4_S6_

        fldcw   30(%esp)

        fmuls   LC1
        fnstcw  30(%esp)
        movw    30(%esp), %ax
        movb    $12, %ah
        movw    %ax, 28(%esp)
        fldcw   28(%esp)
        fistpl  4(%esp)
        fldcw   30(%esp)
        movl    $__ZSt4cout, (%esp)
        call    __ZNSolsEi
        movl    $__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, 4(%esp)
        movl    %eax, (%esp)
        call    __ZNSolsEPFRSoS_E
        xorl    %eax, %eax
        leave
        ret
我也得到了
720

从这一点我只能得出结论,
exp()
并没有像13.0那样精确地计算131

可能值得一看该函数的源代码:::\uuuuuuu promote\u2::\uuuu type std::pow(int,int)
以了解它是如何处理整数的幂运算的(参见,与C的
exp()
不同,它需要两个
整数而不是两个
双倍整数)

但是我不会为此责备
exp()
。C++11定义了
float-pow(float,float)
long-double-pow(long-double,long-double)
以及C的
double-pow(double,double)
。但是标准中没有
double-pow(int,int)

编译器为整型参数提供了一个版本这一事实并不能对结果的精度做出任何额外的保证

ab=2b*log2(a)

或作为

ab=eb*ln(a)

对于浮点值,过程中肯定会有舍入错误

如果“整数”版本的
exp()
做了类似的事情,并且由于舍入错误导致了类似的精度损失,那么它仍然正确地完成了它的工作。即使精度损失是由于一些愚蠢的错误,而不是因为正常的舍入错误


无论这种行为看起来多么令人惊讶,它都是正确的。或者说,在证明它是错误的之前,我相信它是正确的。

结果是什么?编译器标志是什么?为什么要编写这样奇怪的代码?这是在项目中发现的问题,出于测试目的,我编写了这段代码。@pubby通常,您应该避免使用浮点算术进行整数计算但是,如果你真的需要混合它们,考虑转换为int:<代码> a= int(0.5 +a*PoW(b,1))时的舍入;<代码> PoW(b,1)。
返回的是12.9999…而不是13。@chenzhekl,你是如何验证寄存器的内容是
13
?你能看到扩展精度位吗?也许你的调试器/输出被四舍五入为标准的
双精度
类型,显示
13
,而不是显示扩展精度位。非常感谢太多了!这对我帮助很大。