2013-07-29 1 views
-2

Вот что я пробовал:Преобразование Long удвоится в Java, я нашел некоторое несоответствие

public class LongToDoubleTest{ 
    public static void main(String... args) { 
     System.out.println(Long.MAX_VALUE); 
     System.out.println(Long.MAX_VALUE/2); 
     System.out.println(Math.floor(Long.MAX_VALUE/2)); 
     System.out.println(new Double(Math.floor(Long.MAX_VALUE/2)).longValue()); 
    } 
} 

Вот результат:

9223372036854775807 
4611686018427387903 
4.6116860184273879E18 
4611686018427387904 

я изначально пытается выяснить, возможно ли сохранить половину Long.MAX_VALUE в два раза без потери данных. Поэтому у меня был тест со всеми этими строками, кроме последнего. Так оказалось, что я прав, и последний 3 пропал без вести. Затем, чтобы уточнить это, я добавил последнюю строку. И не 3 появился, но 4. Поэтому мой вопрос: откуда появились 4 и почему это 4, а не 3. Потому что 4 на самом деле является неправильным значением здесь. P.S. Я очень плохо разбираюсь в IEEE 754, так что поведение, которое я нашел, абсолютно правильно, но 4, очевидно, является неправильным значением здесь.

+0

4.6116860184273879 ** E18 ** –

ответ

6

Вы должны понимать, что не каждый long может быть точно представлен в виде double - в конце концов, есть 256 long значения и самое, что многие double ценностей (хотя много тех, которые зарезервированы для " а не числа "и т. д.). Учитывая, что есть такжеdouble значения, которые явно не являются значениями long (например, 0,5 - любое нецелое число, для начала), что означает, что не может быть значения double для каждого значения long.

Это означает, что если вы начинаете с long значением, которое не может быть представлено, преобразовать его в double, а затем обратно к long, это вполне разумно, чтобы получить обратно другой номер.

Абсолютная разница между соседними значениями double возрастает по мере увеличения величины чисел. Поэтому, когда числа очень малы, разница между двумя числами действительно крошечная (очень очень маленькая) - но когда числа становятся больше - например, выше диапазона int - зазор между числами становится больше ... больше, чем 1. Таким образом, смежные значения double вблизи Long.MAX_VALUE могут быть на расстоянии друг от друга. Это означает, что несколько значений long будут отображаться на одном и том же ближайшем double.

+0

Итак, '4' Я получил, в конце концов, абсолютно непредсказуем? Это может быть с равной вероятностью '9' или' 7'? – dhblah

+0

@dhblah: Ну нет - для любого заданного 'long', будет ровно один« двойной », чтобы он был округлен. Это предсказуемо, если вы посмотрите на точные битовые узоры, но не легко предсказуемы невооруженным глазом. –

+0

Я имею в виду, что '4' не означает, что округление пошло не так, просто из-за битового шаблона? – dhblah

0

Арифметика здесь вполне предсказуема.

Формат Java double использует один бит для знака и одиннадцать бит для экспоненты. Это оставляет 52 бита для кодирования значения (часть доли числа с плавающей запятой).

Для нормальных чисел значение имеет старший 1 бит, за которым следует двоичная точка, за которой следуют 52 бита кодировки.

Когда Long.MAX_VALUE/2, 4611686018427387903, преобразовано в double, оно должно быть округлено, чтобы соответствовать этим битам. 4611686018427387903 - 0x3fffffffffffffff. Есть 62 значимых бита (два ведущих нуля, которые незначительны, а затем 62 бит). Поскольку не все 62 бита подходят к 53 битам, мы должны их округлить. Последние девять бит, которые мы должны устранить путем округления, - это 111111111 . Мы должны либо округлить их до нуля (производя 0x3ffffffffffffe00), либо до 1000000000 (который переносится в следующий более высокий бит и производит 0x4000000000000000).Последнее изменение (добавление 1) меньше прежнего изменения (вычитание 111111111). Нам нужна меньшая ошибка, поэтому мы выбираем последнюю и округляем. Таким образом, мы округлим 0x3fffffffffffffff до 0x4000000000000000. Это 2 , что составляет 4611686018427387904.

+1

О, отличное объяснение. Если бы я мог это понять. – dhblah

Смежные вопросы