Pull to refresh

Comments 15

Рассказали бы еще про особенности вещественной арифметики (например A + B = A, A * B = 0), представление INF и NaN. Конкретно эта информация реально очень полезна и зачастую бывает неизвестна даже опытным программистам.

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

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

Более интересен вопрос, является ли математика на java полностью переносимой. Будет ли бит-в-бит совпадение если код выполнить на разных ОС с разной разрядностью и разных версиях jre.

Если один и тот же код написать на double, StrictMath и Math, будет ли одинаковый результат?

Емнип, strictfp как раз отвечает за переносимость вычислений, в стандарте прописано. Правда в 17 джаве он уже устарел.

Но насчёт Math и StrictMath — не могу так сразу сказать.

До версии 1.2 - да, все double вычислялись по IEEE754
После версии 17 - тоже да (SSE2 сейчас есть почти везде, а там аппаратная поддержка).
https://openjdk.org/jeps/306

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

float это 32 бита, double это 64 бита. Вы уверены что округления осуществляются одинаково на всех платформах, где работает java?

Например, в одной из версий эмулятора Android была разница при умножении двух double. До сих пор не знаю, это была ошибка в сборке образа для эмулятора, или такое считается нормальным.

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

Я к сожалению не работал Андроидом, но насколько я знаю там не совсем стандартная JVM. Плюс гугл говорит, что последний Android 13 использует Java 11, где переносимость все еще не гарантировалась.

Java 17 - относительно новая версия, ей меньше 2 лет, много где на нее еще не перешли.

Написав код на java вы же не будете выяснять особенности JVM? На ПК тоже jre разные существуют. Кроме x86 есть ARM и Apple со своим процессором. Так что вопрос переносимости вычислений с плавающей точкой интересен.

В ARM тоже есть поддержка IEEE754. Скорее всего во всех популярных процессорах она есть.

Если где-то нет, то придется эмулировать, как в древней Java 1.2.

Но функционально вычисления будут одинаковы на любой JVM 17

функционально вычисления будут одинаковы

бит-в-бит или примерно?

Вот пример операции:

final double result = Double.longBitsToDouble( 0x40d5bd276215b682L ) * Double.longBitsToDouble( 0x411b2dadadf85664L );
System.out.println( "result = " + Double.doubleToRawLongBits( result ) );

Ещё есть IBM формат float-ов, также 32-х битный.

1. Без упоминания 'проблем' с этим форматом не хватает многого. Что иногда a+b может не равняться b+a. Или как тут с 0.1 + 0.2 не всегда 0.3 выдаёт:

https://0.30000000000000004.com/

2.не указано, чем отличаются нормальная от двойной точности. И чтo у двойной точности bias не 127, а 1023 итд.

3. Не хватает описания причин для этого формата. Немного истории и вообще причин для вообще - зачем всё это? Как для этого формата, так и для например представления негативных чисел в памяти - причина для этого, как-бы это не банально не звучал - что основа памяти - байт, и в своей основе байт не что иное как числа т 0 до 255. А нам нужны и другие представления.

У вас ой прямо в первом пункте. Все корнер кейсы будут работать неверно. А их у float прям много.

Посмотрите как правильно:

public static double signum(double d) {
    return (d == 0.0 || Double.isNaN(d))?d:copySign(1.0, d);
}

public static double copySign(double magnitude, double sign) {
    return Double.longBitsToDouble((Double.doubleToRawLongBits(sign) &
                                    (DoubleConsts.SIGN_BIT_MASK)) |
                                   (Double.doubleToRawLongBits(magnitude) &
                                    (DoubleConsts.EXP_BIT_MASK |
                                     DoubleConsts.SIGNIF_BIT_MASK)));
}

Sign up to leave a comment.

Articles