Значение синуса 180 выходит как 1.22465e-16 - PullRequest
3 голосов
/ 04 июля 2011

Я хочу реализовать калькулятор синуса и косинуса в ios4:

if([operation isEqual:@"sin"]){
    operand = (operand*M_PI/180.0);
    operand=sin(operand);
}

Код дает мне правильный ответ для значений от 0 до 90.

Когда я даю значение 180,Я получаю 1.22465e-16 в качестве ответа.Я ожидаю ноль.

Откуда эта маленькая разница?

Ответы [ 4 ]

7 голосов
/ 05 июля 2011

Просто чтобы прояснить, ваша программа равна , давая вам правильный ответ .То есть, он делает именно то, что вы сказали в коде.

180*M_PI правильно округляется (согласно IEEE-754) и дает значение:

565.4866776461627750904881395399570465087890625

деление этого на 180 также правильно округляется и дает результат:

3.141592653589793115997963468544185161590576171875

, который не является точно математическим значением π.Фактически, это:

π - 0.0000000000000001224646799147...

член первого порядка ряда Тейлора для sin(x) вокруг π равен (π-x), поэтому sin(π - x) для малых x почти точно-x.Фактически, вы получите правильно округленный результат .Библиотека не может дать более точный ответ.

Как и Бен Фойгт , если это действительно проблема для вас, вы можете обойти ее, сократив аргумент до диапазона[-90, 90) перед преобразованием из градусов в радианы.Еще лучше предложить njuffa для использования функции sinpi, которая сделает эту работу за вас.В iOS нет такой функции, но в ней есть vvsinpi, которая реализует sin (π * x) для векторов и может быть выполнена для того, что вы хотите:

double result;
int vectorLength = 1;
vvsinpi(&result, &operand, &vectorLength);

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

5 голосов
/ 04 июля 2011

Это вызвано неспособностью двоичной системы счисления точно представлять PI.

Одним из возможных решений было бы использование симметрии греха:

sindeg(x) = sindeg(180 - x)

(или в качестве альтернативы):

sin(x) = sin(M_PI - x)

Преобразование угла в диапазон (-pi / 2: pi / 2) уменьшает ошибку аппроксимации.

В основном:

if([operation isEqual:@"sin"]){
  operand = fmod(operand, 360);
  if (operand > 270) operand -= 360;
  else if (operand > 90) operand = 180 - operand;
  operand=sin(operand*M_PI/180.0);
}
3 голосов
/ 04 июля 2011

Возможно, вы захотите проверить, предлагает ли математическая библиотека на этой платформе функцию sinpi () в качестве расширения стандартных функций математической библиотеки C / C ++. Это позволило бы избежать явного умножения с приблизительным значением pi (то есть M_PI) и повысить точность.

operand = sinpi (operand / 180.0)
0 голосов
/ 04 июля 2011

Вы полагаетесь на тип данных с плавающей запятой в вашем процессоре. С большим (довольно значительным) усилием вы можете реализовать тип данных произвольной точности. Посмотрите на MPFR http://www.mpfr.org/sample.html, он содержит определение функций (например, sin), которые будут доставлять большее количество цифр.

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