【问题标题】:What Are the Maximum Number of Base-10 Digits in the Fractional Part of a Floating Point Number浮点数小数部分中以 10 为基数的最大位数是多少
【发布时间】:2017-02-11 14:20:52
【问题描述】:

如果可以输出浮点数,从而不会截断值(例如setpercision),并且数字以固定表示法输出(例如fixed),那么缓冲区大小是多少是否需要保证浮点数的整个小数部分都可以存储在缓冲区中?

我希望标准中有一些东西,比如 #definenumeric_limits 中的东西,它会告诉我浮点类型小数部分的最大基数为 10 值的位置。

我在这里询问了浮点类型小数部分中基数为 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 这是我应该寻找的吗?

【问题讨论】:

  • base-10 的最大位数是什么意思? 2^-100 有 100 个以 10 为底的非零数字,在 double 中精确表示。
  • double 的尾数可以存储 53 位,因此它可以有 pow(2,53) = 9E15 个不同的值。所以永远不能超过 log10(9E15) ~= 15.95 有效数字。明智地四舍五入到 15。只需将它们算在你得到的值中。
  • @JonathanMee 因此,您需要编写函数的最大精度,而不是内存中的数字表示。现在问题很清楚了,但很遗憾,我不知道答案
  • 我想你想要(例如double),DBL_MANT_DIG - DBL_MIN_EXP(假设FLT_RADIX2)。对于 IEEE 754 binary64 double,这给出了 1074 的值。对于这种格式,小数点后的1074 数字对于能够表示任何double 的精确值都是必要且足够的。 (5e-324 是最后一个非零有效数字在10^{-1074} 位置的情况。)
  • @JonathanMee 老实说,我不得不在聊天时寻求帮助,其中一个建议是:为了将任何双精度值存储为十进制表示,我的缓冲区应该有多大? 这是你的问题吗?

标签: c++ floating-point max decimal numeric-limits


【解决方案1】:

对于 64 位 IEEE 双精度,精确十进制转换中的最大有效位数为 767。这是具有最小指数值 (1) 和最多小数位设置 (53 )。 (最大次正规值的有效小数位数相同。)

0x1fffffffff: 6.79038653103946484377229843314461138310092194376426254559711066591341199697795428720719286691708030861257706156230052848270284693281999335257284225503333669621306363815173250949032599895939692485035854980886484314557513280150853794570573829826804739857524570119217960803180407426491111965307363413286730767487798931547682783285587237815896874519586247523590053014866896717670220058410681569440570831708335441818365520992706048929416204456554630166566744761505361796609796460970870848607530858252375458051540998088502646723863112078256283270166032158271317445541281132771025125941275958574416739473064262902084753576460564142184397648156338301251133401530253459935315283438205175670237273725515135411912887673125670769439486684770912461317493580281734466552734375E-313

【讨论】:

    【解决方案2】:

    如果考虑 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
    

    因为以 10 为底的数字总是以 5 结尾,所以您可以看到,当指数减 1 时,以 10 为底的数字会增加 1。所以 2^(-N) 需要 N 位数字

    另请注意,在添加这些贡献时,结果位数由最小的数字决定。所以你需要找出可以贡献的最小指数。

    对于 32 位 IEEE 754,您有:

    最小指数-126

    分数位 23

    所以最小的指数是-126 + -23 = -149,所以最小的贡献将来自2^-149,即

    对于以 base-10 打印的 32 位 IEEE 754,可以有 149 个小数位

    对于 64 位 IEEE 754,您有:

    最小指数-1022

    分数位 52

    所以最小的指数是-1022 + -52 = -1074,所以最小的贡献将来自2^-1074,即

    对于以 base-10 打印的 64 位 IEEE 754,可以有 1074 个小数位

    【讨论】:

    • 这是正确的数学运算,但这与标准无关。如果是这样,我们就不必仅对“IEEE 754”浮点进行限定。此外,这适用于 base-2 而不是 base-10。我们需要对其进行转换以确定以 10 为底的缓冲区大小。
    • @JonathanMee - 请参阅2^-1 = 0.52^-2 = 0.25 的计算,RHS 确实是 base-10。所以这是计算以base-10打印时小数部分最大位数的方法。
    • 哦,哇,我不明白第一次通过。这真的很强大。
    【解决方案3】:

    我有理由确定该标准没有(并且不能,除非施加其他限制)提供预定义的常量来指定您要求的数字。

    浮点数最常以基数 2 表示,但基数 16 和基数 10 的使用也相当广泛。

    在所有这些情况下,以 10 为底的唯一因数(2 和可能 5)也是 10 的因数。因此,当从它们转换为以 10 为底(十进制)时,我们永远不会得到无限重复的数字。

    不过,标准并未将浮点限制为此类表示。理论上,如果有人真的想要,他们可以使用(例如)base 3 或 base 7 来表示他们的浮点数。如果他们这样做了,那么存储一个在转换为十进制时会无限重复的数字将是微不足道的。例如以 3 为底的 0.1 代表 1/3,当转换为以 10 为底时会无限重复。虽然我从未听说过有人这样做,但我相信这样的实现可以满足标准的要求。

    对于典型的二进制表示,min_exponent 应该是您想要的值的合理代理。不幸的是,可能不可能比这更准确地陈述事情。

    例如,允许实现以比存储在内存中更高的精度存储中间值,因此(例如)如果您在源代码中按字面意思给出1.0/3.0,则结果实际上可能与通过在运行时读取一对输入产生的值,输入 1 和 3,然后将它们相除。在前一种情况下,除法可能在编译时执行,因此您打印的结果将与double 的大小完全相同,没有额外的。当您在运行时输入这两个值时,将在运行时进行除法,您可能会得到精度更高的结果。

    该标准还要求将浮点的基数记录为std::numeric_limits<T>::radix。基于此,您可以根据基数min_exponent 计算小数点后最大位数的近似值,只要基数的质因数与 10 的质因数共享。

    【讨论】:

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

    您并不是真的想知道“小数部分中有多少位”,此语句表明您并非 100% 清楚浮点表示中的幕后情况。整数和小数部分没有单独的精度。

    您真正想知道的是表示的精度

    1) 一个 32 位的单精度 IEEE754 数字有 24 个尾数位,其精度约为 24 * log10(2) = 7.2 位。

    2) 一个 64 位双精度 IEEE754 数字有 53 个尾数位,大约有 53 * log10(2) = 16.0 位精度。

    假设您使用的是双精度数字。如果你有一个非常小的以 10 为底的数字,比如 0 和 1 之间,那么你将在小数点后有大约 16 个小数位的精度。这就是您的1.0/3.0 示例在上面显示的内容-您知道答案应该是 0.3 重复,但是在答案变成废话之前,小数点后还有十六个三。

    如果你有一个非常大的数字,比如十亿除以三 (1000000000.0/3.0),那么在我的机器上,答案将如下所示:

    1000000000.0/3.0 = 333333333.333333313465118
    

    在这种情况下,您仍然有大约 16 位的精度,但精度分为整数部分和小数部分。整数部分有 9 个精确数字,小数部分有 7 个精确数字。小数部分前八位是垃圾。

    同样,假设我们将 1 quintillion(18 个零)除以 3。在我的机器上:

    1000000000000000000.0/3.0 = 333333333333333312.000000000000000
    

    您仍有 16 位精度,但其中零位在小数点后。

    【讨论】:

    • 我 100% 清楚浮点是如何工作的。我显然不是 100% 确定如何表达我的问题,因为人们一直在告诉我有关独特表示的信息。我正在寻找一个尾数全为 1 的数字,以及最负的指数。我不关心独特的代表。如果您这样做cout << fixed << x,我想知道将要写入的最小值是多少。远小于0后的18位。
    • @JonathanMee 无法回答主要问题,但我可以回答如何获得大于 0 的最小数字:std::nextafter
    • @JonathanMee 固定输出说明符与浮点表示无关。它将打印出程序员在 ios_base::precision - cplusplus.com/reference/ios/ios_base/precision 中指定的尽可能多的小数位 - 这将打印出您要求的尽可能多的小数位,无论它们是否是垃圾。
    • @JonathanMee 最小可表示的非规范化数字是 2^-149,它(大约)是一个小数点,后跟 44 个零,然后是 1。
    • @David:我认为 OP 对表达 @ 的 exact 值(而不是 rough 值)所需的位数感兴趣987654331@ 十进制(这也是 any IEEE 754 binary32 值所需的最大位数)。确切的值是0.00000000000000000000000000000000000000000000140129846432481707092372958328991613128026194187651577175706828388979108268586060148663818836212158203125,点后需要 149 个十进制数字。
    【解决方案5】:

    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 加/减一个软糖因子。但是我们可以在编译时可移植地计算出准确的值吗?此外,我们是否需要最大位数来精确地以十进制表示任何小数值,或者最小的唯一可识别渲染就足够了?
    猜你喜欢
    • 2017-02-08
    • 2021-11-06
    • 2011-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多