Что каждый программист должен знать о цифрах с плавающей точкой?

1 minute read

Введение

Давайте рассмотрим классический пример того, с чем может столкнуться программист не изучавший ранее как работают числа с плавающей точкой в современных компьютерных системах:

public class FloatingNumbersMagic {

    public static void main(String[] args) {
        double f1 = 0.3f;
        double f2 = 0.4f;
        double sum = 0.3f + 0.4f;

        System.out.println(sum);
    }
}

Результат поразит неискушенного новичка:

Вместо 0.7 мы получим
0.7000000476837158

Почему мы получили такой странный результат?

Потомучто внутри, компьютеры используют формат который не может ТОЧНО сохранить дробные числа(такие числа как: 0.1, 0.2 или 0.3 и т.п.) Этот формат - двоичный с плавающей точкой.

Когда ваш код компилируется или интерпретируется ваше число “0.1” уже округляется до ближайшего числа с плавающей точкой. Таким образом даже еще не начав реально считать мы получаем небольшие погрешности при переводе числа в компьютерный формат(в Java это типы данных float или double).

Почему компьютеры делают так?

На низком уровне, компьютеры построены из биллионов электрических элементов которые имеют всего два состояния, обычно это или 1 или 0(низкое напряжение или высокое). На основе этих элементов ЛЕГКО построить схемы хранения ДВОИЧНЫХ чисел и выполнения вычислений с ними.

Конечно возможно сэмулировать поведение десятичных чисел используя двоичное представление однако это будет менее производительно. Если бы компьютеры использовали десятичные числа они бы были медленнее и потребляли бы больше памяти по сравнению с использованием двоичной арифметики.

Когда нам надо быть осторожными?

Нужно быть осторожным ВСЕГДА когда вы используете float и double. Если у вас финансовые вычисления основаны на этих типах данных у вас могут быть проблемы. Для корректной работы с деньгами в java лучше использовать BigDecimal

public class BigDecimalExample {

    public static void main(String[] args) {
        BigDecimal n1 = new BigDecimal("0.3");
        BigDecimal n2 = new BigDecimal("0.4");
        BigDecimal sum = n1.add(n2);
        System.out.println(sum.toString());
    }
}

Результат выполнения программы выше вполне вас удовлетворит:

0.7

Подробнее про данную тему можно почитать в следующих источниках:

Leave a Comment