Поскольку вы сказали, что вам нужен более точный результат ...01
, а также детерминизм, вы, к сожалению, не можете просто использовать -msse2 -mfpmath=sse
в своей 32-битной сборке.Будущие читатели, ищущие детерминизм, должны использовать это.
Вы можете использовать -mfpmath=387
, чтобы попросить gcc использовать медленную / устаревшую математику x87 в 64-битном режиме, где это не по умолчанию.Соглашение о вызовах передает / возвращает аргументы FP в регистрах xmm, так что это даже хуже, чем в 32-битном режиме, иногда требуя дополнительного сохранения / перезагрузки.
peter@volta:/tmp$ gcc -m64 -mfpmath=387 -O3 fp-prec.c -o fp-64-387
peter@volta:/tmp$ ./fp-64-387
64 bits: 13.904254700000001
Я не уверен, строго ли gcc ограничивает себядо x87, когда возможна автовекторизация.Если это так, то вам не хватает производительности.
И кстати, в вашем примере ...01
является результатом сохранения точности extra в 80-битном временном коде дляx*30.07
перед добавлением его к d
.(d
равно volatile
, но d += stuff
по-прежнему эквивалентно d = d + stuff
, поэтому x*30.07
не округляется до 64-разрядного double
первого).
Youможно использовать long double
, например, d += x * (long double)30.07
для принудительной установки 80-битного временного значения. long double
- это 80 бит в x86-64 System V ABI Linux / OS X / * BSD / etc, но в Windows x64это так же, как 64-битный double
.Так что это может быть не вариант для вас.
В этом случае вы можете получить тот же результат с FMA, который сохраняет бесконечную точность для умножения перед выполнением сложения.Это медленно на оборудовании без поддержки FMA, но fma(d, 30.07, x)
надежно даст желаемый результат.
Если вам это нужно, используйте его там, где требуется такая точность.
ЕслиВы компилируете с включенным FMA, он может быть встроен в инструкцию FMA.(например, -march=native
на моем процессоре Skylake)
Даже без использования функции fma()
math.h, gcc будет сокращать выражения mul + и добавлять их в FMA при оптимизации.(В отличие от Clang, который, я думаю, не делает FP_CONTRACT
по умолчанию без -ffast-math
).Обратите внимание, что я не , используя -march=387
# your original source code, using an FMA instruction (native=skylake in my case)
peter@volta:/tmp$ gcc -m64 -march=native -O3 fp-prec.c -o fp-64-native
peter@volta:/tmp$ ./fp-64-native
64 bits: 13.904254700000001
Соответствующая часть main
:
57e: c5 fb 10 44 24 08 vmovsd xmm0,QWORD PTR [rsp+0x8] # load x
584: c5 fb 10 0c 24 vmovsd xmm1,QWORD PTR [rsp] # load d
589: c4 e2 f1 99 05 d6 01 00 00 vfmadd132sd xmm0,xmm1,QWORD PTR [rip+0x1d6] # the 30.07 constant
592: c5 fb 11 04 24 vmovsd QWORD PTR [rsp],xmm0 # store d
597: c5 fb 10 04 24 vmovsd xmm0,QWORD PTR [rsp] # reload d
59c: e8 8f ff ff ff call 530 <printf@plt>
FP детерминизм жесткий в целом.
См. Также https://randomascii.wordpress.com/2013/07/16/floating-point-determinism/ и https://randomascii.wordpress.com/2012/03/21/intermediate-floating-point-precision/