Насколько я понимаю, у вас есть программная реализация трансцендентной функции, такой как sin (x), выраженная в терминах стандартных операций IEEE, таких как сложение и умножение с плавающей запятой, и вы хотите, чтобы вы получили тот же ответ на все машины (или, по крайней мере, все машины, которые вам нужны).
Во-первых, поймите: это не будет переносимо на все машины. Например. Шестнадцатеричное число с плавающей точкой мэйнфрейма IBM не является IEEE и не даст таких же ответов. Чтобы получить это точно, вам потребуется программная реализация стандартных операций IEEEE, таких как FP сложение и умножение.
Я предполагаю, что вы заботитесь только о машинах, которые реализуют стандартную IEEE с плавающей запятой. И я также предполагаю, что вы не беспокоитесь о NaN, поскольку NaN не были полностью стандартизированы IEEE 754-1985, и возникли две противоположные реализации: HP и MIPS, vedrsus почти для всех остальных. 1
С этими ограничениями, как вы можете получить изменчивость в ваших расчетах?
(1) Если код распараллеливается. Убедитесь, что этого не происходит. (Это маловероятно, но некоторые машины могут.) Распараллеливание является основным источником изменения результатов в FP. По крайней мере, одна моя знакомая компания, которая заботится о воспроизводимости и параллелизме, отказывается использовать FP и использует только целое число.
(2) Убедитесь, что машина настроена правильно.
например. большинство машин рассчитывают с 32- или 64-битной точностью (исходный стандарт C везде был 64-битным "двойным"). Но Intel x86 / x87 может рассчитывать в 80-битных регистрах и округлять до 64 или 32 при разливе. 1 показано, как изменить точность управления x86 / x87 с 80-битной на 64-битную, используя встроенную сборку. Обратите внимание, что этот код является уровнем сборки, а не переносимым - но большинство других машин уже выполняют вычисления с точностью 32 или 64 бит, и вы не не нужно беспокоиться о 80-битном x87.
(Между прочим, на x86 вы можете избежать всех проблем, только используя SSE FP; старая Intel x87 FP никогда не сможет дать точно такие же ответы (хотя, если вы установите 64-битное управление точностью (ПК), а 80 бит, вы получите те же результаты, за исключением случаев, когда произошло промежуточное переполнение, поскольку ширина экспоненты не изменяется, только мантисса))
например. убедитесь, что вы используете один и тот же режим пониженного уровня на всех машинах. То есть убедитесь, что они включены или включены, или наоборот, что все машины находятся в режиме сброса в ноль. Вот выбор Добсона: режимы сброса на ноль не стандартизированы, но некоторые машины, например, У графических процессоров просто не было денормализованных номеров. То есть многие машины имеют стандартные форматы числа IEEE, но не имеют действительной стандартной арифметики IEEE (со знаками). Мой барабанщик должен требовать IEEE денормс, но если бы я был абсолютно параноиком, я бы пошел со сбросом в ноль и принудительно запустил этот сброс в программном обеспечении.
(3) Убедитесь, что вы используете одни и те же языковые опции. Более старые программы на С выполняют все вычисления в «двойном» (64-битном) режиме, но теперь допустимо вычислять с одинарной точностью. Как бы то ни было, вы хотите сделать это одинаково на всех машинах.
(4) Некоторые мелкие предметы по вашему коду:
Избегайте больших выражений, которые компилятор может переставить (если он не правильно реализует строгие переключатели FP)
Возможно написать весь ваш код в простой форме, как
double a = ...;
double b = ...;
double c = a *b;
double d = ...;
double e = a*d;
double f = c + e;
Вместо
f = (a*b) + (a*c);
, который может быть оптимизирован до
f = a*(b+c);
Я перестану говорить о параметрах компилятора для последнего, потому что он длиннее.
Если вы делаете все эти вещи, то ваши вычисления должны быть абсолютно повторяемыми. IEEE с плавающей точкой является точным - она всегда дает одинаковые ответы. Именно перестановка вычислений компилятором на пути к IEEE FP вносит изменчивость.
Вам не нужно округлять младшие биты. Но это также не повредит, и может замаскировать некоторые проблемы. Помните: вам может понадобиться маскировать хотя бы один бит для каждого добавления ....
(2) Оптимизация компилятора по-разному перестраивает код на разных машинах. Как сказал один из комментаторов, используйте любые ключи вашего компилятора для строгой FP.
Возможно, вам придется отключить всю оптимизацию для файла, содержащего ваш код греха.
Возможно, вам придется использовать летучие вещества.
Надеюсь, есть более специфичные ключи компилятора. Например. для gcc:
-ffp-contract = off --- отключить умноженное слияние, так как не все целевые машины могут иметь их.
-fexcess precision = standard --- отключает такие вещи, как Intel x86 / x87 избыточная точность во внутренних регистрах
-std = c99 --- указывает довольно строгий стандарт языка Си. К сожалению, не полностью реализовано, как я гуглял сегодня
убедитесь, что у вас не включена оптимизация, например -funsafe-math и -fassociativbe-math