Большие числа с плавающей запятой и двойные числа в java печатаются / сохраняются некорректно. Это связано с количеством значащих цифр?

В приложении, в котором я работаю, некоторые числа преобразуются и сохраняются из длинных (18 цифр) в числа с плавающей запятой / удвоением. Эти числа аналогичны ссылкам / идентификаторам, но не используются для расчетов. Недавно я заметил некоторые несоответствия в данных, хранящихся как float / double. Я пытаюсь понять, связано ли это поведение с тем, что числа с плавающей запятой называют значащими цифрами, и, возможно, с простым объяснением того же.

Мои вопросы по программе ниже:

  1. Выход №: 5 показывает действительно большое число (39 цифр до десятичной дроби) как максимальное значение с плавающей запятой. Почему тогда float не может точно отображать что-либо, превышающее 7 цифр. Это потому, что он поддерживает только 6-7 значащих цифр.
  2. Выходной номер 10 показывает действительно большое число как максимальное значение double. Почему тогда double не может точно отображать что-либо, превышающее 16 цифр. Это потому, что он поддерживает только 15 значащих цифр.
  3. Что на самом деле подразумевается под значащими цифрами? Означает ли это, что любые цифры после этого числа не будут представлены точно, независимо от того, стоит ли оно до или после десятичной точки?

ПРИМЕЧАНИЕ. После исследования этой темы я понял, что числа с плавающей запятой по своей природе неточны и не должны использоваться для представления вещей, требующих точного представления. Тем не менее я немного смущен приведенным выше поведением и значащими цифрами.

public class Main
{
    public static void main(String[] args) {
        System.out.printf( "1. Float value of 50000000115 is : %,f. Expected output was 50000000115.000000 \n", 50000000115f );
        System.out.printf( "2. Float value of 50000000116 is : %,f. Expected output was 50000000116.000000 \n", 50000000116f );
        System.out.printf( "3. Float value of 50000000117 is : %,f. Expected output was 50000000117.000000 \n\n", 50000000117f );

        System.out.printf( "4. Float value of 2175863596593954381 is : %,f. Expected output was 2175863596593954381.000000 \n\n", 2175863596593954381f );

        System.out.printf( "5. Float.MAX_VALUE: %,f\n\n", Float.MAX_VALUE );

        System.out.printf( "6. Double value of 50000000115 is : %,f\n", 50000000115d );
        System.out.printf( "7. Double value of 50000000116 is : %,f\n", 50000000116d );
        System.out.printf( "8. Double value of 50000000117 is : %,f\n\n", 50000000117d );

        System.out.printf( "9. Double value of 2175863596593954381 is : %,f. Expected output was  2175863596593954381.000000 \n\n", 2175863596593954381d );

        System.out.printf( "10. Double.MAX_VALUE: %,f\n\n", Double.MAX_VALUE );

        System.out.printf( "11. Float value of number gives expected result till 7 digits ie 12345678 is : %,f\n", 12345678f );
        System.out.printf( "12. Float value of number gives expected result till 7 digits ie 11111111 is : %,f\n", 11111111f );
        System.out.printf( "13. Double value of number gives expected result till 16 digits ie 1122334455667788 is : %,f\n", 1122334455667788d );
        System.out.printf( "14. Double value of number gives expected result till 16 digits ie 1111222233334444 is : %,f\n", 1111222233334444d );
    }
}

Вывод вышеуказанной программы

  1. Плавающее значение 50000000115 составляет: 49 999 998 976,000000. Ожидаемый результат был 50000000115.000000.
  2. Плавающее значение 50000000116 составляет: 49 999 998 976,000000. Ожидаемый результат: 50000000116.000000.
  3. Плавающее значение 50000000117 составляет: 49 999 998 976,000000. Ожидаемый результат: 50000000117.000000.

  4. Плавающее значение 2175863596593954381 составляет: 2,175,863,554,941,386,750,000000. Ожидаемый выпуск 2175863596593954381000000

  5. Float.MAX_VALUE: 340,282,346,638,528,860,000,000,000,000,000,000,000,000000

  6. Двойное значение 50000000115: 50,000,000,115,000000

  7. Двойное значение 50000000116: 50,000,000,116,000000
  8. Двойное значение 50000000117: 50,000,000,117,000000

  9. Двойное значение 2175863596593954381: 2,175,863,596,593,954,300,000000. Ожидаемый результат: 2175863596593954381.0 00000

  10. Double.MAX_VALUE: 179,769,313,486,231,570,000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

  11. Число с плавающей запятой дает ожидаемый результат до 7 цифр, т.е. 12345678: 12,345,678.000000

  12. Число с плавающей запятой дает ожидаемый результат до 7 цифр, т.е. 11111111: 11,111,111.000000
  13. Двойное значение числа дает ожидаемый результат до 16 цифр, т.е. 1122334455667788: 1,122,334,455,667,788.000000
  14. Двойное значение числа дает ожидаемый результат до 16 цифр, т.е. 1111222233334444: 1,111,222,233,334,444.000000

person Raj    schedule 16.09.2019    source источник
comment
Взгляните на класс BigDecimal и возможно, также BigInteger  -  person deHaar    schedule 16.09.2019
comment
ознакомьтесь с Что должен знать каждый компьютерный ученый о плавающей запятой Арифметика, один из первых абзацев об ошибках округления гласит: «Сжатие бесконечного числа действительных чисел в конечное число битов» требует приблизительного представления. (Примечание: величина не имеет значения: System.out.println(0.1+0.2)   -  person user85421    schedule 16.09.2019


Ответы (1)


Тип Float в Java (IEEE-754 binary32) фактически состоит из двух компонентов:

  • целое число единиц от -16,777,215 до +16,777,215 (2 24 -1) и
  • единица, равная степени двойки от 2 104 до 2 −149.

Используется наименьшая единица измерения (в пределах диапазона), которая поддерживает количество единиц в пределах диапазона.

Например, с 50 000 000 115, мы не можем использовать размер единицы 2048 (2 12), потому что 50 000 000 115 составляет примерно 24 414 062 единицы из 2048, что больше чем 16 777 215 единиц. Итак, мы используем размер блока 4096.

50 000 000 115 - это точно 12 207 031,27807617 1875 единиц из 4096, но мы можем использовать только целое число единиц, поэтому значение Float, ближайшее к 50 000 000 115, составляет 12 207 031 единиц из 4096, что составляет 49 999 998 976.

Другие значения в вашем вопросе представлены аналогично, но правила Java для форматирования чисел с помощью %,f приводят к ограниченному количеству десятичных цифр, используемых для отображения значения. Итак, в некоторых из ваших примеров мы видим завершающие нули, где фактическое математическое значение внутреннего числа отличается.

Для Double (IEEE-754 binary64) двумя компонентами являются:

  • целое число единиц от -9,007,199,254,740,991 до +9,007,199,254,740,991 (2 53 -1) и
  • единица, равная степени двойки от 2 972 до 2 −1074.
person Eric Postpischil    schedule 16.09.2019
comment
В следующий раз я хотел бы увидеть больше пояснений, например, по понятию unit. - person Joop Eggen; 16.09.2019
comment
хорошо объяснил .. это была тренировка для меня, чтобы понять: D - person John Humanyun; 17.12.2020