14

How to do arithmetic with floating point numbers such as 1.503923 in a shell script? The floating point numbers are pulled from a file as a string. The format of the file is as follows:

1.5493482,3.49384,33.284732,23.043852,2.2384...
3.384,3.282342,23.043852,2.23284,8.39283...
.
.
.

Here is some simplified sample code I need to get working. Everything works fine up to the arithmetic. I pull a line from the file, then pull multiple values from that line. I think this would cut down on search processing time as these files are huge.

# set vars, loops etc.

while [ $line_no -gt 0 ]
do
    line_string=`sed -n $line_no'p' $file_path`  # Pull Line (str) from a file
    string1=${line_string:9:6}                   # Pull value from the Line
    string2=${line_string:16:6}
    string3=...
    .
    .
    .
    calc1= `expr $string2 - $string7` |bc -l     # I tried these and various
    calc2= ` "$string3" * "$string2" ` |bc -l    # other combinations
    calc3= `expr $string2 - $string1`
    calc4= "$string2 + $string8" |bc
    .
    .
    .
    generic_function_call                        # Use the variables in functions
    line_no=`expr $line_no - 1`                  # Counter--
done

Output I keep getting:

expr: non-numeric argument
command not found
Palec
  • 12,743
  • 8
  • 69
  • 138
Dave Engineer
  • 386
  • 1
  • 3
  • 12
  • use the shell debugging/trace options (`set -vx`) to see what values are being processed, then you can adjust your string parsing to just retrieve the number parts. This is just for a learning exercize, right? It is much more efficient and easier to understand this sort of processing in `awk`. Good luck. – shellter Jan 08 '13 at 19:15
  • Same question on [Unix.SE]: [Perform floating point arithmetic in shell script variable definitions](http://unix.stackexchange.com/q/76060/50602) – Palec Feb 10 '15 at 22:53
  • Related: [How to compare two floating point numbers in a bash script?](http://stackoverflow.com/q/8654051/2157640) – Palec Feb 10 '15 at 23:40
  • 1
    Related: [How to do integer & float calculations, in bash or other languages/frameworks?](http://unix.stackexchange.com/q/40786/21471) at Unix.SE – kenorb Jan 30 '16 at 13:33

4 Answers4

15

I believe you should use : bc

For example:

echo "scale = 10; 123.456789/345.345345" | bc

(It's the unix way: each tool specializes to do well what they are supposed to do, and they all work together to do great things. don't emulate a great tool with another, make them work together.)

Output:

.3574879198

Or with a scale of 1 instead of 10:

echo "scale = 1; 123.456789/345.345345" | bc

Output:

.3

Note that this does not perform rounding.

I highly recommand switching to awk if you need to do more complex operations, or perl for the most complex ones.

ex: your operations done with awk:

# create the test file:
printf '1.5493482,3.49384,33.284732,23.043852,2.2384,12.1,13.4,...\n' > somefile
printf '3.384,3.282342,23.043852,2.23284,8.39283,14.1,15.2,...\n'    >> somefile

# do OP's calculations (and DEBUG print them out!)

awk -F',' '
   # put no single quote in here... even in comments! you can instead print a: \047 
   # the -F tell awk to use "," as a separator. Thus awk will automatically split lines for us using it. 
   # $1=before first ","  $2=between 1st and 2nd ","  ... etc.
    function some_awk_function_here_if_you_want() {  # optionnal function definition
         # some actions here. you can even have arguments to the function, etc.
         print "DEBUG: no action defined in some_awk_function_here_if_you_want yet ..."
    }
    
    BEGIN      {  rem="Optionnal START section. here you can put initialisations, that happens before the FIRST file-s FIRST line is read"
    }
    
    (NF>=8)    {  rem="for each line with at least 8 values separated by commas (and only for lines meeting that condition)"
                  calc1=($2 - $7)
                  calc2=($3 * $2)
                  calc3=($2 - $1)
                  calc4=($2 + $8)
                  # uncomment to call this function :(ex1): #  some_awk_function_here_if_you_want
                  # uncomment to call this script:(ex2): # cmd="/path/to/some/script.sh \"" calc1 "\" \"" calc2 "\" ..." ; rem="continued next line"
                  # uncomment to call this script:(ex2): # system(cmd); close(cmd) 
                  line_no=(FNR-1) # ? why -1? .  FNR=line number in the CURRENT file.   NR=line number since the beginning (NR>FNR after the first file ...)
                  print "DEBUG: calc1=" calc1 " , calc2=" calc2 " , calc3=" calc3 " , calc4=" calc4 " , line_no=" line_no
                  print "DEBUG fancier_exemples: see man printf for lots of info on formatting (%...f for floats, %...d for integer, %...s for strings, etc)"
                  printf("DEBUG: calc1=%d , calc2=%10.2f , calc3=%s , calc4=%d , line_no=%d\n",calc1, calc2, calc3, calc4, line_no)
    }

    END        {  rem="Optionnal END section. here you can put things that need to happen AFTER the LAST file-s LAST line is read"
    }
      
'  somefile # end of the awk script, and the list of file(s) to be read by it.
Olivier Dulac
  • 3,695
  • 16
  • 31
  • Could you please expand a little on "scale" Ive done a bit of searching and man page for it etc. I'm sure its a powerful tool but cant seem to get my head round it and how it would be good for the code. Any helpful links would be just as good. Thanks – Dave Engineer Jan 08 '13 at 19:34
  • The tool here is bc, taking commands from stdin. Scale is à command used inside bc. See [more info on gnu' bc page here](http://www.gnu.org/software/bc/manual/html_mono/bc.html) – Olivier Dulac Jan 08 '13 at 20:58
  • @DaveEngineer I added at the end a demo of awk usage that will be both more powerful and easier to use than bash calculations (awk will auto-split, can do complex function calls, can invoke shell scripts or commands, have sane defaults, etc) – Olivier Dulac Oct 07 '20 at 12:31
10

What about this?

calc=$(echo "$String2 + $String8"|bc)

This will make bc to add the values of $String2 and $String8 and saves the result in the variable calc.

jgr
  • 3,914
  • 2
  • 24
  • 28
10

If you don't have the "bc" you can just use 'awk' :

calc=$(echo 2.3 4.6 | awk '{ printf "%f", $1 + $2 }')
igreenfield
  • 1,618
  • 19
  • 36
2

scale in bc is the precission so with a scale of 4 if you type bc <<< 'scale=4;22.0/7' you get 3.1428 as an answer. If you use a scale of 8 you get 3.14285714 which is 8 numbers after the floating point. So the scale is a precission factor

  • bc = bench calculator? from http://en.wikipedia.org/wiki/Bc_(programming_language) –  Sep 09 '13 at 22:49