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,

Другие вопросы по тегам