Разные результаты деления на M3 Mac?

Предположим следующий класс:

class Test {
    public static void main(String[] args) {
        double result = Math.log(9) / Math.log(3);
        System.out.println(result);
    }
};

На машине Linux x86_64 (Linux 6.8.10-300.fc40.x86_64 #1 SMP PREEMPT_DYNAMIC Fri May 17 21:20:54 UTC 2024 x86_64 GNU/Linux) я получаю следующий результат:

2.0

На Mac M3 вместо этого я получаю следующее:

2.0000000000000004

Каковы возможные объяснения такого поведения, кроме ошибки JDK? Версия JDK, используемая на обеих машинах, — Temurin-21.0.3+9.

Эти числа отличаются на один бит в наименее значимом месте. Пришло время избавиться от старого доброго Математика с плавающей запятой не работает?

teapot418 23.05.2024 10:33
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
2
1
73
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Функция, а не ошибка

В классе Математика говорится следующее предостережение:

В отличие от некоторых числовых методов класса StrictMath, все реализации эквивалентных функций класса Math не определены для возврата побитовых одинаковых результатов. Это ослабление позволяет реализовать более эффективные реализации, где не требуется строгая воспроизводимость.

Это означает, что реализация может варьироваться в небольших пределах. Так что это не ошибка.

Тогда вам стоит рассмотреть арифметику с плавающей запятой. Мы можем заниматься настоящей арифметикой,

log(9)/log(3) = log(3^2)/log(3) = 2 log(3)/log(3) = 2

Java не будет делать за вас эти сокращения, он будет вычислять log(9), который не имеет точного непрерывного двоичного представления. Затем log(3) и затем выполните деление. Итак, происходит несколько приближений.

Верно, StrictMath дает одинаковый результат на обеих платформах. (Результат М3.) Спасибо за ответ.

Lukáš Petrovický 23.05.2024 10:51

У меня сложилось впечатление, что начиная с JEP 306 (в Java 17) этой разницы больше не должно быть.

Joachim Sauer 23.05.2024 10:59

@JoachimSauer Меня это тоже немного смущает. StrictMath.log(3) возвращает результат, отличный от Math.log(3).

matt 23.05.2024 11:01

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

matt 23.05.2024 11:06

Конечно, самая крутая часть — это Math.log просто делегирование в StringMath.log. Вы ожидаете получить одинаковые результаты для обоих... Единственное, что я могу сказать, это то, что Math.log - это @IntrinsicCandidate

matt 23.05.2024 11:18

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