二进制浮点数可以表示哪些类型的数字?

28

我已经阅读了很多与浮点数相关的内容,但它们都过于复杂。我认为我已经基本理解了,但是还有一件事我想确定:

我知道形如 1/pow(2,n) 的分数可以在浮点数中精确表示,其中 n 是整数。这意味着如果我将 1/32 加上自身 3200 万次,那么结果将精确等于 1,000,000

那么像 1/(32+16) 这样的表达式呢?它是两个二的幂次方之和的倒数,它能行吗?或者是 1/32+1/16 行吗?这就是我困惑的地方,如果有人能为我澄清一下,我将不胜感激。

4个回答

35
这个规则可以总结如下:
  • 如果分母的质因数分解只包含2(即分母是2的幂),那么一个数在二进制中可以被准确表示。
所以,1/(32 + 16)在二进制中无法表示,因为分母中有一个因子3。但是1/32 + 1/16 = 3/32可以表示。
话虽如此,在浮点数类型中还有更多的限制。例如,在IEEE double中,你只有53位的尾数,所以1/2 + 1/2^500无法表示。
因此,只要指数的范围不超过53个幂,你可以进行幂的求和。

将这个推广到其他进制:

  • 如果分母的质因数分解只包含2和5,那么一个数在十进制中可以被精确表示。

  • 如果有理数X的分母的质因数分解只包含在进制N的质因数分解中出现的质数,那么X可以在进制N中被精确表示。


5
如果我理解正确的话,只要Y是2的幂次方且X是小于2的53次方的数字,我就可以使用任何数字X/Y吗? - Niet the Dark Absol
@Mysticial:答案赞一个,但我有一个疑问。24/48 = 0.5,但是按照上述规则,它不应该表示,因为3是48的质因子之一,而不是10的质因子之一。为什么? - legends2k
3
@legends2k 24/48是可以约分的。在尝试应用上述规则之前,您应该先将分数约简。 - Niet the Dark Absol
@NiettheDarkAbsol:啊,我明白了,这个规则只有在数字是互质整数 [gcd(X, Y) = 1] 的情况下才能起作用。 - legends2k

10
只有当一个有限的数字等于M•2的e次方,且M和e为整数,且满足-2的53次方小于M小于2的53次方,-1074小于等于e小于等于971时,它才可以使用普通的IEEE 754双精度格式表示。
对于单精度,-2的24次方小于M小于2的24次方,-149小于等于e小于等于104。
对于双精度,这些结果是由以下事实导致的:双精度格式使用52位来存储尾数(由于隐含的1通常具有53位),并使用11位来存储指数。11位编码的数字范围从0到2047,但为了特殊目的而排除了0和2047,并且编码的数字被偏置1023,因此它表示不偏倚的指数从-1022到1023。但是,这些不偏倚的指数适用于区间[1,2)中的尾数,而这些尾数具有小数部分。为了将尾数表示为整数,我通过52调整了指数范围。 单精度也类似,使用23位来存储24位尾数,8位用于指数,偏置为127。
使用整数乘以二的幂次方来表示可表示的数字,而不是更常见的分数尾数,可以简化一些关于浮点属性的数论和其他推理。我在此答案中使用它,因为它允许简洁地表示可表示值的集合。

看,这正是我在问题中提到的“过于深入参与”的例子... - Niet the Dark Absol
2
@Kolink:答案本身是一个单句,准确地说明了哪些数字可以和不能用整数、乘法、幂以及小于等于这些熟悉的概念来表示。比这还要简单吗?你有一个整数乘以二的幂,而且这个整数和幂必须在某些范围内。答案的其余部分只是关于这个句子来自哪里的解释。 - Eric Postpischil

4
浮点数使用以下形式进行字面表示:
1.m * 2^e

其中1.m是一个二进制小数,e是一个正整数或负整数。

因此,您可以精确地表示1/32 + 1/16,如下:

1.1000000 * 2^-4

(1.10是二进制分数等于1.5。)1/48在这种格式下无法表示。


(我认为您的意思是 1.m * 2^e。) - huon
那不应该是 1.1000000 * 2^-4 吗? - mkeiser

0

还有一点尚未提及的是,从语义上讲,浮点数可能最好被视为表示一系列值的范围。这个值的范围有一个非常精确定义的中心点,IEEE规范通常要求浮点计算的结果是包含原始数字中心点操作得到的点的范围内的数字,但顺序如下:

  double N1 = 0.1;
  float  N2 = (float)N1;
  double N3 = N2;

N2是正确的单精度表示,它明确地表示了在N1中所表示的值,尽管该语言要求使用显式转换。N3将表示N2可以表示的值之一(语言规范恰好选择了其范围以float的范围中间为中心的double值)。请注意,虽然N2表示其类型的值包含正确的值,但N3却不是。

顺便提一下,在 .net 和 .net 语言中将字符串转换为浮点数时,似乎会经过一个中间转换到 double 的步骤,这可能有时会改变值。例如,即使值 13571357 可以表示为单精度浮点数,但值 13571357.499999999069f 会被四舍五入为 13571358(尽管它显然更接近于 13571357)。


“语言的愚蠢要求使用显式转换”:你说的是哪种语言?在C语言中这里不需要强制转换... - glglgl
@glglgl:引用的示例代码在C、Java或C#中都是有效的;后两种语言都需要将其转换为“float”,但不需要转换为“double”。 - supercat
好的,谢谢。评论2不再适用了,我考虑了一下字符串->双精度浮点数->单精度浮点数的转换,你这里似乎是对的。 - glglgl
@glglgl:自我上述内容以来,我已经决定,如果允许的隐式转换是从doublefloat而不是不幸允许的floatdouble,那么将float转换为double会是一件好事。 其中一个原因是,如果允许隐式转换long->Decimal->double->float,则可以指定,在内置类型中,从T到U到V的任何隐式转换序列都等同于从T直接到V的隐式转换。 允许从floatdouble的隐式转换会破坏这一点。 - supercat

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