C++两种等价double除法运算写法结果不一致原因咨询
两段C++浮点代码结果差异的核心原因
差异本质来自x87 FPU的80位扩展精度计算规则,以及C++标准对浮点中间结果精度的宽松规定,和你引入的math.h库没有关系,具体逻辑如下:
- 你使用的32位MinGW编译环境默认调用x87浮点单元做运算:x87的内部计算寄存器是80位扩展双精度格式,尾数长度64位,远长于标准
double类型的53位尾数,计算过程中如果中间结果不写入内存的double变量,会一直以80位精度保存在寄存器里,不会截断成64位double的精度。 - 第一段代码的执行流程:
这里double a= 1e-14; double b= 1e-7; double val=0; val+=b; val-=(a/b);a/b属于表达式中间结果,没有被赋值给独立的double变量,编译器不会主动把它截断成64位精度,全程在80位寄存器中计算。注意1e-7不是2的整数次幂,无论用什么二进制浮点格式存储都是近似值:a本身是b*b舍入到64位double的结果,在80位精度下计算a/b时,乘除操作的舍入误差会导致结果和寄存器中存储的b(也就是val += b后的值)存在极微小的尾数差,两者相减后残差约为1e-24量级,最终写回val时就保留了这个极小的非零结果。 - 第二段代码的执行流程:
这里你显式定义了double a= 1e-14; double b= 1e-7; double val=0; val+=b; double val2= a/b; val-=val2;double类型的变量val2存储a/b的结果,按照C++的类型规则,编译器必须把80位寄存器里的计算结果截断为64位双精度,再写入val2对应的内存位置。截断后的val2和val中存储的64位精度的1e-7值完全一致,两者相减的结果就是精确的0。
差异的验证与消除方法
这个现象是32位x86平台x87浮点运算的典型表现,不是代码逻辑错误:
- 如果你切换到64位MinGW环境编译,默认会使用SSE2指令集做浮点运算,SSE2的寄存器是原生64位双精度,没有80位扩展精度,两段代码运行结果会完全一致
- 如果你给32位MinGW加编译选项
-msse2 -mfpmath=sse指定用SSE2做浮点运算,或者加-ffloat-store强制所有浮点中间结果写入内存截断为对应类型精度,两段代码结果也会一致- 开启高等级优化(比如
-O2)时,编译器会做常量折叠计算,直接在编译期算出所有结果,两段代码的结果也会统一。
内容的提问来源于stack exchange,提问作者all
相关产品推荐
相关产品推荐

