другое поведение или sqrt при компиляции с 64 или 32 битами

Я использую функцию sqrt () из математической библиотеки, когда я строю для 64-битной версии с использованием -m64, я получаю правильный результат, но когда я строю для 32-битной версии, у меня очень непоследовательное поведение.

Например на 64bit

double dx = 0x1.fffffffffffffp+1023;
sqrt(dx); // => 0x1.fffffffffffffp+511
sqrt(0x1.fffffffffffffp+1023);// => 0x1.fffffffffffffp+511

(который, я считаю, является правильно округленным результатом, подтвержденным с помощью mpfr)

Но на 32-битном одном и том же входном значении он ведет себя по-другому.

double dx = 0x1.fffffffffffffp+1023;
sqrt(dx); // => 0x1.0p+512
sqrt(0x1.fffffffffffffp+1023); // => 0x1.fffffffffffffp+511

Когда в переменную передается то же значение, я получаю неверный результат. Я проверил режим округления до и после каждого вызова, и все настроены на округление до ближайшего. В чем причина? Я использую gcc 4.6 на 64-битной машине, и варианты -mfpmath=sse и -march=pentium для обоих случаев x86 и x64.


person kanna    schedule 30.05.2012    source источник
comment
Похоже, здесь обнаружена одна ошибка на sources.redhat.com/bugzilla/ show_bug.cgi? id = 14032, даже когда я использую -msse2 для 32-битного кода i386, это тот, который выполняется.   -  person kanna    schedule 30.05.2012


Ответы (2)


Вы не сказали, какой компилятор или архитектуру вы используете, но если предположить gcc на x86 / x86-64, тогда разница, вероятно, сводится к тому факту, что по умолчанию gcc использует 387 инструкций с плавающей запятой на 32-битном x86, тогда как он использует инструкции SSE на x86-64.

387 регистров с плавающей запятой имеют ширину 80 бит, а double - 64 бита. Это означает, что промежуточные результаты могут иметь более высокую точность при использовании инструкций 387, что может привести к несколько иному ответу после округления. (Инструкции SSE2 работают с упакованными 64-битными двойниками).

Есть несколько способов изменить способ работы компилятора, в зависимости от того, что вы хотите:

  • Если вы используете опцию -ffloat-store в сборках x86, компилятор откажется от дополнительной точности всякий раз, когда вы сохраняете значение в переменной double;
  • Если вы используете параметры -mfpmath=sse в сборках x86 вместе с переключателем -msse2 или -march=, который указывает архитектуру, поддерживающую SSE2, компилятор будет использовать инструкции SSE для плавающей запятой так же, как на x86-64. Однако код будет работать только на процессорах, поддерживающих SSE2 (Pentium-M / Pentium 4 и новее).
  • Если вы используете опцию -mfpmath=387 в сборках x86-64, компилятор будет использовать 387 инструкций для чисел с плавающей запятой так же, как и на x86. Однако это не рекомендуется - x86-64 ABI указывает, что значения с плавающей запятой передаются в регистрах SSE, поэтому компилятору приходится выполнять много операций переключения между регистрами 387 и SSE с этой опцией.
person caf    schedule 30.05.2012
comment
Я использую gcc 4.6 на 64-битной машине, и варианты -mfpmath=sse и -march=pentium для обоих случаев x86 и x64. Разве я не должен получить одинаковый результат в обоих случаях? Меня больше всего интересует 32-битная сборка, поэтому я думаю, -mfpmath=387 может дать мне правильный результат, если нет других проблем - person kanna; 30.05.2012
comment
@giridhart: -march=pentium включает только SSE1, который выполняет только одинарную точность - для двойной точности он должен вернуться к 387, что дает бонусную точность. Вы хотите -msse2 или -march=pentium-m или что-то подобное вместе с -mfpmath=sse. - person caf; 30.05.2012
comment
На самом деле я использую -mfpmath=sse и -msse. Теперь я перешел на -mfpmath=sse2, но все равно получаю неправильный результат. пожалуйста, предложите правильный вариант для решения этой проблемы. Спасибо - person kanna; 30.05.2012

Некоторые компиляторы, такие как gcc, когда видят определенные функции математической библиотеки, выполняемые со статическими литералами, фактически вычисляют значение во время компиляции, тогда как, как и в случае с переменной, оно по необходимости вычисляется во время выполнения. Значение времени компиляции обычно рассчитывается компилятором с использованием математической библиотеки, такой как MPFR, GNU MP и т. Д., Поэтому результаты будут более точными или, по крайней мере, максимально точными для разных платформ.

person Jason    schedule 30.05.2012
comment
Я также, кажется, помню, что gcc может выполнять математические вычисления во время компиляции с литералами, используя 80-битную точность на x86, в то время как значение, хранящееся в double, должно быть уменьшено до 64-битной точности. - person hobbs; 30.05.2012
comment
Да, я думаю, что причина правильного результата при использовании константы. Есть ли способ исправить проблему с 32-битной библиотекой? Аналогичная проблема указана при переполнении стека, но она связана с C # stackoverflow.com/questions/2461319/ - person kanna; 30.05.2012
comment
@giridhart: Судя по вашему сообщению, похоже, что ваши 32-битные и 64-битные результаты с литералом совпадают ... вы говорите, что они разные? - person Jason; 30.05.2012
comment
@Jason: Да, я должен был добавить оба корпуса для x64. На x64 и буквальный, и переменный регистр дают правильный результат, тогда как на x86 только буквальный регистр - person kanna; 30.05.2012