有什么办法可以确保浮点运算结果在linux和windows中都是一样的

我的程序运行在Linux和Windows中,我必须确保浮点运算在不同的操作系统中得到相同的结果。

这里是代码:

for (int i = 0; i < 100000; ++i) { float d_value = 10.0f / float(i); float p_value = 0.01f * float(i) + 100.0f; } 

我使用“ g ++ -m32 -c -static -g -O0 -ffloat-store ”在linux中构build代码。 我使用“/ fp:precise / O2”在vs2005的窗口中构build代码。

当我printf的“d_value”和“p_value”,“d_value”都在Linux和Windows相同。 但是“p_value”有时是不同的。 例如,打印hex格式的“p_value”:

 windows: 42d5d1eb linux: 42d5d1ec 

为什么要这样做呢?

我的g ++版本是

 Configured with: ../src/configure -v --with-pkgversion='Debian 4.4.5-8' --with-bugurl=file:///usr/share/doc/gcc-4.4/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.4 --enable-shared --enable-multiarch --enable-linker-build-id --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.4 --libdir=/usr/lib --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug --enable-objc-gc --enable-targets=all --with-arch-32=i586 --with-tune=generic --enable-checking=release --build=i486-linux-gnu --host=i486-linux-gnu --target=i486-linux-gnu Thread model: posix gcc version 4.4.5 (Debian 4.4.5-8) 

我使用flag -ffloat-store ,因为这里有人的build议: Linux,Mac OS X和Windows之间不同的math舍入行为

在Windows上使用/fp:strict来告诉编译器生成严格遵循IEEE 754的代码,在Linux上使用gcc -msse2 -mfpmath=sse来获得相同的行为。

您所看到的差异的原因已经在StackOverflow上进行了讨论,但最好的调查是David Monniaux的文章 。


使用gcc -msse2 -mpfmath=sse编译时获得的汇编指令如下所示。 说明cvtsi2ssqcvtsi2ssqmulssaddss是正确的使用说明,它们产生一个程序,其中p_value包含在一个点42d5d1ec

  .globl _main .align 4, 0x90 _main: ## @main .cfi_startproc ## BB#0: pushq %rbp Ltmp2: .cfi_def_cfa_offset 16 Ltmp3: .cfi_offset %rbp, -16 movq %rsp, %rbp Ltmp4: .cfi_def_cfa_register %rbp subq $32, %rsp movl $0, -4(%rbp) movl $0, -8(%rbp) LBB0_1: ## =>This Inner Loop Header: Depth=1 cmpl $100000, -8(%rbp) ## imm = 0x186A0 jge LBB0_4 ## BB#2: ## in Loop: Header=BB0_1 Depth=1 movq _p_value@GOTPCREL(%rip), %rax movabsq $100, %rcx cvtsi2ssq %rcx, %xmm0 movss LCPI0_0(%rip), %xmm1 movabsq $10, %rcx cvtsi2ssq %rcx, %xmm2 cvtsi2ss -8(%rbp), %xmm3 divss %xmm3, %xmm2 movss %xmm2, -12(%rbp) cvtsi2ss -8(%rbp), %xmm2 mulss %xmm2, %xmm1 addss %xmm0, %xmm1 movss %xmm1, (%rax) movl (%rax), %edx movl %edx, -16(%rbp) leaq L_.str(%rip), %rdi movl -16(%rbp), %esi movb $0, %al callq _printf movl %eax, -20(%rbp) ## 4-byte Spill ## BB#3: ## in Loop: Header=BB0_1 Depth=1 movl -8(%rbp), %eax addl $1, %eax movl %eax, -8(%rbp) jmp LBB0_1 LBB0_4: movl -4(%rbp), %eax addq $32, %rsp popq %rbp ret 

您的代码的精确结果不完全由IEEE和C / C ++标准定义。 这是问题的根源。

主要的问题是,虽然所有的输入都是浮点数,但这并不意味着必须以浮点精度进行计算。 如果需要,编译器可以决定对所有中间值使用双精度。 当编译x87 FPU时,这种情况往往会自动发生,但编译器(例如VC ++ 2010)可以在编译SSE代码时明确地进行这种扩展。

这不是很好理解。 几年前我在这里分享了我的理解:

http://randomascii.wordpress.com/2012/03/21/intermediate-floating-point-precision/

一些编译器让你指定中间精度。 如果您可以强制所有编译器使用相同的中间精度,那么结果应该是一致的。