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

我正在重新审视一个问题( 如何测试数字转换是否会改变值? ),就我所关注的问题而言,我已经完全解决了。 问题是检测特定数值何时会溢出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中返回false (值= -9223372036854775808L):

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

这在Java中返回false (值= -9223372036854775808L):

 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); } 

这对于(value = -9223372036854775808L)返回true ,但不太准确:

 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); } } 

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

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

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

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

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

评估为True