Вопреки тому, что говорили другие, это не проблема сопроцессора x87. XCode по умолчанию использует SSE для вычислений с плавающей запятой в Intel (за исключением long double
арифметики).
«Проблема» заключается в следующем: когда вы пишете cosf(M_PI_2)
, вы на самом деле говорите компилятору XCode (gcc или llvm-gcc или clang) сделать следующее:
- Посмотрите на расширение
M_PI_2
в <math.h>
. Согласно стандарту POSIX, это литерал двойной точности, который преобразуется в правильно округленное значение π / 2.
- Округление преобразованного значения двойной точности до одинарной точности.
- Вызвать функцию математической библиотеки
cosf
для значения одинарной точности.
Обратите внимание, что на протяжении всего этого процесса вы не работаете с фактическим значением π / 2. Вместо этого вы работаете с этим значением, округленным до представимого числа с плавающей точкой. Хотя cos (π / 2) равно точно нулю, вы не говорите компилятору делать это вычисление. Вместо этого вы указываете компилятору сделать cos (π / 2 + tiny), где tiny - это разница между округленным значением (float)M_PI_2
и (непредставленным) точным значением π / 2. Если cos
вычислено безо всякой ошибки, результат cos (π / 2 + tiny) будет примерно -tiny. Если он вернул ноль, , что будет ошибкой.
edit: пошаговое расширение вычислений на Intel Mac с текущим компилятором XCode:
M_PI_2
определяется как
1.57079632679489661923132169163975144
но на самом деле это не представимое число двойной точности. Когда компилятор преобразует его в значение двойной точности, оно становится равным
1.5707963267948965579989817342720925807952880859375
Это самое близкое число двойной точности к π / 2, но оно отличается от фактического математического значения π / 2 примерно на 6,12 * 10 ^ (- 17).
Step (2) округляет это число до одинарной точности, что меняет значение точно на
1.57079637050628662109375
Что примерно равно π / 2 + 4,37 * 10 ^ (- 8). Когда мы вычислим cosf
этого числа, то получим:
-0.00000004371138828673792886547744274139404296875
, что очень почти точное значение косинуса, оцененного в этой точке:
-0.00000004371139000186241438857289400265215231661...
На самом деле это правильно округленный результат ; нет никакого значения, которое вычисление могло бы возвратить, которое было бы более точным. Единственная ошибка здесь в том, что вычисления, которые вы просили выполнить компилятор, отличаются от вычислений, которые вы думали , которые вы просили это сделать.