我在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
中测试它的原因太。)
我不认为它与舍入有任何关系。我是否无意中触发了一些八进制或十六进制系统?
感谢。
答案 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)