表示任何双值所需的字符的最大长度是多少?

mar*_*tin 49 c c++ floating-point

当我将无符号的8位int转换为字符串时,我知道结果总是最多3个字符(对于255),对于有符号的8位int,我们需要4个字符,例如"-128".

现在我真正想知道的是浮点值是一回事.将任何"double"或"float"值表示为字符串所需的最大字符数是多少?

假设一个常规的C/C++ double(IEEE 754)和正常的十进制扩展(即没有%e printf格式化).

我甚至不确定真正的小数字(即0.234234)是否会比真正庞大的数字(代表整数的双数)更长?

Mik*_*our 34

<float.h>C或<cfloat>C++中的标准头包含几个常量,用于处理浮点类型的范围和其他度量.其中之一是DBL_MAX_10_EXP表示所有double值所需的最大10次幂指数.由于1eN需要N+1数字来表示,并且可能还有负号,所以答案是

int max_digits = DBL_MAX_10_EXP + 2;
Run Code Online (Sandbox Code Playgroud)

这假设指数大于表示最大可能尾数值所需的位数; 否则,还会有一个小数点后跟更多数字.

更正

最长的数字实际上是最小的可表示的负数:它需要足够的数字来覆盖指数和尾数.这个值是-pow(2, DBL_MIN_EXP - DBL_MANT_DIG),其中DBL_MIN_EXP是负数.很容易看到(并通过归纳证明)-pow(2,-N)需要3+N非科学十进制表示"-0."N字符(后跟数字).所以答案是

int max_digits = 3 + DBL_MANT_DIG - DBL_MIN_EXP
Run Code Online (Sandbox Code Playgroud)

对于64位IEEE双核,我们有

DBL_MANT_DIG = 53
DBL_MIN_EXP = -1023
max_digits = 3 + 53 - (-1023) = 1079
Run Code Online (Sandbox Code Playgroud)

  • 你一定是在开玩笑吧... 1079位?你能做一个在stdout中生成该数字的示例C程序吗? (3认同)
  • 这个答案(仍然)是错误的。打印任何十进制“double”值(即“%f”格式)所需的最大字符数将是“-DBL_MIN”的值(即-0x1p-1022,假设binary64 IEEE 754是你的“双重”)。为此,您正好需要 325 个字符。那是:`DBL_DIG + abs(DBL_MIN_10_EXP) + strlen("-0.")`。这当然是因为 `log10(fabs(DBL_MIN))` 是 308,这也是 `abs(DBL_MIN_10_EXP)+1` (+1 是因为小数点左边的前导数字),这就是有效数字左侧前导零的数量。 (3认同)
  • 抱歉,这是错误的 - 最长的数字将是非常小的数字,而不是非常大的数字,并且计算出它们的长度更加复杂。应该可以从 DBL_MIN_EXP 和 DBL_MANT_DIG 计算出来;如果我能解决的话我会更新答案。 (2认同)
  • -1023,因为DBL_MIN_EXP是位而不是数字,因此此处的max_digits计算似乎不正确 (2认同)
  • `printf("%0.320lf\n", -DBL_MIN);` (2认同)

Vit*_*kov 15

根据IEEE 754-1985,由double类型表示的值的最长表示法,即:

-2.2250738585072020E-308

24个字符.

  • op明确表示不是科学记谱法. (8认同)
  • 很酷,你能解释为什么这是最长的吗?为什么不能这样一个非常小的0.033211233457645 ... 234234变得更长? (3认同)
  • 如果你不想要科学记数法会给你多 308 个字符,那么...... (2认同)
  • 由于遵循IEEE 754-1985,双精度数的尾数可以表示点后最大精度为17位的数字。加上尾数和句点,点,电子字符和3位数字(8位)的负号,您将获得准确的24个字符。 (2认同)
  • `0.00000000000000000 ...约308 ... 00002225073858507202` ==>约326个字符:) (2认同)

小智 6

IEEE-754 规范更详细的正确信息来源是加州大学伯克利分校第 4 页的这些讲义,以及一些 DIY 计算。这些讲座幻灯片也适合工程专业的学生。

推荐的缓冲区大小

| Single| Double | Extended | Quad  |
|:-----:|:------:|:--------:|:-----:|
|   16  |  24    |    30    |  45   |
Run Code Online (Sandbox Code Playgroud)

这些数字基于以下计算:

积分部分的最大小数位数

| Single| Double | Extended | Quad  |
|:-----:|:------:|:--------:|:-----:|
|   9   |   17   |    21    |  36   |

* Quantities listed in decimals.
Run Code Online (Sandbox Code Playgroud)

小数计数基于以下公式:至多 Ceiling(1 + N Log_10(2)) 小数,其中 N 是整数部分中的位数*。

最大指数长度

| Single| Double | Extended | Quad  |
|:-----:|:------:|:--------:|:-----:|
|   5   |   5    |     7    |   7   |
* Standard format is `e-123`.
Run Code Online (Sandbox Code Playgroud)

最快的算法

打印浮点数的最快算法是研究论文“快速准确地打印浮点数”中详述的 Grisu2 算法。我能找到的最好的基准可以在这里找到。


pmg*_*pmg 5

您可以用来snprintf()检查您需要多少个字符。 snprintf()返回打印传递给它的任何内容所需的字符数。

/* NOT TESTED */
#include <stdio.h>
#include <stdlib.h>
int main(void) {
    char dummy[1];
    double value = 42.000042; /* or anything else */
    int siz;
    char *representation;
    siz = snprintf(dummy, sizeof dummy, "%f", value);
    printf("exact length needed to represent 'value' "
           "(without the '\\0' terminator) is %d.\n", siz);
    representation = malloc(siz + 1);
    if (representation) {
        sprintf(representation, "%f", value);
        /* use `representation` */
        free(representation);
    } else {
        /* no memory */
    }
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

snprintf()是C99函数。如果 C89 编译器将其作为扩展提供,它可能不会执行上述程序所期望的操作。

编辑:将链接更改为snprintf()实际描述 C99 标准强加的功能的链接;原始链接中的描述是错误的。
2013:将链接更改POSIX 站点,与第一次编辑的站点相比,我更喜欢该站点。