Ответ 1
Нет, стандарт С++ не требует, чтобы результаты cmath-функций были одинаковыми во всех реализациях. Во-первых, вы не можете получить арифметику с плавающей точкой IEEE-754/IEC 60559.
Тем не менее, если реализация использует IEC 60559 и определяет __STDC_IEC_559__
, то она должна придерживаться Приложения F стандарта C (да, ваш вопрос о С++, но стандарт С++ отменит стандарт C для заголовков C, таких как math.h
). Приложение F гласит:
- Тип
float
соответствует стандарту IEC 60559.- Тип
double
соответствует двойному формату IEC 60559.- Тип
long double
соответствует расширенному формату IEC 60559, иначе расширенный формат без стандарта IEC 60559, кроме формата IEC 60559double
.
Далее, он говорит, что нормальная арифметика должна соответствовать стандарту IEC 60559:
- Операторы
+
,−
,*
и/
предоставляют IEC 60559 операции добавления, вычитания, умножения и деления.
Далее требуется sqrt
следовать IEC 60559:
- Функции
sqrt
в<math.h>
обеспечивают работу квадратного корня IEC 60559.
Далее описывается поведение нескольких других функций с плавающей запятой, большинство из которых вам, вероятно, не интересует этот вопрос.
Наконец, он попадает в заголовок math.h
и указывает, как различные математические функции (т.е. sin
, cos
, atan2
, exp
и т.д.) должны обрабатывать особые случаи (т.е. asin(±0)
возвращает ±0
, atanh(x)
возвращает NaN и вызывает "неверное" исключение с плавающей запятой для | x | > 1 и т.д.). Но это не приводит к точному вычислению для обычных входов, а это означает, что вы не можете полагаться на все реализации, производя точно такие же вычисления.
Нет, это не требует, чтобы эти функции вели себя одинаково во всех реализациях, даже если все реализации определяют __STDC_IEC_559__
.
Это все с теоретической точки зрения. На практике все хуже. Процессоры обычно реализуют арифметику IEC 60559, но могут иметь разные режимы округления (поэтому результаты будут отличаться от компьютера к компьютеру), а компилятор (в зависимости от флагов оптимизации) может сделать некоторые предположения, которые не соответствуют строгому стандарту в отношении вашего арифметика с плавающей запятой.
Таким образом, на практике это даже менее строго, чем теоретически, и вы, вероятно, увидите, что два компьютера производят несколько разные результаты в какой-то момент.
Реальный пример этого в glibc - реализация библиотеки GNU C. У них есть таблица известных пределов ошибок для их математических функций в разных ЦП. Если все C-математические функции были точными по битам, все эти таблицы отображали бы ULP с ошибками 0. Но они этого не делают. В таблицах показано, что в их математической функции C есть действительно различная погрешность. Я думаю, что это предложение - самое интересное резюме:
За исключением определенных функций, таких как
sqrt
,fma
иrint
, результаты которых полностью заданы ссылкой на соответствующие операции с плавающей запятой IEEE 754 и преобразованиями между строками и плавающей точкой, библиотека GNU C не нацелены на корректно округленные результаты для функций в математической библиотеке [...]
Единственными вещами, которые являются точными в glibc, являются вещи, которые должны быть точными в приложении F стандарта C. И, как вы можете видеть в их таблице, большинство вещей - нет.