查找具有相同内部表示的浮点数/双精度浮点数的最小值/最大值

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's DoubleConverter类可能有所帮助。
2个回答

7

有一种方法来回答你的问题,就是找到浮点数中最后一位的ULP(单位)。简单地说,这是一个给定浮点数和下一个更大的数字之间的距离。再简单地说,对于可表示的浮点值x,任何十进制字符串的值介于(x-1/2 ulp)和(x+1/2 ulp)之间时,当转换为浮点值时会四舍五入为x。

关键在于(x +/- 1/2 ulp)不是可表示的浮点数,因此实际计算其值需要使用更宽的浮点类型(如果有的话)或任意宽度的big decimal或类似类型来执行计算。

如何找到一个ulp的大小?一种相对简单的方法是大致你提出的方法,下面是C风格的伪代码,因为我不知道C#:

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

这是因为将x的位模式加1恰好对应于将x的值加1 ulp。在减法中没有浮点舍入,因为所涉及的值非常接近(特别是,有一个ieee-754浮点算术定理,如果两个数字x和y满足y / 2 <= x <= 2y,则计算x-y)。唯一的注意事项是:
1.如果x恰好是最大的有限浮点数,则无法工作(它将返回inf,这显然是错误的)。 2.如果您的平台不正确支持渐进式下溢(例如运行在flush-to-zero模式下的嵌入式设备),则此方法对于x非常小的值不起作用。
听起来您可能不太可能处于这两种情况中,因此这应该可以满足您的需求。
现在您知道了x的ulp是什么,您可以找到四舍五入为x的值的区间。您可以精确地计算浮点数除以2的ulp(再次排除下溢),因为浮点数除以2是精确的。然后,您只需要计算适当的较大浮点类型(如果您对float感兴趣,则double将起作用)或Big Decimal类型的x +/- ulp(x)/ 2的值,然后您就有了区间。
在本说明中,我做出了一些简化的假设。如果您需要确切地拼写出这一点,请发表评论,我将在有机会时扩展一下模糊的部分。
另外一个注意事项是,您的问题中以下语句是不正确的:
在0.1的情况下,没有表示为相同位模式的较低十进制数字。
您只是看错了值(0.999999...而不是0.099999...-一个容易犯的笔误)。

非常好的答案,看起来就是我在寻找的信息。我会尝试用C#解决问题,如果需要更多细节方面的帮助,我会再回来的。我注意到您曾与IEEE-754团队合作制定标准?我感到很荣幸 :)。而且您说得对,那个错别字真的很明显!我很惊讶自己没有找到更小的值,但我当时认为这是理所当然的,并将其写下来,包括错误,哈哈! - Abel

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接