7

刷新浮点数(也是PDF)、IEEE-754 并参与了关于在转换为字符串时进行浮点舍入的讨论,这让我想到了修补程序:如何获得二进制表示的给定浮点数的最大值和最小值是平等的。

免责声明:对于本次讨论,我喜欢使用 IEEE-754 描述的 32 位和 64 位浮点。我对扩展浮点(80 位)或四边形(128 位 IEEE-754-2008)或任何其他标准(IEEE-854)不感兴趣。

背景:计算机不擅长以0.1二进制表示。在 C# 中,浮点数将其表示为3DCCCCCD内部(C# 使用四舍五入),双精度数表示为3FB999999999999A. 相同的位模式用于十进制0.100000005(float) 和0.1000000000000000124(double),但不用于0.1000000000000000144(double)。

为方便起见,以下 C# 代码给出了这些内部表示:

string GetHex(float f)
{
    return BitConverter.ToUInt32(BitConverter.GetBytes(f), 0).ToString("X");
}

string GetHex(double d)
{
    return BitConverter.ToUInt64(BitConverter.GetBytes(d), 0).ToString("X");
}

// float
Console.WriteLine(GetHex(0.1F));

// double 
Console.WriteLine(GetHex(0.1));

在 的情况下0.1,没有用相同位模式表示的小十进制数,任何0.99...99将产生不同的位表示(即,内部0.999999937产生的浮点数3F7FFFFF)。

我的问题很简单:如何找到内部存储在相同二进制表示中的给定浮点(或双精度)的最低和最高十进制值。

为什么:(我知道你会问)在 .NET 转换为字符串以及从字符串转换时查找舍入错误,以找到内部精确值并更好地理解我自己的舍入错误。

我的猜测是这样的:取尾数,去掉其余的,得到它的确切值,得到一个(尾数位)高,然后计算平均值:低于它的任何东西都会产生相同的位模式。我的主要问题是:如何将小数部分作为整数(位操作它不是我最强的资产)。Jon Skeet 的 DoubleConverter类可能会有所帮助。

4

2 回答 2

6

解决问题的一种方法是查找浮点数的ULPLast PlaceUnit的大小。稍微简化一下,这是给定浮点数和下一个更大数字之间的距离。再次简化一点,给定一个可表示的浮点值 x,任何值在 (x - 1/2 ulp) 和 (x + 1/2 ulp) 之间的十进制字符串在转换为浮点数时将被舍入为 x -点值。

诀窍是 (x +/- 1/2 ulp) 不是可表示的浮点数,因此实际计算它的值需要您使用更宽的浮点类型(如果可用)或任意宽度的大小数或类似的类型来进行计算。

如何找到 ulp 的大小?一种相对简单的方法大致是您建议的方法,这里写的是 C-ish 伪代码,因为我不懂 C#:

float absX = absoluteValue(x);
uint32_t bitPattern = getRepresentationOfFloat(absx);
bitPattern++;
float nextFloatNumber = getFloatFromRepresentation(bitPattern);
float ulpOfX = (nextFloatNumber - absX);

这是有效的,因为在 x 的位模式上加一恰好对应于在 x 的值上加一 ulp。减法中不会发生浮点舍入,因为所涉及的值非常接近(特别是 ieee-754 浮点算术的定理,如果两个数 x 和 y 满足 y/2 <= x <= 2y,然后x - y精确计算)。这里唯一需要注意的是:

  1. 如果 x 恰好是最大的有限浮点数,这将不起作用(它将返回inf,这显然是错误的)。
  2. 如果您的平台不能正确支持逐渐下溢(例如嵌入式设备以清零模式运行),则这不适用于非常小的 x 值。

听起来您不太可能处于这两种情况中,因此这应该可以很好地满足您的目的。

现在您知道 x 的 ulp 是什么,您可以找到四舍五入为 x 的值的区间。您可以精确地以浮点计算 ulp(x)/2,因为浮点除以 2 是精确的(同样,除非下溢)。然后你只需要计算 x +/- ulp(x)/2 合适的更大的浮点类型(double如果你有兴趣的话会起作用float)或 Big Decimal 类型的值,并且你有你的间隔。

我通过这个解释做了一些简化的假设。如果您需要真正准确地说明这一点,请发表评论,当我有机会时,我将扩展有点模糊的部分。


另请注意您的问题中的以下陈述:

在 0.1 的情况下,没有用相同的位模式表示的低十进制数

是不正确的。您只是碰巧查看了错误的值(0.999999... 而不是 0.099999... - 很容易打错)。

于 2009-11-03T16:58:18.620 回答
1

Python 3.1 刚刚实现了这样的功能:查看更新日志(向下滚动一点)错误报告

于 2009-11-17T20:22:06.390 回答