c# - 查找具有相同内部表示的 float / double 的最小值/最大值

标签 c# .net floating-point bit-manipulation ieee-754

刷新 floating points (也是 PDF ),IEEE-754 并参与 in this discussion on floating point rounding when converting to strings ,让我想到了修补程序:如何获得二进制表示形式相等的给定 float 的最大值和最小值。

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

背景:计算机不擅长用二进制表示法来表示 0.1。在 C# 中, float 在内部表示为 3DCCCCCD(C# 使用最近舍入法),double 表示为 3FB999999999999A。相同的位模式用于十进制 0.100000005(浮点型)和 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 的 float 在内部产生 3F7FFFFF)。

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

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

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

最佳答案

解决您的问题的一种方法是找到 ULPLast 中的 Unit 的大小地方,你的 float 。稍微简化一下,这是给定 float 与下一个更大数之间的距离。同样,稍微简化一下,给定一个可表示的浮点值 x,任何值介于 (x - 1/2 ulp) 和 (x + 1/2 ulp) 之间的十进制字符串在转换为 float 时都将四舍五入为 x -点值。

诀窍在于 (x +/- 1/2 ulp) 不是可表示的 float ,因此实际计算其值需要使用更宽的浮点类型(如果可用)或任意width big decimal 或类似的类型来进行计算。

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

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

这是可行的,因为将 1 加到 x 的位模式恰好对应于将 1 ulp 加到 x 的值。减法时不会出现浮点舍入,因为所涉及的值非常接近(特别是ieee-754浮点运算有一个定理,如果两个数字x和y满足y/2 <= x <= 2y,然后 x - y 被精确计算)。这里唯一的警告是:

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

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

现在您知道 x 的 ulp 是什么,您可以找到舍入到 x 的值的区间。您可以精确地以 float 计算 ulp(x)/2,因为 float 除以 2 是精确的(同样,除非下溢)。然后你只需要计算 x +/- ulp(x)/2 合适的更大浮点类型的值(如果你对 float 感兴趣,double 就可以)或大十进制类型,你有你的间隔。

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


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

In the case of 0.1, there is no lower decimal number that is represented with the same bit pattern

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

关于c# - 查找具有相同内部表示的 float / double 的最小值/最大值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1668183/

相关文章:

c# - 使用restsharp反序列化 - 我尝试了文档,但它不起作用

c# - NU1107 : . Microsoft.EntityFrameworkCore.SqlServer 的 Net Core 2.1 兼容替代品?

c# - 在运行时设置应用栏按钮的图像

c# - 为什么我找不到 RadioButtonFor 方法?

c# - 为什么我使用 UpdateSourceTrigger=PropertyChanged ,TwoWay 还不够?

javascript - Javascript 中的单精度 float 仿真 (float32)

c - 错误的 Visual C float / double 转换?

c# - 继续运行特定数量的任务

c# - 如何刷新绑定(bind)源

c - 为什么 printf("%.6g, <value>) 忽略小数点后的零?