BC arithmetic error

I am trying to solve this bash script that reads an arithmetic expression from the user and displays it on the output screen with a rounding to 3 decimal places at the end.

sample input

5+50*3/20 + (19*2)/7 

sample output

 17.929 

my code

 read x echo "scale = 3; $x" | bc -l 

when there is an entrance

 5+50*3/20 + (19*2)/7 

** my conclusion **

 17.928 

which car wants her to be

 17.929 

and because of this I am wrong. any idea?

+6
source share
3 answers

The key here is to use printf with the format specification "% .3f", and printf will take care of rounding as you wish, while "scale = 4" for bc.

Here is a script that works:

 echo -e "please enter math to calculate: \c" read x printf "%.3f\n" $(echo "scale=4;$x" | bc -l) 

You can understand what happens with the above solution if you run this command on the command line: echo "scale=4;5+50*3/20 + (19*2)/7" | bc echo "scale=4;5+50*3/20 + (19*2)/7" | bc result will be equal to 17.9285. When this result is provided to printf as an argument, the function takes into account the fourth decimal place and rounds the value, so that the formatted result is displayed with an accuracy of three decimal places and with a value of 17.929.

Alternatively, this also works without a channel, redirecting the document here as an input to bc, as shown below, which avoids creating a sub-shell:

 echo -e "please enter math to calculate: \c" read x printf "%.3f\n" $(bc -l <<< "scale=4;$x") 
+3
source

You do not round a number; you crop it.

 $ echo "5+50*3/20 + (19*2)/7" | bc -l 17.92857142857142857142 $ echo "scale = 3; 5+50*3/20 + (19*2)/7" | bc -l 17.928 

The only way I know to round a number is to use awk :

 $ awk 'BEGIN { rounded = sprintf("%.3f", 5+50*3/20 + (19*2)/7); print rounded }' 17.929 

So in your example:

 read x awk 'BEGIN { rounded = sprintf("%.3f", $x; print rounded }' 
+3
source

I completely agree with jherran that you are not rounding the number, you are trimming it. I would say that the scale probably just does not behave the way you want it, perhaps the way no one wants it to behave.

 > x="5+50*3/20 + (19*2)/7" > echo "$x" | bc -l 17.92857142857142857142 > echo "scale = 3; $x" | bc -l 17.928 

In addition, due to scale behavior , you round each multiplication / division separately from additions. Let me prove my point with a few examples:

 > echo "scale=0; 5/2" | bc -l 2 > echo "scale=0; 5/2 + 7/2" | bc -l 5 > echo "5/2 + 7/2" | bc -l 6.00000000000000000000 

However, the scale without any operation also does not work. There is an ugly job:

 > echo "scale=0; 5.5" | bc -l 5.5 > echo "scale=0; 5.5/1" | bc -l 5 

So things come out of it.

  • If you want to use the bc scale, do it only for the end result already calculated, and even then beware.

  • Remember that rounding is the same as truncating a number + half the precision you want.

Let's take an example of rounding to the nearest integer, if you add .5 to the number to be rounded, its integer part will take the next integer value, and truncation will give the desired result. If this number is to be rounded, then adding .5 will not change its integer value, and truncating will give the same result as when nothing was added.

So my solution follows:

 > y=$(echo "$x" | bc -l) > echo "scale=3; ($y+0.0005)/1" | bc -l # scale doesn't apply to the +, so we get the expected result 17.929 

Again, note that the following does not work (as explained above), so it is very important to break it into two operations:

 > echo "scale=3; ($x+0.0005)/1" | bc -l 17.928 
+1
source

Source: https://habr.com/ru/post/979239/


All Articles