Ответ 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), который проделал определенную работу по достижению этой цели, и они разработали реализации для значительных частей математической библиотеки, которые правильно округлены и имеют хорошую производительность, но не всю математическую библиотеку пока.
В целом, если у вас есть управляемый набор вычислений, понимайте свою реализацию компилятора и очень осторожны, вы можете полагаться на идентичные результаты на разных процессорах. В противном случае получение полностью идентичных результатов не является чем-то, на что вы можете положиться.