2010-12-03 2 views
12

Я возвращаюсь к вопросу (How to test if numeric conversion will change value?), который, насколько я был заинтересован, был полностью решен. Проблема заключалась в обнаружении, когда определенное числовое значение переполнило бы тип номера IEEE-754 типа JavaScript. В предыдущем вопросе использовался C#, и отмеченный ответ работал отлично.IEEE-754 Двойная (64-разрядная плавающая точка) по сравнению с длинным (64-разрядное целое). Повторно рассмотрен

Теперь я выполняю ту же задачу, но на этот раз в Java, и она не работает. AFAIK, Java использует IEEE-754 для своего двойного типа данных. Поэтому я должен был бы отбросить его назад и вперед, чтобы заставить потеря точности, но это круговые поездки. Сбитый с толку, я начал глубже натыкаться на Java, и теперь я действительно смущен.

В обеих C# и Java, минимальное и максимальное значение для длины одинаково:

long MIN_VALUE = -9223372036854775808L; 
long MAX_VALUE = 9223372036854775807L; 

AFAIK, эти значения находятся за пределами представимых чисел в IEEE-754 из-за фиксированные биты, зарезервированных для показателя и знак.

// this fails in browsers that have stuck with the pure ECMAScript Number format 
var str = Number(-9223372036854775808).toFixed(); 
if ("-9223372036854775808" !== str) { throw new Error("Overflow!"); } 

Это возвращает false для (значение = -9223372036854775808L) в Java:

boolean invalidIEEE754(long value) { 
    try { 
     return ((long)((double)value)) != value; 
    } catch (Exception ex) { 
     return true; 
    } 
} 

Это возвращает false для (значение = -9223372036854775808L) в Java:

boolean invalidIEEE754(long value) { 
    // trying to get closer to the actual representation and 
    // being more explicit about conversions 
    long bits = Double.doubleToLongBits(Long.valueOf(value).doubleValue()); 
    long roundtrip = Double.valueOf(Double.longBitsToDouble(bits)).longValue(); 
    return (value != roundtrip); 
} 

Это возвращает true для (значение = -9223372036854775808L), но менее точно:

boolean invalidIEEE754(long value) { 
    return (0x0L != (0xFFF0000000000000L & (value < 0L ? -value : value))); 
} 

Почему это так работает? Я упускаю что-то вроде оптимизации компилятора, например. компилятор обнаруживает мои конверсии и «фиксирует» их для меня?

Редактировать: Добавление тестового образца по запросу. Все три из этих тестов не в состоянии:

import static org.junit.Assert.*; 
import org.junit.Test; 

public class FooTests { 

    @Test 
    public void ieee754One() { 
     assertTrue(((long)((double)Long.MIN_VALUE)) != Long.MIN_VALUE); 
    } 

    @Test 
    public void ieee754Two() { 
     long bits = Double.doubleToLongBits(Long.valueOf(Long.MIN_VALUE).doubleValue()); 
     long roundtrip = Double.valueOf(Double.longBitsToDouble(bits)).longValue(); 

     assertTrue(Long.MIN_VALUE != roundtrip); 
    } 

    @Test 
    public void ieee754Three() { 
     long bits = Double.doubleToRawLongBits(Long.valueOf(Long.MIN_VALUE).doubleValue()); 
     long roundtrip = Double.valueOf(Double.longBitsToDouble(bits)).longValue(); 

     assertTrue(Long.MIN_VALUE != roundtrip); 
    } 
} 
+0

Оба метода возвращают `false` для меня. JDK 1.6.0_21. – axtavt 2010-12-03 19:37:50

+0

Интересно! Теперь я действительно смущен. Шахта работает через JUnit, но также должна быть на JDK 1.6.0_21. – mckamey 2010-12-03 19:42:06

ответ

6

-9223372036854775808Lявляется представима как IEEE-754 двойной точности. Это точно -2^63, который имеет двойное представление -1.0 x 2^63 и кодировку 0xc3e0000000000000.

Двойной способен представлять цифры намного, намного больше, чем это. Тем не менее, он не способен представлять все целые числа в диапазоне представимых чисел. Например, если вы добавите номер к номеру, вы получите -9223372036854775807 = -2^63 + 1, что равно , а не, представляемое как значение двойной точности, и не сможет пережить конверсию в оба конца.

Преобразование -2^63 + 1, чтобы удвоить округление до ближайшего представимого двойного значения, которое равно -2^63; преобразование обратно в long сохранит это значение.

Редактировать: На какой платформе вы проверили JavaScript? В настоящее время Safari,

"-9223372036854775808" === Number(-9223372036854775808).toFixed() 

оценивается как True.

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

  • Нет связанных вопросов^_^