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

Abe*_*bel 7 .net c# floating-point bit-manipulation ieee-754

刷新浮点(也是PDF),IEEE-754并参与有关转换为字符串时浮点舍入的讨论,让我修补一下:如何获得给定浮点数的二进制表示的最大值和最小值是平等的.

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

背景:计算机不能0.1用二进制表示来表示.在C#中,float表示3DCCCCCD内部(C#使用round-to-nearest)和double表示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));
Run Code Online (Sandbox Code Playgroud)

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

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

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

我的猜测是这样的:取尾数,移除其余部分,得到其精确值,得到一个(尾数位)更高,并计算平均值:低于该值的任何东西将产生相同的位模式.我的主要问题是:如何将小数部分作为整数(位操纵它不是我最强的资产).Jon Skeet的DoubleConverter课程可能会有所帮助.

Ste*_*non 6

解决问题的一种方法是找到你的浮点数中的ULP大小,或者在L ast P花边中的U nit .简化一点,这是给定浮点数与下一个更大数之间的距离.再次,简化一点,给定一个可表示的浮点值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);
Run Code Online (Sandbox Code Playgroud)

这是有效的,因为在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)或大十进制类型,并且你有你的间隔.

我通过这个解释做了一些简化的假设.如果你需要这个确实拼写出来,请留下评论,当我有机会时,我会扩展有点模糊的部分.


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

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

是不正确的.你刚才正好在看在错误的值(0.999999 ...而不是0.099999 ... - 一个简单的错字进行).