How to print float value from binary file in shell?
Asked Answered
V

5

9

I've binary file consisting double float (8 bytes) or just float (4 bytes) value which was generated in the following way:

$ 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..|.?

which I could print it back from Python via:

$ python -c $'from struct import unpack\nwith open("file.bin", "rb") as f: print(unpack("<d", f.read(8)))'
(0.123,)

The same for 4-byte float, just change <d into <f (mentioned as float.bin later on).

How do I print that value from the shell script using cleaner way (without using Python)? Either using built-in tools (e.g. printf), or wide-used external tools (e.g. xxd, dc, bc, od, hexdump, etc.).


For example to print decimal values, I can use xxd (part of Vim), e.g. in 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
    

However I would like to print original floating value (0.123) on Unix-family system. Ideally using some one-liner (to keep it simple as part of the script), so I can assign it into text variable or print the value on the screen.


I've tried to use printf (on the 4-byte float number to make it simpler), but it didn't work as expected:

$ 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

where according to this on-line converter, 0x3dfbe76d is the same as 0.123, so the hex values are in reverse (what xxd actually gives).

Verniavernice answered 22/4, 2016 at 10:43 Comment(0)
G
17

This doesn't use Python and is a widely-used external tool, Perl.

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

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

Or you can use GNU od utility, e.g.:

od -tfD file.bin
0000000                    0.123
0000010

Where -t parameter specifies the output format for floating-point number (f) followed by optional size specifier (F for float, D for double or L for long double), in short -tfD can be replaced by -e or -F. To print only value without address, -A n can be specified.

Genny answered 22/4, 2016 at 12:53 Comment(1)
+1: Sounds like od (part of coreutils) is even shorter, this works great, thank you. Here is trimmed version: od -tfD file.bin | awk '{print $2}' | xargs.Verniavernice
A
12
od -f <filename>

That will dump your file as floats.

od is a standard Linux tool, and it's what I use. The manpage reads:

od - dump files in octal and other formats

Atalaya answered 11/2, 2018 at 3:39 Comment(1)
od -t fD <filename> for doublesSeljuk
T
1

As a oneliner

echo -n "000000000000f03f" | while read -N2 code; do printf "\x$code"; done | od -t f8
Tobitobiah answered 16/1, 2019 at 17:18 Comment(2)
It should produce: 0000000 1?Verniavernice
Yep 3ff0000000000000 in little endian is the echo body of my answer, and that value is the IEEE754 binary representation of the real value 1.0e0. Its an example for a little endian machine like any x86Tobitobiah
H
1

od -tfD filename would do the job. Check the specific flags following od here: https://www.ibm.com/docs/en/zos/2.4.0?topic=descriptions-od-dump-file-in-specified-format

Hinterland answered 26/4, 2023 at 11:52 Comment(0)
R
-3

You may wish to consider some of the mentioned tools such as bc which can be used in arithmetic operations and comparisons - eg:

#!/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" ;

Additional formatting for related numbers and types such as zero may be required; as addressed in some other postings eg: How to show zero before decimal point in bc?

Ramah answered 22/4, 2016 at 11:29 Comment(1)
Great if you've already in float format, but you're missing conversion from hex format representation. So conversion from 3dfbe76d into 0.123 (in case of 4-byte float).Verniavernice

© 2022 - 2024 — McMap. All rights reserved.