bashsedfloating-pointprecisionfloating-accuracy

Format floating-point numbers with sed to equal precision - add trailing zeros


Is it possible to format floating point numbers with sed to extend to equal precision of e.g. 8 digits after the decimal point? Because this problem is part of a bigger context, it is essential to use GNU sed

Input Examples:

0
126
99.0234
.38
-47.88
-234.23001101
40565004.22

The goal is to append trailing 0's to each number until the desired precision is reached. It is sure that no number of the input has more digits after the decimal point as the desired precision. Another (minor) goal is to add a leading zero before the decimal point, if it is missing.

Just the same as bash LC_NUMERIC=en_US.UTF-8 printf "%0.8f" .123 works.

Achieved output is:

0.00000000
126.00000000
99.02340000
0.38000000
-47.88000000
-234.23001101
40565004.22000000

All I found so far is only about limiting the number digits, but not expanding.


An alternative solution would be a way to call the shell command LC_NUMERIC=en_US.UTF-8 printf "%0.8f\n" \3 for a particular match group (here: the third match group) within the sed processing.

Input: <rowNo>|<date time>|<number> (example: 1|2024-02-01 00:27:16|.38, numbers as above)

command (obviously not working, for third match group processing):

sed 's/^\([0-9]*\)|\(.*\)|\([0-9\.]*\)/\1|\2|$(LC_NUMERIC=en_US.UTF-8 printf "%.8f\n" \3))/g' test.csv

Updates in reply to some comments: It is not the point to have exact floating point representations. The numbers in my input have been printed out by a program which is out of my control as (up-to) 8 digit string representations. I added tag 'bash' because I thought it might be helpful.


Solution

  • This sed command might be what you are looking for:

    sed 's/^\(-\{0,1\}\)[.]/\10./
         /[.]/!s/$/./
         s/$/00000000/
         s/\([.].\{8\}\).*/\1/
    ' input
    

    If the input lines are in the form of <rowNo>|<date time>|<number>, then

    sed 'h
         s/.*|//
         s/^\(-\{0,1\}\)[.]/\10./
         /[.]/!s/$/./
         s/$/00000000/
         s/\([.].\{8\}\).*/\1/
         x
         s/[^|]*$//
         G
         s/\n//
    ' input