Получение синуса угла как двойного в c++

В моем приложении, поскольку я получаю неверные результаты из-за ошибок округления (например, расстояние, вычисленное между двумя точками, разделенными на 1853 м на поверхности земли, равно 0!), Мне нужно вычислить синус и косинус с двойной точностью. К сожалению, я не могу найти в NDK подходящих функций.

Как я могу получить более точные результаты?

Я использую формулу 60*1852*toDegrees(acos(sin(toRadians(lat1))*sin(toRadians(lat2))+cos(toRadians(lat1))*cos(toRadians(lat2))*cos(toRadians(long2-long1)))).

с lat1 = 45.7729721; lat2 = 45,792984; long1 = 2,96383333; long2 = 2,96895313

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

PaulMcKenzie 10.08.2018 17:29

Хм. вы используете double для своих переменных? Он должен вычислить sin / cos уже с двойной точностью.

geza 10.08.2018 17:51

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

Zelig63 10.08.2018 19:25

@geza Спасибо, Геза. Сделав несколько забросов на «удвоение», я получаю правильный результат.

Zelig63 11.08.2018 08:16
2
4
107
1

Ответы 1

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

Давайте посмотрим на важное выражение (без преобразования):

acos(sin(lat1)*sin(lat2) + cos(lat1)*cos(lat2)*cos(long2-long1))

Что касается ваших входных данных, lat1 и lat2 очень близки друг к другу. Кроме того, разница между long2 и long1 очень мала, поэтому его косинус будет очень сильно близким к единице.

Учитывая это, выражение внутри acos почти в точности совпадает с этим:

sin(x)*sin(x) + cos(x)*cos(x) = 1

Надежда взять acos из этого выражения даст именно тот результат, который вы наблюдали, поскольку числа с плавающей запятой не справятся с этим хорошо (у вас большая точность около нуля, но не вокруг других целых чисел).

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


Хорошо, вооружившись https://en.wikipedia.org/wiki/List_of_trigonometric_identities, рассмотрим это:

sin(lat1)*sin(lat2) = 0.5 (cos(lat1 - lat2) - cos(lat1 + lat2)) =: a
cos(lat1)*cos(lat2) = 0.5 (cos(lat1 - lat2) + cos(lat1 + lat2)) =: b
cos(long2-long1) =: c

Затем выражение становится acos(a + b * c), или

  0.5 (1 + c) cos(lat1 - lat2) - 0.5 (1 - c) cos(lat1 + lat2)

Термины, которые имеют здесь опасную отмену, - это (1-c) и cos(lat1 - lat2) (по крайней мере, для ваших данных). Для первого вы можете попытаться найти полиномиальные приближения, которые точно вычисляют 1 - cos(x) для x, близкого к 0. Устранить потерю точности в последнем случае сложнее (но это не так важно для данных входных данных).

Хотя ваши рассуждения могут быть правильными (я не читал их полностью), использование чисел OP должно дать хороший результат с двойной точностью.

geza 10.08.2018 17:54

@geza Только последний cos вычисляет только семь значащих цифр double. Windows calc дает мне 0,99999999600763048017366864048184, double имеет ~ 16 значащих цифр около 1.0. И этот термин превращается в значение acos, которое уже для всех намерений и целей уже является 1.

Max Langhof 10.08.2018 17:59

@geza Аргумент acos - это 0,99999993706236262324711684728014. Без фактора cos(long2-long1) аргумент был бы cos(lat1-lat2) или 0,99999993900399614839805988293966. Разница между ними - это 2e-9 (имеет смысл, каждый почти квадрат составляет около 0,5).

Max Langhof 10.08.2018 18:10

Я не понимаю твоих рассуждений. Истинное значение примера OP: ~ 2258.8379895734, при использовании двойников я получаю: 2258.83798862704953. У него довольно хорошая цена.

geza 10.08.2018 18:33

@geza Я думаю, вы правы, acos возвращает вам некоторые значащие цифры, которые потерял cos (при условии, что вы не потеряете их все, как это делает float).

Max Langhof 10.08.2018 18:37

Ах, теперь я могу понять ваши рассуждения. Да, результат не имеет 16 правильных значащих цифр, но имеет более 8, что может быть хорошо для OP. Но, конечно, с другими входами все может быть намного хуже. Итак, стоит преобразовать исходную формулу во что-то другое, которое имеет гораздо лучшую (и гарантированную) точность. Возможно, стоит задать еще один вопрос (с тегом с плавающей запятой).

geza 10.08.2018 19:23

Почему вы говорите, что моя проблема «не очень разрешима с числами с плавающей запятой»? То же выражение, написанное на Java и использующее двойные числа, дает очень удовлетворительный результат.

Zelig63 10.08.2018 19:28

@MaxLanghof Спасибо за время, которое вы потратили, пытаясь мне ответить, но зачем менять формулу, чтобы адаптировать ее к приведенному числовому примеру («устранение потери точности в последнем более сложно (но также не критично для ваших данных)») ?

Zelig63 10.08.2018 20:05

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