Неверный расчет математического выражения с использованием sin и cos в C?

Почему эта простая программа

#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

Каков ожидаемый результат?

Some programmer dude 16.07.2024 14:05

@Someprogrammerdude -1

Fyodor 16.07.2024 14:08

Причина неудачи в том, что вы создали res int и на компиляторе, который вы используете, sin(M_PI/2) оценивается как 1-machine_eps. Я подозреваю, что если вы скомпилировали его с полной оптимизацией, он действительно будет работать так, как задумано, поскольку тогда ответ может быть вычислен во время компиляции, и обычно вычисление постоянных значений во время компиляции может быть более точным, чем у библиотек времени выполнения. Лишь немногие порядочные оптимизаторы не знают этого sin(M_PI/2) == 1 и заменяют соответственно. Какой это был компилятор? Измените %f на %g24.16, чтобы увидеть, что происходит на самом деле.

Martin Brown 16.07.2024 14:11

Я удалил свой комментарий, потому что предоставленный ответ более подробный. Приведение к целому числу просто усекается. Если вы хотите округлить, вы должны использовать функцию round или аналогичную.

Gerhardh 16.07.2024 14:13

@MartinBrown я использовал gcc -g -Wall -Wextra stack_overflow.c -lm -o stack_overflow

Fyodor 16.07.2024 14:19

Во-первых, несмотря на видимость, на самом деле вы не взяли синус или косинус точного числа пи/2. См. этот вопрос и, возможно, также этот вопрос.

Steve Summit 16.07.2024 15:12
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
6
105
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 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 16.07.2024 14:24

ОТ: Приведение к двойному значению не требуется.

Support Ukraine 16.07.2024 14:35

@Fyodor Вы можете использовать функцию round для округления до ближайшего целого числа (с округлением в сторону от нуля).

Ian Abbott 16.07.2024 15:23

В большинстве систем cos на самом деле довольно точно вычисляет ближайший возможный результат (для не слишком больших входных данных). Основная проблема в том, что M_PI не является (и не может быть) в точности п, следовательно, cos(M_PI/2) != 0. Если у вас есть C23, рассмотрите возможность использования cospi.

chtz 16.07.2024 17:02

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 и ошибка округления может помочь.

Другие вопросы по теме