2013-05-06 14 views
5

Моя программа работает как в linux, так и в окнах, я должен убедиться, что арифметика с плавающей запятой получает одинаковый результат в разных ОС.Есть ли способ убедиться, что арифметический результат с плавающей точкой одинаковый как в 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; 
} 

Я использую "г ++ -m32 -c -g -static -O0 -ffloat-магазин" для построения кода в Linux. Я использую «/ fp: exact/O2» для создания кода в окнах с vs2005.

Когда я печатаю «d_value» и «p_value», «d_value» все равно как в linux, так и в окнах. Но иногда «p_value» отличается. Для exsample, распечатайте "p_value" с шестнадцатеричным форматом:

windows: 42d5d1eb 
linux: 42d5d1ec 

Почему дозой это произошло?

версия

Мой г ++ является

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) 

Я использую флаг -ffloat-store, из-за чьей-то предложение здесь: Different math rounding behaviour between Linux, Mac OS X and Windows

+0

Как насчет использования [MPFR] (http://www.mpfr.org/)? – hek2mgl

+0

Для какого значения 'i' вы наблюдаете данный выход? –

+0

Язык и компилятор - это не все, что находится под контролем здесь, и в основном такой подход не приведет к сопоставлению результатов. Например, синтаксический анализатор будет использовать некоторую C или другую библиотеку для преобразования строки чисел и десятичной точки в двоичное число с плавающей запятой. эти библиотеки различаются, в частности, между операционными системами, компиляторами и т. д. Таким образом, даже если код был идентичен, значения, вводимые кодом, могут различаться, и в результате выход изменяется. IEEE754 также противен в отношении округления. –

ответ

7

Использование /fp:strict на Windows, чтобы сообщить компилятору производить код, который строго следует IEEE 754, и gcc -msse2 -mfpmath=sse на Linux, чтобы получить там такое же поведение.

Причины различий, которые вы видите, обсуждались в пятнах на StackOverflow, но лучшим обзором является article David Monniaux.


Инструкция по сборке я получаю при компиляции с gcc -msse2 -mpfmath=sse являются следующим. Инструкции cvtsi2ssq, divss, mulss, addss являются правильными инструкциями по использованию, и они приводят к программе, в которой 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 [email protected](%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 
+0

Спасибо за ваш ответ. Но это не работает. Разные все еще происходят. Моя версия g ++ - «gcc версия 4.4.5 (Debian 4.4.5-8)». – hdbean

+0

Возможно, вам поможет модернизация GCC. Текущая версия GCC - 4.8. –

+1

@hdbean '42d5d1ec' - правильное значение для получения в качестве одного из значений переменной' p_value'. Я проверил это, прочитав сборку, созданную моим собственным GCC, и убедился, что использует правильные инструкции. Является ли ваш Visual компилятор генерирует инструкции SSE2 для плавающей запятой? Сложно и дорого вывести точное правильное вычисление без этих инструкций, что нет никаких шансов, что он будет генерировать правильное вычисление, не используя их (как объясняется в статье, ссылки на мои ответы). –

1

Точные результаты вашего кода не полностью определены стандартами IEEE и C/C++. Это и есть источник проблемы.

Основная проблема заключается в том, что, хотя все ваши входы - это поплавки, которые не означают, что расчет должен выполняться с точностью поплавка. Компилятор может решить использовать двойную точность для всех промежуточных значений, если захочет. Это происходит автоматически при компиляции для x90 FPU, но компилятор (например, VC++ 2010) может сделать это расширение явно, если он хочет даже при компиляции кода SSE.

Это не совсем понятно. Я поделился своим пониманием этого несколько лет назад здесь:

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

Некоторые компиляторы позволяют установить промежуточную точность. Если вы можете заставить все компиляторы использовать ту же промежуточную точность, то ваши результаты должны быть последовательными.

+0

Я представил дополнительную информацию в этом вопросе, где возникает проблема избыточной точности: http://stackoverflow.com/a/23518798/139746. Как я уже сказал, «C99 допускает дополнительную точность для выражений с плавающей запятой, которая в прошлом неправильно интерпретировалась разработчиками компилятора как лицензия на непредсказуемость поведения с плавающей запятой». Этот вопрос конкретно относится к VS2005, который не совместим с C99, поэтому единственной жизнеспособной стратегией является то, что можно получить и запустить. То, что можно получить, - это режим «FLT_EVAL_METHOD = 0». Забудьте о том, чтобы сделать какую-либо другую работу промежуточной точности, это не так. –

+0

Например, C99 требует, чтобы десятичные константы с плавающей запятой были оценены с промежуточной точностью, а не точностью тип. Это то, что делает GCC, потому что это то, что требуется стандарту, но Clang этого не делает (разработчикам Clang не очень-то нравится режим FLT_EVAL_METHOD = 2). Я также сомневаюсь, что это то, что делает VS2005 (но я не знаю У меня есть этот компилятор). –

+0

На самом деле я был бы рад получить информацию о компиляторах Microsoft в дополнение к этому сообщению в блоге, если у вас есть доступ к некоторым из них: http://blog.frama-c.com/index. PHP? пост/2013/07/24/More-на-FLT_EVAL_METHOD_2 –

Смежные вопросы