Как я могу обойти тот факт, что в C++ sin(M PI) не равен 0?

В C++,

const double Pi = 3.14159265;
cout << sin(Pi);                          // displays: 3.58979e-009

он должен отображать число ноль

Я понимаю, что это потому, что Pi аппроксимируется, но есть ли способ, которым я могу иметь значение Pi, жестко закодированное в моей программе, которое вернет 0 для sin(Pi)? (может быть, другая константа?)

Если вам интересно, что я пытаюсь сделать: я конвертирую Полярный в прямоугольный, и хотя есть некоторые трюки printf (), которые я могу сделать, чтобы напечатать его как "0.00", он все еще не последовательно верните достойные значения (в некоторых случаях я получаю "-0.00")

линии, которые требуют греха и Косинуса:

x = r*sin(theta);
y = r*cos(theta);

BTW: мой прямоугольный - > Полярный работает нормально... это просто Полярный - > прямоугольный

спасибо!

edit: я ищу обходной путь, чтобы я мог печатать sin (некоторые кратные Pi) как хороший круглый номер на консоли (в идеале без тысячи if-операторов)

14 ответов


Что Каждый Компьютерщик Должен Знать Об Арифметике С Плавающей Запятой (edit: также связан в комментарии) - довольно жесткое чтение (я не могу утверждать, что прочитал все это), но суть в следующем: вы никогда не получите совершенно точные вычисления с плавающей запятой. Из статьи:

сжатие бесконечно многих вещественных чисел в конечное число битов требует приблизительного представления.

Не позволяйте вашей программе зависеть от точного результаты расчетов с плавающей запятой-всегда допускают диапазон допуска. FYI 3.58979 e-009 составляет около 0.0000000036. Это хорошо в пределах любого разумного диапазона толерантности, который вы выбираете!


скажем так,3.58979e-009 is как закрыть до 0 как ваш 3.14159265 значение для реального Pi. Технически, ты получил то, о чем просил. :)

Теперь, если вы поместите только 9 значащих цифр (8 знаков после запятой), то проинструктируйте выход также не отображать больше, т. е. использовать:

cout.precision(8);
cout << sin(Pi);

вы пробовали M_PI, доступный в большинстве <cmath> или <math.h> реализации?

тем не менее, использование плавающей точки таким образом всегда будет вводить определенную ошибку.


Это должно показать ноль:

cout << fixed << sin(Pi);

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


3.58979 e-009 это 0,0000000358979

Это ~~0, как ваш ~~PI.


Это равно нулю, если ваш оператор равенства имеет достаточный допуск


вы можете добавить еще несколько цифр, чтобы получить лучший результат (попробуйте, например, 3.1415926535897932384626433832795029 L), но вы все равно получите ошибки округления.

тем не менее, вы можете создать свой собственный sin и cos версии, которые проверяют Ваше известное значение Pi и возвращают ровно ноль в этих случаях.

namespace TrigExt
{
    const double PI = 3.14159265358979323846;

    inline double sin(double theta)
    {
        return theta==PI?(0.0):(std::sin(theta));
    }
}

вы также можете расширить эту вещь для других тригонометрических функций и кратных Пи.


вы можете написать небольшую функцию обертки:

double mysin(const double d) {
    double ret = sin(d);
    if(fabs(ret) < 0.0000001) {
        return 0.0;
    } else {
        return ret;
    }
}

как отмечали другие, математика с плавающей запятой, как известно, неточна. Вам нужна какая-то терпимость, если вы хотите, чтобы что-то выглядело как ровно ноль.


почему бы не заставить столько цифр, сколько вам нужно

 int isin = (int)(sin(val) * 1000);
 cout << (isin/1000.0)

sin (PI) должен быть равен 0, для точного значения PI. Вы не вводите точное значение PI. Как указывают другие люди, результат, который вы округляете до 7 десятичных знаков, равен 0, что довольно хорошо для вашего приближения.

Если вам нужно другое поведение, вы должны написать свою функцию синуса.


Если вы используете float или double в математических операциях, у вас никогда не будет точных результатов. Причина в том, что в компьютере все хранится в силу 2. Это не переводится точно в нашу десятичную систему счисления. (Примером является то, что в базе 2 нет представления 0.1)

кроме того, float и double-это 64 бита, по крайней мере, на некоторых компиляторах и платформах. (Я думаю-кто-нибудь поправит меня на это, если понадобится). Это приведет к некоторым ошибкам округления либо для очень большие значения или для очень малых значений (0.0000000000 xxx)

чтобы получить точные результаты, вам понадобится большая целочисленная библиотека.

как написано в комментариях к вопросу выше см. сайт ... http://docs.sun.com/source/806-3568/ncg_goldberg.html


double cut(double value, double cutoff=1e-7) {
  return (abs(value) > cutoff)*value;
}

это будет нулевое значение ниже порога, используйте его так cut(sin(Pi))


более значительные цифры могут помочь. Мой компилятор C (gcc) использует константу 3.14159265358979323846 для M_PI в "Математика.ч." Кроме этого, вариантов немного. Создание собственной функции для проверки ответа (как описано в другом ответе на ваш вопрос), вероятно, лучшая идея.


вы знаете, просто для математической корректности там: sin (3.14159265) модули не ноль. Это примерно ноль, это именно то, что программа говорит вам. Для расчетов это число должно дать вам хороший результат. Для отображения это отстой, поэтому всякий раз, когда вы печатаете float, обязательно форматируйте число.

Я действительно не думаю, что здесь есть какая-либо механика поплавка... это простая математика.

о код, однако, будьте осторожны... не заставляет ваш код давать неправильный результат, делая приближения перед отображением, просто отображайте информацию правильным образом.