IEEE-754 Double (64-битная с плавающей запятой) против Long (64-битное целое) Revisited

Я снова возвращаюсь к вопросу ( Как проверить, изменит ли числовое преобразование значение? ), который, насколько мне известно, был полностью решено. Проблема заключалась в том, чтобы определить, когда конкретное числовое значение будет переполнять тип JavaScript IEEE-754 Number. В предыдущем вопросе использовался C #, и отмеченный ответ работал отлично.

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

В C # и Java минимальные и максимальные значения для 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);
}

Это возвращает истину для (значение = -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);
    }
}

12
задан Community 23 May 2017 в 12:08
поделиться