Точность чисел
На определённые исследования по этой теме меня сподвиг вопрос теста:
Почему?
Магия, не иначе. Корень зла в том, что на типы long и double выделено по 8 байт. Вот только у long все они идут на представление мантиссы, а у double нет: 53 бита на мантиссу и 11 на ординату (степень).
Приведу более короткий пример на мелких числах:
Если в long число выглядит как 12345, то в double будет нечто типа 123 * 10^2 = 12300
А всё из-за нехватки разрядов...
Сколько же можно вычитать?
Оказывается, что 511. При 512 ответ уже станет false, что достаточно логично.
Посчитаем сами:
1 бит знаковый. то есть в long остаётся 63, а в double 52.
Разница в 11 бит. Что можно ими закодировать? 512 - это 10 бит. И ещё 1 даёт правильное округление.
2^10 + 1 = 1024. Отсюда следует, что если в long заполнены и нулевой и 62 биты (то есть double не может откинуть с какой-то стороны биты, заменив соответствующей ординатой), то в double эти числа идут с большой потерей точности: на 1024. Учитывая правильное округление, то максимальная ошибка составит 512.
Небольшой эксперимент, позволяющий увидеть, что же происходит:
Результат работы:
Как видим, самый верхний пример показывает, что шаг в double равен 1023, но это только потому, что нет ещё одного разряда. А вот следующие шаги уже по 1024.
Вывод:
Несмотря на то, что в приведении long к double компилятор даже не требует явного преобразования типов, преобразование это опасно. поэтому нужно внимательнее подходить к таким вещам.
На определённые исследования по этой теме меня сподвиг вопрос теста:
long l1 = Long.MAX_VALUE;
long l2 = Long.MAX_VALUE-1;
double d1 = Long.MAX_VALUE;
System.out.println("l1 == d1: " + (l1 == d1));
System.out.println("l2 == d1: " + (l2 == d1));Чтобы никто не мучился, сразу скажу, вывод даст:l1 == d1: true l2 == d1: true
Почему?
Магия, не иначе. Корень зла в том, что на типы long и double выделено по 8 байт. Вот только у long все они идут на представление мантиссы, а у double нет: 53 бита на мантиссу и 11 на ординату (степень).
Приведу более короткий пример на мелких числах:
Если в long число выглядит как 12345, то в double будет нечто типа 123 * 10^2 = 12300
А всё из-за нехватки разрядов...
Сколько же можно вычитать?
Оказывается, что 511. При 512 ответ уже станет false, что достаточно логично.
Посчитаем сами:
1 бит знаковый. то есть в long остаётся 63, а в double 52.
Разница в 11 бит. Что можно ими закодировать? 512 - это 10 бит. И ещё 1 даёт правильное округление.
2^10 + 1 = 1024. Отсюда следует, что если в long заполнены и нулевой и 62 биты (то есть double не может откинуть с какой-то стороны биты, заменив соответствующей ординатой), то в double эти числа идут с большой потерей точности: на 1024. Учитывая правильное округление, то максимальная ошибка составит 512.
Небольшой эксперимент, позволяющий увидеть, что же происходит:
double d1 = Long.MAX_VALUE;
double d2 = Long.MAX_VALUE - 511;
double d3 = Long.MAX_VALUE - 512;
double d4 = Long.MAX_VALUE - 1024;
double d5 = Long.MAX_VALUE - 1024-512;
double d6 = Long.MAX_VALUE - 1024*2;
double d7 = Long.MAX_VALUE - 1024*2-512;
long l1 = (long) d1;
long l2 = (long) d2;
long l3 = (long) d3;
long l4 = (long) d4;
long l5 = (long) d5;
long l6 = (long) d6;
long l7 = (long) d7;
System.out.println(Long.toBinaryString(l1));
System.out.println(Long.toBinaryString(l2));
System.out.println(Long.toBinaryString(l3));
System.out.println(Long.toBinaryString(l4));
System.out.println(Long.toBinaryString(l5));
System.out.println(Long.toBinaryString(l6));
System.out.println(Long.toBinaryString(l7));
System.out.println(l1);
System.out.println(l2);
System.out.println(l3);
System.out.println(l4);
System.out.println(l5);
System.out.println(l6);
System.out.println(l7);Результат работы:
111111111111111111111111111111111111111111111111111111111111111 111111111111111111111111111111111111111111111111111111111111111 111111111111111111111111111111111111111111111111111110000000000 111111111111111111111111111111111111111111111111111110000000000 111111111111111111111111111111111111111111111111111100000000000 111111111111111111111111111111111111111111111111111100000000000 111111111111111111111111111111111111111111111111111010000000000 9223372036854775807 9223372036854775807 9223372036854774784 9223372036854774784 9223372036854773760 9223372036854773760 9223372036854772736
Как видим, самый верхний пример показывает, что шаг в double равен 1023, но это только потому, что нет ещё одного разряда. А вот следующие шаги уже по 1024.
Вывод:
Несмотря на то, что в приведении long к double компилятор даже не требует явного преобразования типов, преобразование это опасно. поэтому нужно внимательнее подходить к таким вещам.
Комментариев нет:
Отправить комментарий