printf更改输入编号

时间:2014-07-17 13:25:26

标签: macos bash printf

我在OSX Mavericks上,尝试在shell和awk中学习printf命令。我正在玩不同的格式说明符和字段精度值。

我得到了一个非常意外的结果,我真的不明白为什么。

这是我的代码:

#!/bin/bash
a="12345.123456789012345678901"
printf "(%40.30s)\n" $a
printf "(%40.30f)\n" $a
printf "(%40.30e)\n" $a

我原本希望看到这样的事情:

(             12345.123456789012345678901)
(    12345.123456789012345678901000000000)
(    1.234512345678901234567890100000e+04)

但相反,这是我的实际结果:

(             12345.123456789012345678901)
(    12345.123456789011470391415059566498)
(    1.234512345678901147039141505957e+04)

第一行按预期工作,但第二行和第三行显示的数字(分别为%f%e)不是。请注意,前11个小数位已正确显示,但其余的不是。

我使用printf中的awk进行了双重检查,并获得了相同的修改后的数字。 (我不知道这是事实,但根据我的测试,awk printf和shell printf的行为有所不同,这就是我在awk中测试它的原因太。)

我不认为它与舍入有任何关系。我是否无意中触发了一些八进制或十六进制系统?

感谢。

2 个答案:

答案 0 :(得分:3)

17位有效数字超出了64位浮点数的限制(53位有效精度,指数为11位,由IEEE 754双精度二进制浮点格式定义)。换句话说,您的数字有太多的数字,无法准确表示为64位浮点数。小数点左侧或右侧有多少位数无关紧要:位数的总和很重要 - 小数点的位置在内部由指数表示。所以你有 11 + 5位数 - 这是16位正确的数字。更重要的是失去精确度。

答案 1 :(得分:1)

在我的测试中,Zsh和Awk的输出是相同的,但Bash的输出是不同的。我试图做的第一件事是检查这些工具使用哪个库。 Awk和Zsh使用libm.so,但Bash没有。我认为差异在于浮点的实现。

    $ ldd /bin/bash
    linux-vdso.so.1 (0x00007fffb475e000)
    libreadline.so.6 => /lib64/libreadline.so.6 (0x00007fc8666d3000)
    libncurses.so.5 => /lib64/libncurses.so.5 (0x00007fc866474000)
    libdl.so.2 => /lib64/libdl.so.2 (0x00007fc866270000)
    libc.so.6 => /lib64/libc.so.6 (0x00007fc865ec5000)
    /lib64/ld-linux-x86-64.so.2 (0x00007fc86691d000)

另见Double-precision floating-point format