Math.cos(90°) = 6.123233995736766E-17

157
29 мая 2021, 14:10

Столкнулся с проблемой в Math:

double a = 0.0;
double b = Math.toRadians(a);
System.out.println(Math.cos(b));

Вывод: 1.0.

double a = 90.0;
double b = Math.toRadians(a);
System.out.println(Math.cos(b));

Вывод: 6.123233995736766E-17.

Почему Java подсчитывает 90 градусов неверно?

Answer 1

Обратите внимание, программа вывела не просто 6.123233995736766, а 6.123233995736766E-17.

Это представление (с «E») называется научной нотацией. В ней числа имеют вид:

±mmmmpppp

где:

  • m — мантисса (эдакое базовое число);
  • p — порядок (на сколько цифр сдвинуть запятую в мантиссе; плюс — направо, минус — налево).

Таким образом, 6.123233995736766E-17 превращается в 0.00000000000000006123233995736766.

Answer 2

Так а все верно.

6.123233995736766E-17

это почти 0, такой ответ связан с точностью представления чисел с плавающей точкой.

PS: Сравнивать double в этом случае нужно примерно так, если Вас устраивает такая точность...

boolean isEqual(double d1, double d2){
    return Math.abs(d1 - d2) < 1e-10;
}
Answer 3

Метод cos() вычисляет косинус, используя бесконечную сумму (у меня нет инсайдерских данных, просто по-другому это реализовать проблематично: *P.S.). Чем больше слагаемых, тем точнее вычисления.

Примерно так он реализован:

double cos(double x, int precision) {
    double result = 0;
    for (int i = 0; i < precision; i++) {
        result += Math.pow(x,2*i)/factorial(2*i);
        i++;
        result -= Math.pow(x,2*i)/factorial(2*i);
    }
    return result;
}
ArrayList<Double> factorialCache = new ArrayList<>();
{
    factorialCache.add(1D);
}
double factorial(int n) {
    if (n < factorialCache.size()) return factorialCache.get(n);
    double result = factorialCache.get(factorialCache.size() - 1);
    for (int i = factorialCache.size(); i <= n; i++)
        factorialCache.add(result *= i);
    return result;
}

На достаточно небольших числах, порядка десяти, факториал начинает немыслимо быстро расти. Чтобы производить точные вычисления на очень больших и очень маленьких числах, необходимо вместо double использовать BigDecimal, но с ним вычисления происходят намного дольше, чем с double, поэтому разработчиками явы принято решение пожертвовать точностью взамен производительности. При желании вы можете сами написать более точный метод для определения косинуса.

При точности (precision) 12 (или 11) метод возвращает 4.245771032544636E-17, немного меньше, чем стандартный метод. Меньше при использовании double получить не получится.

P.S.: В принципе можно реализовать вычисление косинуса ещё и с помощью формулы Эйлера о синусе и косинусе, но там нужно реализовывать класс, представляющий комплексные числа. Ещё можно сохранить "транспортир" в виде массива косинусов при разных углах, но точность от этого будет страдать.

READ ALSO
Jackson не считывает boolean

Jackson не считывает boolean

У меня есть медот который принимает и парсит JSON с помощью Jackson

96
Падает приложение java.lang.NoSuchMethodError

Падает приложение java.lang.NoSuchMethodError

Собираю приложение через Usb отладку все окНо когда собираю Apk приложение падает java

88
Параметр типа boolean для запуска java программы

Параметр типа boolean для запуска java программы

Допустим есть программаПри определенном параметре в строке запуска её поведение изменится

115
Как прочитать файл на компьютере

Как прочитать файл на компьютере

У меня есть Skullstxt документ, мне нужно читать его на компьютере а не на сайте

110