64-битные числа с плавающей точкой ведут себя одинаково на всех современных ПК? - PullRequest
10 голосов
/ 27 января 2010

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

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

Ответы [ 7 ]

8 голосов
/ 28 января 2010

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

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

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

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

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

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

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

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

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

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

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

7 голосов
/ 27 января 2010

Если вы хотите получить точно такой же результат, то ответ - нет.

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

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

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

2 голосов
/ 27 января 2010

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

1 голос
/ 27 января 2010

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

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

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

См. Также: Плавающая / двойная точность в режимах отладки / выпуска

1 голос
/ 27 января 2010

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

1 голос
/ 27 января 2010

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

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

0 голосов
/ 27 января 2010

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

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

...