【问题标题】:Precision of floats with printf使用 printf 的浮点数精度
【发布时间】:2012-04-15 07:51:55
【问题描述】:

众所周知,使用printf 输出float 的值时,精度有限。
但是,有一个技巧可以提高输出的准确性,如下例所示:

#include <stdio.h>

int main()
{
    float f = 1318926965;        /* 10 random digits */
    printf("%10.f\n", f);        /* prints only 8 correct digits */
    printf("%10d\n", *(int*)&f); /* prints all digits correctly */
    return 0;
}

我的问题是,为什么人们不经常使用这个技巧?

【问题讨论】:

  • 因为潜在的未定义行为是不好的。 (编辑:从技术上讲,它可能不是未定义的行为,因为我不确定标准对此有何规定。不过,Endianess 可能会咬你。)
  • 4 月 1 日开个玩笑+1!
  • 这显然让我很兴奋
  • 你应该用工会来开一个无可指责的愚人节玩笑。
  • @MrLister 我不是说你的程序可读性太强,我是说它太不确定了。明天查一下“严格的别名规则”。

标签: c floating-point printf precision


【解决方案1】:

愚人节?

您的“随机数”1318926965 具有相同的十进制和浮点形式的底层表示。

尝试其他值,例如10。它将打印为:

        10
1092616192

所以回答你的问题:

and my question is, why don't people use this trick more often?

因为一年中只有一天是愚人节……其余的日子都行不通……

【讨论】:

    【解决方案2】:

    用不同的数字尝试同样的技巧,比如 2318926965。

    #include <stdio.h>
    
    int main()
    {
        float f = 2318926965;        /* 10 random digits */
        printf("%10.f\n", f);        /* prints only 8 correct digits */
        printf("%10d\n", *(int*)&f); /* prints all digits correctly */
        return 0;
    }
    
    $ gcc -Wall -O3  t.c
    t.c: In function ‘main’:
    t.c:7:5: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
    $ ./a.out 
    2318926848
    1326069764
    

    我没有看到你的“技巧”提高了精度,这取决于你平台上浮点数的位表示(据我所知)。

    This thread 有其他一些“神奇的浮点数”以及生成它们的方法。

    【讨论】:

    • 这取决于 IEEE 754 中浮点数的位表示,而不是“在您的平台上”。 (模可能的字节顺序问题在任何现实世界的机器上都不存在。)IMO 对这样的代码唯一不好的是不正确的类型双关语,应该用联合替换(粗略但所有编译器有意支持)或memcpy(100% 合法 C)。
    • 如果 IEEE 754 是强制性的,那么 __STDC_IEC_559__ 定义有什么用?
    • 这不是强制性的,但考虑到 C 标准的破坏程度如何允许不符合 IEEE 的实现的浮点成为,并且没有其他方法可以告诉您在浮点方面可以依赖什么点算术,我认为对于想要移植到非 IEEE 数学系统的程序使用浮点是一个巨大的错误。 (当然,您可以在非便携式程序上使用它,该程序仅用于单个非 IEEE 数学实现。)
    • 感谢您提供有趣的链接!
    【解决方案3】:

    精度的限制是浮点表示,而不是printf(),这是一个错误的前提。

    此外,单精度浮点数只能保证正确到 6 位精度,因此“技巧”会自欺欺人;在一般情况下,它不起作用。

    如果你想要 10 位浮点数,那么你应该使用双精度,这对于 15 位是很好的。

    【讨论】:

      猜你喜欢
      • 2016-09-02
      • 1970-01-01
      • 2019-05-22
      • 2015-03-21
      • 2013-05-26
      • 2020-02-03
      • 1970-01-01
      相关资源
      最近更新 更多