C++ 整型到浮点型到整型转换精度损失
最近,我编写了一个小程序,并使用两个不同版本的mingw32(在Windows8上)进行编译。令人惊讶的是,我得到了两个不同的结果。我试着解开它,但没发现什么特别的。有人能帮我吗?多谢各位 exe文件: 结果:720720(gcc版本4.5.2),720719(gcc版本4.7.0) 编译器标志:-lstdc++-static 代码截取如下: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
#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
,而不是显示扩展精度位。非常感谢太多了!这对我帮助很大。