Число 64-разрядных чисел с плавающей запятой ведет себя одинаково на всех современных компьютерах?

Я хотел бы знать, могу ли я предположить, что те же самые операции в одних и тех же 64-битных числах с плавающей запятой дают точно такие же результаты на любом современном ПК и в большинстве распространенных языков программирования? (С++, Java, С# и т.д.). Мы можем предположить, что мы работаем по числам, а результат также является числом (без NaN, INF и т.д.).

Я знаю, что существуют два очень простых стандарта вычислений с использованием чисел с плавающей запятой (IEEE 854-1987 и IEEE 754-2008). Однако я не знаю, как это происходит на практике.

Ответ 1

Современные процессоры, реализующие 64-разрядную плавающую точку, обычно реализуют нечто, близкое к стандарту IEEE 754-1985, недавно замененное стандартом 754-2008.

В стандарте 754 указывается, какой результат вы должны получить от определенных основных операций, в частности сложения, вычитания, умножения, деления, квадратного корня и отрицания. В большинстве случаев точно определяется числовой результат: результат должен быть представимым числом, которое ближе всего к точному математическому результату в направлении, указанном режимом округления (до ближайшего, к бесконечности, к нулю или к отрицательной бесконечности). В режиме "до ближайшего" стандарт также указывает, как разорваны связи.

Из-за этого операции, не связанные с такими условиями исключения, как переполнение, получат одинаковые результаты на разных процессорах, соответствующих стандарту.

Однако есть несколько проблем, которые мешают получать идентичные результаты на разных процессорах. Один из них заключается в том, что компилятор часто может свободно выполнять последовательности операций с плавающей запятой различными способами. Например, если вы пишете "a = bc + d" в C, где все переменные объявляются double, компилятор может свободно вычислять "bc" либо в арифметике с двойной точностью, либо в более широком диапазоне или точности. Если, например, у процессора есть регистры, способные удерживать числа с плавающей запятой с плавающей запятой и выполнение арифметики с расширенной точностью не требует больше времени процессора, чем выполнение арифметики с двойной точностью, компилятор, скорее всего, сгенерирует код с использованием расширенных -precision. На таком процессоре вы можете получить те же результаты, что и на другом процессоре. Даже если компилятор делает это регулярно, это может быть не в некоторых случаях, потому что регистры заполняются во время сложной последовательности, поэтому он временно сохраняет промежуточные результаты в памяти. Когда он это сделает, он может записать только 64-битный двойной, а не номер расширенной точности. Таким образом, процедура, содержащая арифметику с плавающей запятой, может давать разные результаты только потому, что она была скомпилирована с другим кодом, возможно, в одном месте, а компилятор нуждался в регистрах для чего-то еще.

Некоторые процессоры имеют инструкции для вычисления умножения и добавления в одну команду, поэтому "bc + d" может быть вычислен без промежуточного округления и получить более точный результат, чем на процессоре, который сначала вычисляет bc, а затем добавляет d.

У вашего компилятора могут быть переключатели для управления таким образом.

Есть несколько мест, где стандарт 754-1985 не требует уникального результата. Например, при определении того, произошло ли переполнение (результат слишком мал, чтобы быть представленным точно), стандарт позволяет реализации выполнить определение до или после того, как он округляет значение (бит бит) до целевой точности. Таким образом, некоторые реализации подскажут, что underflow произошел, когда другие реализации не будут.

Общей особенностью процессоров является режим "почти IEEE 754", который устраняет сложность работы с underflow, заменяя нуль вместо того, чтобы возвращать очень небольшое число, которое требует стандарт. Естественно, вы получите разные номера при выполнении в таком режиме, чем при выполнении в более совместимом режиме. Неприемлемый режим может быть установлен по умолчанию вашим компилятором и/или операционной системой по причинам производительности.

Обратите внимание, что реализация IEEE 754 обычно не предоставляется только аппаратными средствами, а комбинацией аппаратного и программного обеспечения. Процессор может выполнять основную часть работы, но полагаться на программное обеспечение для обработки определенных исключений, устанавливать определенные режимы и т.д.

Когда вы переходите от основных арифметических операций к вещам вроде синуса и косинуса, вы очень зависите от используемой библиотеки. Трансцендентальные функции обычно вычисляются с помощью тщательно спроектированных приближений. Реализации разрабатываются независимо разными инженерами и получают разные результаты друг от друга. В одной системе функция sin может давать результаты в пределах ULP (единица наименьшей точности) для небольших аргументов (меньше, чем pi или около того), но больших ошибок для больших аргументов. В другой системе функция sin может давать результаты в пределах нескольких ULP для всех аргументов. Известно, что текущая математическая библиотека не дает корректно округленных результатов для всех входных данных. Существует проект crlibm (Correctly Rounded Libm), который проделал определенную работу по достижению этой цели, и они разработали реализации для значительных частей математической библиотеки, которые правильно округлены и имеют хорошую производительность, но не всю математическую библиотеку пока.

В целом, если у вас есть управляемый набор вычислений, понимайте свою реализацию компилятора и очень осторожны, вы можете полагаться на идентичные результаты на разных процессорах. В противном случае получение полностью идентичных результатов не является чем-то, на что вы можете положиться.

Ответ 2

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

В некоторых случаях вы даже можете получить разные результаты для отладочных (не оптимизированных) сборок или версий (оптимизированных) на одной машине, поэтому даже не предполагайте, что результаты могут быть всегда одинаковыми на разных машинах.

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

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

Ответ 3

Современные FPU все реализуют поплавки IEEE754 в одном и двух форматах, а некоторые в расширенном формате. Поддерживается определенный набор операций (почти что-либо в math.h), с некоторыми специальными инструкциями, плавающими там.

Ответ 4

Предполагая, что вы говорите о применении нескольких операций, я не думаю, что вы получите точные цифры. Архитектура процессора, использование компилятора, настройки оптимизации изменят результаты ваших вычислений.

если вы имеете в виду точный порядок операций (на уровне сборки), я думаю, что вы все равно получите варианты. Например, чипы Intel используют внутреннюю расширенную точность (80 бит), что может быть не так для других процессоров. (Я не думаю, что расширенная точность задана)

Ответ 5

Одна и та же программа на С# может выводить разные результаты на один и тот же компьютер, после компиляции в режиме отладки без оптимизации, второй раз скомпилирована в режиме деблокирования с включенной оптимизацией. Это мой личный опыт. Мы не рассматривали это, когда впервые создали набор тестов для автоматической регрессии для одной из наших программ, и были полностью удивлены тем, что многие наши тесты не прошли без каких-либо видимых причин.

Ответ 6

Для С# на x86 используются 80-битные FP-регистры.

В стандарте С# говорится, что процессор должен работать с той же точностью, что и сам, или больше, чем сам тип (то есть 64-бит в случае "двойного" ). Разрешения допускаются, за исключением хранения. Это означает, что локали и параметры могут быть более 64-битной.

Другими словами, назначение переменной-члена локальной переменной могло (и на самом деле будет при определенных обстоятельствах) достаточным для того, чтобы дать неравенство.

Смотрите также: Float/double precision в режимах отладки/выпуска

Ответ 7

Для 64-битного типа данных я знаю только "double precision" / "binary64" из IEEE 754 (1985 и 2008 здесь не сильно отличаются для обычных случаев).

Примечание. Типы оснований, определенные в IEEE 854-1987, в любом случае заменяются IEEE 754-2008.