如何在Shell中从二进制文件中打印浮点值?

9

我有一个二进制文件,其中包含双精度浮点数(8个字节)或单精度浮点数(4个字节)的值,该文件是以下方式生成的:

$ python -c $'from struct import pack\nwith open("file.bin", "wb") as f: f.write(pack("<d", 0.123))'
$ xxd file.bin
00000000: b072 6891 ed7c bf3f                      .rh..|.?

我可以通过Python打印回来:
$ python -c $'from struct import unpack\nwith open("file.bin", "rb") as f: print(unpack("<d", f.read(8)))'
(0.123,)

对于4字节浮点数,只需将<d更改为<f(稍后称为float.bin)。
如何以更清晰的方式从shell脚本中打印该值(不使用Python)?可以使用内置工具(例如printf)或广泛使用的外部工具(例如xxddcbcodhexdump等)。
例如,要打印十进制值,我可以使用xxd(Vim的一部分),例如在Bash中:
  • get the value of first byte:

    $ echo $((16#$(xxd -ps -s0 -l1 file.bin)))
    176
    

    For 2nd and forth bytes, increase -s.

  • get decimal value from all 8 bytes:

    $ echo $((16#$(xxd -ps -s0 -l8 file.bin)))
    -5732404399725297857
    

然而,我希望在Unix系统上打印原始浮点值(0.123)。最好使用一些单行代码(作为脚本的一部分保持简单),这样我可以将其分配到文本变量中或将该值打印在屏幕上。


我尝试使用printf(对4字节浮点数进行简化),但结果与预期不符:

$ xxd -p float.bin
6de7fb3d
$ printf "%.4f\n" 0x.$(xxd -p float.bin)
0.4293
$ printf "%.4f\n" 0x3dfbe76d
1039918957.0000
$ printf "%.4e\n" 0x3dfbe76d
1.0399e+09

根据此 在线转换器0x3dfbe76d 等同于 0.123,所以十六进制值是反向的(实际上是由 xxd 给出的)。
5个回答

17

这不是使用 Python 编写的,而是一个广泛使用的外部工具,Perl

perl -e "print pack('d>',0.123)" > file.bin

perl -e "print unpack('d>',<>)" < file.bin
0.123

你也可以使用GNU od 工具,例如:

od -tfD file.bin
0000000                    0.123
0000010

-t参数指定浮点数的输出格式(f),后面可以跟可选的大小规格符(F表示float,D表示double或L表示long double),简而言之,-tfD可以被-e-F替换。要仅打印值而不带地址,可以指定-A n


+1:听起来像是coreutils的一部分od更短,这个方法效果很好,谢谢。这是精简版本:od -tfD file.bin | awk '{print $2}' | xargs - kenorb

12
od -f <filename>

这将以浮点数的形式转储您的文件。

od是一个标准的Linux工具,我经常使用它。手册上写道:

od - 以八进制和其他格式转储文件


od -t fD <filename> 用于双精度浮点数。 - Matthias Beaupère

1
作为一行代码。
echo -n "000000000000f03f" | while read -N2 code; do printf "\x$code"; done | od -t f8

它应该产生:0000000 1 - kenorb
1
是的,3ff0000000000000在小端模式下是我的答案的回显体,该值是实际值1.0e0的IEEE754二进制表示。这是一个小端机器(如任何x86)的示例。 - debuti

1

-3

您可能希望考虑一些提到的工具,例如bc,它可以用于算术运算和比较 - 例如:

#!/bin/bash
A=1.3141592653581 # variable 1
B=1.3141592653589 # variable 2
if (( `echo $A"<"$B | bc` )) ; then printf "$A is: < $B\n" ; else printf "$B is: < $A\n" ; fi ;
C=$(echo "$A+$B" | bc)
printf "C == $C\n" ;

对于相关数字和类型(如零),可能需要进行额外的格式化,正如其他帖子中所提到的: 如何在bc中显示小数点前的零?


如果您已经使用浮点格式,那就太好了,但是您需要将十六进制格式转换为浮点格式。因此,在4字节浮点数的情况下,需要将3dfbe76d转换为0.123 - kenorb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接