浮点数小数部分的最大十进制位数是多少?

5
如果浮点数可以输出而不截断值(例如使用setpercision),并且以固定表示法输出(例如使用fixed),则需要多大的缓冲区才能保证浮点数的整个小数部分都可以存储在缓冲区中?
我希望标准库中有一些东西,例如#definenumeric_limits中的某些内容,可以告诉我浮点类型的小数部分的最大十进制值位数。
我在这里询问了浮点数类型整数部分的最大基数10位数:What Are the Maximum Number of Base-10 Digits in the Integral Part of a Floating Point Number 但我意识到这可能更加复杂。例如,1.0 / 3.0是一个无限重复的数字序列。当我使用fixed格式输出它时,在重复0之前得到这么多位数:

0.333333333333333314829616256247390992939472198486328125

但我不能确定这是最大精度,因为我不知道这些尾随的0实际上在浮点数的分数中表示了多少,并且它还没有通过负指数向下移动。
我知道我们有min_exponent10,这是我应该寻找的吗?

2
什么是最大的十进制数字位数?2^-100在十进制下有100个非零数字,并且可以精确地表示为double - alexeykuzmin0
1
一个 double 的尾数可以存储53位,因此它可以有pow(2,53) = 9E15个不同的值。因此,它永远不会有超过log10(9E15) ~= 15.95个有效数字。明智地向下舍入为15。只需在您获得的值中计数即可。 - Hans Passant
1
@JonathanMee 所以,您需要编写函数的最大精度,而不是内存中的数字表示。现在问题很清楚了,但不幸的是,我不知道答案。 - alexeykuzmin0
1
我认为你想要的是(例如对于double),DBL_MANT_DIG - DBL_MIN_EXP(假设FLT_RADIX2)。对于IEEE 754二进制64位浮点数,这给出了一个值为1074。对于该格式,小数点后的1074个数字既是必要的,也足以表示任何double的精确值。(5e-324是一个例子,其中最后一个非零有效数字在10^{-1074}位置上。) - Mark Dickinson
1
我认为你要找的是 numeric_limits::min_exponent10 + numeric_limits::digits10,但这个公式可能会有1或2的偏差;我现在没有时间进行全面分析。你可以用 numeric_limits::min 进行测试,它将具有最大数量的前导零和精度数字。numeric_limits::lowest 应该有更多的前导零,但只有一位数字的精度。 - Mark Ransom
显示剩余22条评论
5个回答

6

如果考虑32位和64位IEEE 754数字,可以按照以下描述进行计算。

这都与2的负次幂有关。因此,让我们看看每个指数如何贡献:

2^-1 = 0.5         i.e. 1 digit
2^-2 = 0.25        i.e. 2 digits
2^-3 = 0.125       i.e. 3 digits
2^-4 = 0.0625      i.e. 4 digits
....
2^-N = 0.0000..    i.e. N digits

由于十进制数字总是以5结尾,因此当指数减1时,十进制位数会增加1。所以2^(-N)将需要N位数字。
还要注意的是,当添加这些贡献时,得到的数字位数由最小的数字确定。因此,您需要找出可以提供贡献的最小指数。
对于32位IEEE 754,最小指数为-126,分数位23。因此,最小指数为-126 + -23 = -149,因此最小贡献将来自2 ^ -149,即:
对于以10为底打印的32位IEEE 754,可以有149个小数位数字。
对于64位IEEE 754,最小指数为-1022,分数位52。因此,最小指数为-1022 + -52 = -1074,因此最小贡献将来自2 ^ -1074,即:
对于以10为底打印的64位IEEE 754,可以有1074个小数位数字。

这是正确的数学方法,但并不是标准依赖型的。如果是的话,我们就不需要限定仅适用于“IEEE 754”浮点数。另外,这是在基于2进制的环境下工作,而不是在基于10进制的环境下工作。因此,如果我们想要确定基于10进制的缓冲区大小,就需要进行转换。 - Jonathan Mee
@JonathanMee - 请看 2^-1 = 0.52^-2 = 0.25 的计算,右侧确实是十进制。因此这是计算在十进制下打印时小数部分的最大位数的方法。 - Support Ukraine
哦,哇,第一遍我没理解。那真的很强大。 - Jonathan Mee

2
我相信标准(如果不加其他限制)没有提供预定义的常量来指定您要请求的数字。
浮点数通常以基数2表示,但基数16和基数10也被广泛使用。
在所有这些情况下,基数(2和可能是5)中的唯一因素也是10的因数。因此,在从它们转换为基数10(十进制)时,我们永远不会得到无限重复的数字。
标准并不限制浮点数使用这样的表示法。理论上,如果有人真的想要,他们可以使用(例如)基数3或基数7来表示他们的浮点数。如果他们这样做,存储一个在转换为十进制时会无限重复的数字将是微不足道的。例如,在基数3中,0.1表示1/3,当转换为基数10时会无限重复。虽然我从未听说过有人这样做,但我认为这样的实现可以满足标准的要求。
对于典型的二进制表示,min_exponent应该是您想要的值的合理代理。不幸的是,可能无法比这更精确地陈述。
例如,实现允许将中间值存储到比内存中存储的精度更高的精度,因此,如果您在源代码中直接给出1.0/3.0,结果实际上可能与在运行时读取一对输入(输入1和3,然后将它们除以)产生的值不同。在前一种情况下,除法可能在编译时进行,因此您打印出的结果将恰好是double的大小,没有额外的内容。当您在运行时输入两个值时,除法将在运行时进行,并且您可能会获得更高精度的结果。
标准还要求浮点数的基数文档化为std::numeric_limits::radix。基于这个,您可以计算出基于radix^min_exponent的小数点后最大位数的近似值,只要基数的质因数与10的质因数相同即可。

2
如果我正确理解了OP的问题,对于典型的二进制表示(IEEE 754),min_exponent10不是所需值的良好代理,但min_exponent是。这是因为需要在点后面写出2 ** min_exponent的完整值需要min_exponent个十进制数字。(您可以通过查看前几个负幂次方2来证明这一点:0.5,0.25,0.125,0.0625等:每次都需要一个额外的数字。)对于IEEE 754 binary64格式,所需点后数字的最大数量正好为1074 - Mark Dickinson
@MarkDickinson 我不确定 OP 是在问最大的有效数字还是最大的可能非零数字,但你应该将你的评论扩展为自己的答案。如果它被接受了,那么我们就会知道。 - Mark Ransom

2

对于64位IEEE双精度,一个精确的十进制转换中最多有767个有效数字。这是指具有最小指数值(1)和设置了最多分数位(53)的值的精确十进制表示形式。 (最大次正常值具有相同数量的有效十进制数字。)

0x1fffffffff: 6.79038653103946484377229843314461138310092194376426254559711066591341199697795428720719286691708030861257706156230052848270284693281999335257284225503333669621306363815173250949032599895939692485035854980886484314557513280150853794570573829826804739857524570119217960803180407426491111965307363413286730767487798931547682783285587237815896874519586247523590053014866896717670220058410681569440570831708335441818365520992706048929416204456554630166566744761505361796609796460970870848607530858252375458051540998088502646723863112078256283270166032158271317445541281132771025125941275958574416739473064262902084753576460564142184397648156338301251133401530253459935315283438205175670237273725515135411912887673125670769439486684770912461317493580281734466552734375E-313


2

这句话"小数部分有多少位数字"并不能真正告诉你浮点表示法的内部运作方式。实际上,整数和小数部分没有单独的精度。

你真正想知道的是表示法的精度

1)32位单精度IEEE754数字有24个尾数位,大约可以提供24 * log10(2) = 7.2位数字的精度。

2)64位双精度IEEE754数字有53个尾数位,大约可以提供53 * log10(2) = 16.0位数字的精度。

假设你正在使用双精度数。如果你有一个非常小的十进制数,比如在0到1之间,那么在小数点后面你将有大约16个十进制数字的精度。这就是你上面示例中的1.0/3.0——你知道答案应该是无限循环的0.3,但在答案变成无意义之前,你会看到十六个3。

如果你有一个非常大的数字,比如十亿除以三(1000000000.0/3.0),那么在我的机器上答案看起来会像这样:

1000000000.0/3.0 = 333333333.333333313465118

在这种情况下,您仍然具有约16位数字的精度,但是精度分散在整数部分和小数部分。整数部分有9个精确数字,小数部分有7个精确数字。小数部分中第八个数字以后是无用的垃圾。
同样地,假设我们将一万亿(18个零)除以三,在我的机器上:
1000000000000000000.0/3.0 = 333333333333333312.000000000000000

您仍然拥有16位数字的精度,但这些数字中没有任何一位在小数点后面。

我完全清楚浮点数是如何工作的。但我并不确定如何表达我的问题,因为人们总是告诉我关于唯一表示法的事情。我正在寻找一个尾数中所有数字都是1且指数最小的数字。我不关心唯一表示法。如果你使用 cout << fixed << x,我想知道将被写入的最小值位置是什么。它比0后面18个位置要小得多。 - Jonathan Mee
1
@JonathanMee 无法回答主要问题,但我可以回答如何获得大于0的最小数字:std::nextafter - milleniumbug
1
@JonathanMee 固定的输出格式说明符与浮点表示无关。它将按照程序员在ios_base :: precision中指定的数量打印出十进制位数-http://www.cplusplus.com/reference/ios/ios_base/precision/ - 这将打印出您要求的所有小数位,无论它们是垃圾还是不是。 - David
1
@JonathanMee 最小可表示的非规格化数是2^-149,大约是一个小数点后面跟着44个零,然后是1。 - David
4
@David:我认为OP对于以十进制表示2^{-149}精确值(而不是粗略值)所需的位数感兴趣(这也是表示任何IEEE 754二进制32位值所需的最大位数)。该精确值为0.00000000000000000000000000000000000000000000140129846432481707092372958328991613128026194187651577175706828388979108268586060148663818836212158203125,小数点后需要149个数字。 - Mark Dickinson

0

std::numeric_limits<double>::min_exponent

最小负整数值,使得基数的(min_exponent-1)次方生成一个规范化的浮点数。 对于浮点类型,等同于FLT_MIN_EXP、DBL_MIN_EXP或LDBL_MIN_EXP。

min_exponent10也可用。

最小负整数值,使得10的该幂次方生成一个规范化的浮点数。 对于浮点类型,等同于FLT_MIN_10_EXP、DBL_MIN_10_EXP或LDBL_MIN_10_EXP。


换句话说,如果浮点格式支持非规格化数,则FLT_MIN_EXP为最小指数,如果不支持,则为FLT_MIN_EXP + FLT_DIG加/减一个调整因子。但是我们能否在编译时可移植地计算出精确值?此外,我们需要用十进制表示法表示任何分数值的最大位数,还是最小唯一可识别渲染就足够了? - doynax

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