Почему эта простая программа
#include <stdio.h>
#include <math.h>
int main()
{
int res = (int)(-1 * sin(M_PI/2) + 1 * cos(M_PI/2));
printf("-1 * %f + 1 * %f = %d\n", sin(M_PI/2), cos(M_PI/2), res);
return 0;
}
дать следующий неверный вывод?
-1 * 1.000000 + 1 * 0.000000 = 0
@Someprogrammerdude -1
Причина неудачи в том, что вы создали res
int и на компиляторе, который вы используете, sin(M_PI/2)
оценивается как 1-machine_eps
. Я подозреваю, что если вы скомпилировали его с полной оптимизацией, он действительно будет работать так, как задумано, поскольку тогда ответ может быть вычислен во время компиляции, и обычно вычисление постоянных значений во время компиляции может быть более точным, чем у библиотек времени выполнения. Лишь немногие порядочные оптимизаторы не знают этого sin(M_PI/2) == 1
и заменяют соответственно. Какой это был компилятор? Измените %f
на %g24.16
, чтобы увидеть, что происходит на самом деле.
Я удалил свой комментарий, потому что предоставленный ответ более подробный. Приведение к целому числу просто усекается. Если вы хотите округлить, вы должны использовать функцию round
или аналогичную.
@MartinBrown я использовал gcc -g -Wall -Wextra stack_overflow.c -lm -o stack_overflow
Во-первых, несмотря на видимость, на самом деле вы не взяли синус или косинус точного числа пи/2. См. этот вопрос и, возможно, также этот вопрос.
Если вы напечатаете более значащие цифры:
double res = -1 * sin(M_PI/2) + 1 * cos(M_PI/2);
printf("-1 * %.17f + 1 * %.17f = %.17f\n", sin(M_PI/2), cos(M_PI/2), res);
Вы увидите проблему:
-1 * 1.00000000000000000 + 1 * 0.00000000000000006 = -0.99999999999999989
Неточная природа чисел с плавающей запятой в данном случае дает результат для вызова cos
, который не равен точно 0. Таким образом, ваш окончательный результат немного больше -1, и преобразование этого значения в int
усекает значение в сторону 0, давая 0. как результат.
Да, спасибо, я просто не ожидал, что преобразование не округлит число до ближайшего значения.
ОТ: Приведение к двойному значению не требуется.
@Fyodor Вы можете использовать функцию round
для округления до ближайшего целого числа (с округлением в сторону от нуля).
В большинстве систем cos
на самом деле довольно точно вычисляет ближайший возможный результат (для не слишком больших входных данных). Основная проблема в том, что M_PI
не является (и не может быть) в точности п, следовательно, cos(M_PI/2) != 0
. Если у вас есть C23, рассмотрите возможность использования cospi.
cos(M_PI/2)
не ожидается равным 0.
M_PI
это не π.
M_PI
, иногда называемое машинным числом «пи», представляет собой значение с плавающей запятой, близкое к π, но не совсем π. π — трансцендентное число и не представимо в виде рационального числа. Все конечные числа с плавающей запятой рациональны.
Значение косинуса (π/2) с математической точки зрения равно 0,0, однако, поскольку cos(M_PI/2)
лишь немного близко к нулю, будучи немного выше или ниже 0,0, -1 * sin(M_PI/2) + 1 * cos(M_PI/2)
может быть немного выше, равно или ниже -1. Когда сумма равна -0.999...
, (int)(-0.999...)
равна 0.
График sin, cos, tan и ошибка округления может помочь.
Каков ожидаемый результат?