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

336
29 октября 2021, 03:00
  1. Почему Float.MAX_VALUE-1 == Float.MAX_VALUE – true?
  2. Какие максимальные числа для float и double, с которыми такой проблемы не будет?
Answer 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 и повторное вычитание не даст результата).

Answer 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));
    }
}
READ ALSO
Как сделать так, чтобы на всех вкладках JTabbedPane работал один буфер обмена?

Как сделать так, чтобы на всех вкладках JTabbedPane работал один буфер обмена?

У меня есть проект текстового редактора с классом, который расширяет JTabbedPane

167
Как обновить adb.exe для Android Studio?

Как обновить adb.exe для Android Studio?

Запускаю эмулятор в Android Studio, появляется окно:

125
BeanShell sampler не импортирует библиотеки

BeanShell sampler не импортирует библиотеки

Нужно написать простой get запрос, но BeanShell sampler почему-то не хочет импортировать библиотеки

198
Проблема с запуском эмулятора в Android Studio

Проблема с запуском эмулятора в Android Studio

Пытаюсь запустить один из этих эмуляторов:

155