再次访问IEEE-754双(64位浮点)与长(64位整数)

时间:2010-12-03 19:28:16

标签: java javascript double long-integer ieee-754

我正在重新审视一个问题(How to test if numeric conversion will change value?),就我而言,这个问题已经完全解决了。问题是检测特定数值何时会溢出JavaScript的IEEE-754数字类型。之前的问题是使用C#,标记的答案完美无缺。

现在我正在执行完全相同的任务,但这次是在Java中,它不起作用。 AFAIK,Java使用IEEE-754作为其双数据类型。所以我应该能够来回摆动它以强制失去精确度,但它往返。令我感到困惑的是,我开始深入研究Java,现在我真的很困惑。

在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!"); }

这将在Java中返回{value = -9223372036854775808L的false

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

这将在Java中返回{value = -9223372036854775808L的false

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 个答案:

答案 0 :(得分:6)

-9223372036854775808L 可表示为IEEE-754双精度数字。它正好是-2^63,它具有双重表示-1.0 x 2^63和编码0xc3e0000000000000

Double能够表示比这更大的数字。但是,它无法在可表示数字的范围内表示所有整数。例如,如果您在数字中添加一个,您将得到-9223372036854775807 = -2^63 + 1可表示为双精度值,并且无法在往返转换中存活。

-2^63 + 1转换为double会将其四舍五入到最接近的可表示的double值,即-2^63;转换回长期将保留该值。

编辑:您在JavaScript测试中做了什么平台?在当前的Safari中,

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

评估为True