是否存在错误计算为略低于 .5 或 .0 的浮点数?

Does a float exist that wrongly evaluates to slightly below .5 or .0?

我正在使用它来获得正确舍入的整数除法:

Math.round((float)dividend/(float)divisor)

除以 0 已在别处处理。除此之外,这会失败吗?
我可以想象该除法的结果是一个浮点数, 应该 something.5,但实际上计算结果是 something.4999997 左右。这样的浮动存在吗?

为了自己检查,我尝试像这样打印出所有接近 .5 的浮点数:

for(int i=Integer.MIN_VALUE+1; i!=Integer.MIN_VALUE; i++){
   float f=Float.intBitsToFloat(i);
   if ((f>(int)f+0.4999999 && f<(int)f+0.5000001 && f!=(int)f+0.5)
     ||(f<(int)f-0.4999999 && f>(int)f-0.5000001 && f!=(int)f-0.5)){
      System.out.println(Float.toString(f));
   }
}

Integer.MIN_VALUE 已手动检查。)

只打印了:

-0.4999999
-0.49999994
-0.49999997
-0.50000006
0.4999999
0.49999994
0.49999997
0.50000006

因为 -0.50.5 肯定是浮点数,这意味着不存在这样的浮点数。但也许我的范围太小并且有问题的数字超出了范围。无论如何,我想要第二个意见,所以我在这里问,也许有人对 IEEE754 有先进的内部知识,并且有证据证明为什么这绝对不可能发生。

将范围扩大一位数,其中 this result、none 个数字回答了这个问题。

相关问题:something.99999呢?一个similar test had this result。所有这些浮点数都与精确整数的浮点数不同。

你问的不是很清楚:'fail' 是什么意思?我想你想知道是否有可能在你的第一行中有股息和除数的整数值,并且由于四舍五入问题,结果不是你所期望的。

我认为这是可能的。在下面的代码中,999,999,999/2,000,000,000 略小于 0.5,因此我们期望结果为 0.0。但是在 Java 中,结果是 1.0。如果你做 Math.round(dividend/divisor) 你做整数除法并得到正确答案。

int dividend = 999999999;
int divisor = 2000000000;
float result = Math.round((float)dividend/(float)divisor);
System.out.println(result);  // 1.0

你是这个意思吗?这里的问题不是没有正确舍入的浮点数,而是在将整数转换为浮点数时你失去了准确性。 float(dividend) 实际上是 1.0E9.

简短回答:是的,它可能会失败。

更长的答案:您的问题完全取决于您正在考虑的整数和 floating-point 格式 - 这不仅仅是关于 IEEE 754 的问题。

例如,如果您正在查看 IEEE 754 "double precision" binary64 类型(假设您的语言称之为 double),并且只考虑 fixed-width 32 位整数(有符号或无符号),那么每个这样的整数都可以精确表示为 binary64 浮点数,因此在这种情况下转换为 double 不会更改值,您确实可以依赖 (double)x / (double)y 是正确的根据当前舍入模式舍入(假设您使用的任何语言都能保证除法根据当前舍入模式正确舍入,例如因为它保证遵循 IEEE 754 标准)。

如果另一方面 float 是 IEEE 754 binary32 "single-precision" floating-point 类型,则有 32 位有符号整数的示例 xy 使得 x 可以被 y 整除(因此商是一个小的、可以完美表示的整数),但是 (float)x / (float)y 给出了不同的值。一个这样的例子是 x = 296852655y = 59370531。那么 x / y 恰好是 5,但是最接近 x 的 binary32 float 是 296852640.0,最接近 y 的 binary32 float 是 59370532.0,并且最接近商 296852640.0 / 59370532.0 的 binary32 浮点数正好是 4.999999523162841796875,距离 5.0.

一个 ulp

binary64 "double precision" IEEE 754 floating-point 和 64 位整数也存在类似的例子。例如,对于 x = 2135497568948934666y = 305071081278419238xy 都适合一个带符号的 64 位整数类型,x / y 正好是 7 , 但 (double)x / (double)y6.99999999999999911182158029987476766109466552734375, 又与确切结果相差 1 ulp。

这里有几个例子更接近你在标题中提出的问题:再次假设 IEEE 754 single-precision 算法,取 x = 592534758y = 395023172,它们都是可表示为带符号的 32 位整数。那么x / y正好是1.5,但是(float)x / float(y)就是1.50000011920928955078125。例如,舍入方向相反,如果 x = 1418199327y = 945466218,则 x / y1.5(float)x / (float)y1.49999988079071044921875

但是,值得一提的是 0.5 不存在这样的示例:如果 xy 是 32 位有符号整数,那么 x / y 恰好是 0.5,那么 than 意味着 y 恰好是 x 的两倍,而 that 意味着 (float)y 恰好是 (float)x 的两倍](因为如果 (float)x 是最接近 x 的可表示值,那么 2.0 * (float)x 必须是最接近 2.0 * x 的可表示值)。所以在那种情况下,(float)x / (float)y 也保证是 0.5