Страницы

Поиск по вопросам

воскресенье, 9 февраля 2020 г.

Почему Float.MAX_VALUE-1 == Float.MAX_VALUE – true?

#java #float #double



Почему Float.MAX_VALUE-1 == Float.MAX_VALUE – true?
Какие максимальные числа для float и double, с которыми такой проблемы не будет?

    


Ответы

Ответ 1



Для начала рассмотрим аналогию, которая пояснит ситуацию. Представьте, что вы имеете возможность хранить только 3 десятичные цифры и вам нужно записать число 12345. Не получится, но вы можете сохранить его в другом формате: 123 * 10**2. Именно так (упрощённо говоря) и работает арифметика с плавающей запятой: хранятся только самые старшие биты числа и порядок (сдвиг, степень, экспонента - как хотите, так и называйте). Теперь вы хотите отнять единицу. Ну давайте попробуем вместе: 12345 - 1 = 12344. Однако вы можете хранить только 3 цифры, стало быть, имеем 123 * 10**2, а это в точности то же самое, что было! То есть вычитание единицы не влияет на ситуацию, потому что единица влияет на те цифры, которые не хранятся, она как бы слишком маленькая. Подробнее об этом есть здесь. Там на примере игрушечного формата чисел с плавающей запятой идёт рассказ об этом и некоторых других неожиданных эффектах. Далее вы спрашиваете какое максимальное число этим свойством не обладает. Коль скоро у типа float 23 бита отводится под цифры (мантисса), а ещё один бит всегда единица, мы получаем, что из чисел вплоть до 2**24 можно вычитать единицу и она БУДЕТ вычитаться корректно. Однако далее между 2**24 и 2**25 числа идут с шагом два, поэтому каждое второе при вычитании единицы будет посередине между чётным и нечётным числами, а потому по правилу округления nearest-to-even, half-to-even число будет округляться вниз (к чётному). Таким образом, на этом интервале все ЧЁТНЫЕ числа, НЕ делящиеся на 4, будут уменьшаться на 2 при попытке отнять 1. Остальные числа НЕ будут уменьшаться. Например, число 33554430 (не делится на 4) станет равным 33554428, однако при повторном вычитании 1 ничего не изменится. Кстати, число 33554430=2**25-2 и есть максимальное, о котором вы просите. Однако если вам нужно, чтобы и предыдущее (полученное после вычитания 1) тоже нормально вычиталось, то тогда наибольшим будет 2**24+2. После вычитания 1 вы получите 2**24 (то есть оно уменьшится на 2) и далее при вычитании 1 будет уменьшаться на 1. Для double это будет 2**53+2 = 9007199254740994 (если нужно, чтобы все предыдущие вычитались нормально) и 2**54-2 = 18014398509481982 (если после вычитание 1 оно уменьшится на 2 и повторное вычитание не даст результата).

Ответ 2



Можно вот так проверить: public static void main(String[] args) { float i; for (i = 1; i < 1e38; i *= 10) { System.out.println(i + " " + (Float.MAX_VALUE-i == Float.MAX_VALUE)); } }

Комментариев нет:

Отправить комментарий