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

Я хотел бы знать, может ли какой-либо код на C или С++ использовать арифметику с плавающей запятой производить бит точные результаты в любой архитектуре на основе x86, независимо от сложности кода.

Насколько мне известно, любая архитектура x86, поскольку Intel 8087 использует блок FPU, подготовленный для обработки чисел с плавающей запятой IEEE-754, и я не вижу никакой причины, по которой результат будет отличаться в разных архитектурах. Однако, если бы они были разными (а именно, из-за разного компилятора или разного уровня оптимизации), можно ли каким-то образом генерировать бит-точные результаты, просто сконфигурировав компилятор?

Ответ 1

Содержание:

  • C/С++
  • ASM
  • Создание реального программного обеспечения, которое достигает этого.

В C или С++:

Нет, полностью совместимая с ISO C11 и IEEE реализация C не гарантирует идентичные бит результаты другим реализациям C, даже другим реализациям на одном и том же оборудовании.

(И прежде всего, я собираюсь предположить, что мы говорим о нормальных реализациях C, где double - это формат IEEE-754 binary64 и т.д., хотя было бы законным для реализации C на x86 использовать другой формат для double и реализовать математику FP с программной эмуляцией и определить пределы в float.h. Это могло быть правдоподобно, если не все процессоры x86, входящие в FPU, но в 2016 году, что Deathstation 9000.)


related: Брюс Доусон Детерминизм с плавающей запятой сообщение в блоге - ответ на этот вопрос. Его вступительный абзац забавный (и за ним следует много интересного):

Является ли математическая математика с плавающей запятой IEEE детерминированной? Будете ли вы всегда получать одинаковые результаты с одних и тех же данных? Ответ однозначный "да". К сожалению, ответ также является однозначным "нет". Я боюсь, что вам нужно будет уточнить свой вопрос.

Если вы размышляете над этим вопросом, то вам обязательно захочется взглянуть на индекс на статьи Брюса статей о плавающих математическая точка, реализованная компиляторами C на x86, а также asm и IEEE FP в целом.


Первая проблема: только "основные операции" : + - */и sqrt должны возвращать "правильно округленные" результаты, т.е. <= 0.5ulp ошибки, правильно округленная до последнего бита мантиссы, поэтому результаты являются ближайшим представимым значением для точного результата.

Другие функции математической библиотеки, такие как pow(), log() и sin(), позволяют разработчикам совершать компромисс между скоростью и точностью. Например, glibc обычно поддерживает точность и медленнее, чем математические библиотеки Apple OS X для некоторых функций, IIRC. См. Также glibc документация об ошибках для каждой функции libm для разных архитектур.


Но подождите, он становится хуже. Даже код, который использует только правильные операции округления, не гарантирует одинаковых результатов.

Правила

C также допускают некоторую гибкость при сохранении более высоких временных времен. Реализация определяет FLT_EVAL_METHOD, поэтому код может определить, как он работает, но вы не получаете выбора, если вам не нравится, что осуществление. У вас есть выбор (с #pragma STDC FP_CONTRACT off), чтобы запретить компилятор, например. превращая a*b + c в FMA без округления a*b до добавления.

В x86 компиляторы, нацеленные на 32-разрядный код без SSE (т.е. с использованием устаревших инструкций x87), обычно сохраняют временные ряды FP в регистре x87 между операциями. Это создает поведение FLT_EVAL_METHOD = 2 80-битной точности. (Стандарт указывает, что округление по-прежнему происходит при каждом присваивании, но реальные компиляторы, такие как gcc, на самом деле не делают дополнительных хранилищ/перезагрузок для округления, если вы не используете -ffloat-store. См. https://gcc.gnu.org/wiki/FloatingPointMath. Эта часть стандарта, похоже, была написана в предположении неубывающих компиляторов, потому что сохранение после каждого утверждения - это именно то, что gcc -O0 и большинство других компиляторы делают.)

Поэтому, когда таргетинг на x87, компилятору разрешено оценивать сумму трех float с двумя инструкциями x87 FADD, не округляя сумму первых двух до 32-разрядных float. В этом случае временная имеет 80-битную точность... Или это так? Не всегда, поскольку код запуска реализации C (или библиотека Direct3D!!!), возможно, изменил настройку точности в управляющем слове x87, поэтому значения в регистрах x87 округлены до 53 или 24 бит мантиссы. (Это делает FDIV и FSQRT работать немного быстрее.) Все это из статья Брюса Доусона о промежуточной точности FP).


В сборке:

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

Руководства Intel не определяют точно, что эти результаты для каждого случая, но по крайней мере Intel считает, что точная обратная совместимость важнее, чем улучшение ограниченной точности некоторых инструкций. Например, FSIN использует только 66-битную аппроксимацию pi для уменьшения диапазона, поэтому документация Intel о наихудшем случае была отключена в 1,3 раза [a href= "https://randomascii.wordpress.com/2014/10/09/intel-underestimates-error-bounds-by-1-3-quintillion/" rel= "nofollow noreferrer" > пока они не обновили его после того, как Брюс Доусон заметил, насколько плохим в худшем случае было.

(Я не уверен, что эта 66-битная мантисса (или точная, чем 80-битный регистр может удерживать), или если она содержит 66 бит, включая одинаковое количество знаков и экспонентов в виде длинного двойника).

Я не знаю, сможет ли AMD реализовать свой FSIN, чтобы всегда давать бит-идентичные результаты Intel, но я не удивлюсь. Предположительно, какое-то программное обеспечение полагается на него, иначе Intel могла бы повысить точность своего внутреннего значения pi несколько лет назад.


Поскольку SSE предоставляет только инструкции для add/sub/mul/div/sqrt, ничего интересного сказать не может. Они реализуют операцию IEEE точно, поэтому нет никакой возможности, что любая реализация x86 когда-либо даст вам что-то другое (если только режим округления не установлен по-другому, или denormals-are-zero и/или flush-to-zero отличаются, и у вас есть денормализованные числа).


В реальной жизни:

Итак, если вы статически связываете libm, который использует SSE/SSE2 и распространяет эти двоичные файлы, они будут работать одинаково везде. Если эта библиотека не использует обнаружение ЦП во время выполнения, чтобы выбрать альтернативные реализации...

Как указывает @Yan Zhou, вам очень нужно контролировать каждый бит реализации вплоть до asm, чтобы получить бит-точные результаты.

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

В RTS, клиенты проверяют свою игру, чтобы обнаружить desync. Я не играл в нее какое-то время, но я помню что-то прочитанное по крайней мере 5 лет назад о том, что они пытались добиться синхронизации, убедившись, что все их сборки x86 используют математику SSE, даже 32-битные сборки.

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

Дополнительная литература: GAFFER ON GAMES: Дефинитив с плавающей запятой. Некоторые методы, которые используют реальные игровые механизмы для получения детерминированных результатов. например wrap sin/cos/tan в неоптимизированных вызовах функций, чтобы заставить компилятор оставить их с одной точностью.

Ответ 2

Если компилятор и архитектура соответствуют стандартам IEEE, да.

Например, gcc является совместимым с IEEE, если он настроен правильно. Если вы используете флаг -ffast-math, он не будет соответствовать IEEE.

См. http://www.validlab.com/goldberg/paper.pdf страница 25.

Если вы хотите точно знать, на какую точность вы можете положиться при использовании аппаратной/компиляторной пары IEEE 754-1985, вам необходимо приобрести стандартную бумагу на сайте IEEE. К сожалению, это не общедоступно

http://ieeexplore.ieee.org/xpl/login.jsp?tp=&arnumber=30711&url=http%3A%2F%2Fieeexplore.ieee.org%2Fstamp%2Fstamp.jsp%3Ftp%3D%26arnumber%3D30711