【发布时间】:2020-01-09 23:52:55
【问题描述】:
long 值如何在不损失精度的情况下适合 Java 中的浮点数据类型。因为如果有精度损失,那么下面的 sn-p 应该会产生
long lMax = Long.MAX_VALUE;
float f = lMax;
System.out.println(lMax == f);
Output
true
【问题讨论】:
标签: java floating-point precision long-integer
long 值如何在不损失精度的情况下适合 Java 中的浮点数据类型。因为如果有精度损失,那么下面的 sn-p 应该会产生
long lMax = Long.MAX_VALUE;
float f = lMax;
System.out.println(lMax == f);
Output
true
【问题讨论】:
标签: java floating-point precision long-integer
问题是,为了比较long 和float,long 值被强制转换为float,并且发生了与赋值相同的精度损失...
如果您想“检测”精度损失,请尝试使用BigDecimals,如下所示:
System.out.println(new BigDecimal(lMax).compareTo(new Bigdecimal(f)));
我知道构造函数BigDecimal(double) 会将float 转换为double,但不会丢失精度...
对于大多数人来说,long 似乎比float 更“精确”。但是float 的范围是+/-10^38,比long 的范围要大很多...
long lMax = Long.MAX_VALUE;
float f = lMax;
System.out.println( lMax == f );
System.out.println( new BigDecimal( lMax ).compareTo( new BigDecimal( f ) ) );
System.out.println( ( (Float) f ).longValue() == lMax );
System.out.println( ( (Long) lMax ).floatValue() == f );
System.out.printf( "%d, %d%n", ( (Float) f ).longValue(), lMax );
f = --lMax;
System.out.println( lMax == f );
System.out.println( new BigDecimal( lMax ).compareTo( new BigDecimal( f ) ) );
System.out.println( ( (Float) f ).longValue() == lMax );
System.out.println( ( (Long) lMax ).floatValue() == f );
System.out.printf( "%d, %d%n", ( (Float) f ).longValue(), lMax );
输出:
true
-1
true
true
9223372036854775807, 9223372036854775807
true
-1
false
true
9223372036854775807, 9223372036854775806
【讨论】:
Java 语言规范中的Binary Numeric Promotion 规则是理解问题中代码 sn-p 行为的关键。特别是“否则,如果任一操作数的类型为浮点数,则另一个将转换为浮点数。”
在lMax == f 比较中,lMax 被转换为浮点数,正如float f = lMax 赋值所做的那样。如果你取相同的输入值,对它进行两次相同的转换,然后比较结果,无论转换是否改变了值,它们都将相等。
【讨论】:
如果您想很好地演示精度损失,请在获取系统时间(以毫秒为单位)并将 long 转换为 float 时尝试这个小脚本:
class Scratch {
public static void main(String[] args) throws InterruptedException {
float currentF;
long currentL;
float lastNewF = 0;
long lastL = System.currentTimeMillis();
long diff;
while (true) {
currentL = System.currentTimeMillis();
currentF = (float) currentL;
if (currentF != lastNewF) {
// we finally got a new "different" float value
lastNewF = currentF;
diff = currentL - lastL;
lastL = currentL;
System.out.println(diff + " milliseconds since we got a new time as float");
}
Thread.sleep(250);
}
}
}
您可以从输出中看到,您有时会损失多达 2 分钟的精度:
0 milliseconds since we got a new time as float
46964 milliseconds since we got a new time as float
131054 milliseconds since we got a new time as float
131093 milliseconds since we got a new time as float
131077 milliseconds since we got a new time as float
【讨论】:
lMax 是一个长数字。当你将它分配给一个浮点数时,它会失去精度。
float f = lMax;
当您将 long 与 float 进行比较时,在内部将 long 转换为 float 或 float 转换为 long。你的代码
lMax == f
将类似于
((Float)f).longValue() == lMax
这是 9.223372E18 == 9.223372E18 为真
或
((Long)lMax).floatValue() == f
这是 9223372036854775807 == 9223372036854775807 是真的
无论哪种方式,答案都是正确的。
【讨论】:
f 丢失精度,将其转换回long 会导致不同的long 值,并且可以看到丢失精度...(一般来说,不是关于Long.MAX_VALUE ...)
Long.MAX_VALUE -1 给出false....(System.out.printf( "%d, %d%n", ( (Float) f ).longValue(), lMax ); 输出:9223372036854775807, 9223372036854775806)
Long.MAX_VALUE。这就是我这么说的原因。
lMax的值=9223372036854775807; // 在你的 JVM 中可能会有所不同。
f 的值 = 9.223372E18
现在当您比较lMax == f 时,首先将 f 转换为 long,然后进行比较。转换成long后,f值与lMax相同。
以下代码同样适用。
long lMax = Long.MAX_VALUE; // value is 9223372036854775807
float f =lMax;
float f2 = 9223372036854775807.5f;
System.out.println(lMax == f);
System.out.println(lMax == f2);
输出
true
true
这里 f2 与 long 相比正在丢失十进制值。 f2的long值与lMax相同。
【讨论】: