IEEE-754 Двойной (64-битное с плавающей запятой) и Длинный (64-битное целое)
Я возвращаюсь к вопросу ( Как проверить, изменит ли числовое преобразование значение?), Который, насколько я был обеспокоен, был полностью решен. Проблема заключалась в том, чтобы определить, когда конкретное числовое значение переполняет тип числа IEEE-754 в JavaScript. Предыдущий вопрос использовал C#, и помеченный ответ работал отлично.
Сейчас я делаю ту же самую задачу, но на этот раз на Java, и она не работает. AFAIK, Java использует IEEE-754 для своего двойного типа данных. Таким образом, я должен быть в состоянии бросать это назад и вперед, чтобы вызвать потерю точности, но это круглая поездка. Озадаченный этим, я начал углубляться в Java, и теперь я действительно запутался.
И в C#, и в Java значения min и max для long одинаковы:
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);
}
}
1 ответ
-9223372036854775808L
представляется как число двойной точности IEEE-754. Это точно-2^63
, который имеет двойное представление-1.0 x 2^63
и кодирование 0xc3e0000000000000
,
Double способен представлять числа намного, намного больше, чем это. Однако он не способен представлятьвсе целые числа в диапазоне представимых чисел. Например, если вы добавите один к номеру, вы получите -9223372036854775807 = -2^63 + 1
, которыйне может быть представлен как значение двойной точности, и не выдержит двустороннего преобразования.
преобразование -2^63 + 1
удвоить будет округлять его до ближайшего представимого двойного значения, которое -2^63
; обратное преобразование в long сохранит это значение.
Изменить: На какой платформе вы тестировали JavaScript? В текущем Safari,
"-9223372036854775808" === Number(-9223372036854775808).toFixed()
оценивает как True
,