4

When I'm performing float operations in shell using bc, the result is not the same if I use a regular calculator. Am I doing something wrong?

For example, I need to find a volume of a sphere. User inputs the radius value.

pi = 3.14

volume=$(echo "scale = 2; (4 / 3) * $pi * ($r ^ 3)" | bc)
echo "Volume is $volume"

If radius = 3, unix returns 112.59, and the calculator 113.1.

Slado
  • 43

2 Answers2

14

You need to understand the meaning of the scale of an expression in bc. bc can do arbitrary precision (which doesn't necessarily mean infinite precision) while your calculator will probably have the precision of the float or double data type of your processor.

In bc. The scale is the number of decimal after the dot, so related to the precision. The scale of an expression is determined based on rules that depend on which operator is involved and the scale variable (that variable is the one that gives the arbitrary dimension of the precision of bc that is, that can make its precision as big as you want).

For instance, the scale of the result of a division is scale. So 4/3 when scale is 2 is 1.33, so a very rough approximation of 4/3. The scale of x * y will be min(a+b,max(scale,a,b)) (where a is the scale of x and b the scale of y), so here 2. so 1.33 * 3.14 will be 4.17.

For the rules, you can check the POSIX specification for bc.

If you want a greater precision, increase scale. You can increase it indefinitely. With bc -l, scale is automatically set to 20.

$ pi='(a(1)*4)' r=3
$ $ echo "(4 / 3) * $pi * ($r ^ 3)" | bc -l
113.09733552923255658339

$ echo "scale=1000; (4 / 3) * $pi * ($r ^ 3)" | bc -l
113.0973355292325565846551617980621038310980983775038095550980053230\
81390626303523950609253712316214447357331114478163039295378405943820\
96034211293869262532022821022769726978675980014720642616237749375071\
94371951239736040606251233364163241939497632687292433484092445725499\
76355759335682169861368969085854085132237827361174295734753154661853\
14730175311724413325296040789909975753679476982929026989441793959006\
17331673453103113187002257495740245517842677306806456786589844246678\
87098096084205774588430168674012241047863639151096770218070228090538\
86527847499397329973941181834655436308584829346483609858475202045257\
72294881898002877683392804259302509384339728638724440983234852757850\
73357828522068813321247512718420036644790591105239053753290671891767\
15857867345960859999994142720979823815034238137946746942088054039248\
86988951308030971204086612694295227741563601129621951039171511955017\
31142218396089302929537125655435196874321744263099764736353375070480\
1468800991581641650380680694035580030527317911271523

$ echo "scale=1; (4 / 3) * $pi * ($r ^ 3)" | bc -l
97.2

You can also do all your calculations with a high scale, and reduce it in the end for display:

$ echo "scale=10; (4 / 3) * $pi * ($r ^ 3)" | bc -l
113.0973355107
$ echo "scale=100; x = (4 / 3) * $pi * ($r ^ 3); scale = 10; x / 1" | bc -l
113.0973355292
1

You should use a better value for pi and allow the scale to be as is defined by default in bc (20 digits):

$ echo "r=3; pi=4*a(1); (4/3)*pi*(r^3)" | bc -l
113.09733552923255658339

That is a better way to perform the calculation. Scale is a parameter to define the precision used in the calculation, not a parameter to format the numeric output of the result.

To format the number you may use printf:

$ result="$(echo "r=3; pi=4*a(1); (4/3)*pi*(r^3)" | bc -l)"
$ printf '%.2f' "$result"
113.10

If you get into trouble with the numeric decimal character (, or .), you may use this version:

$ LC_NUMERIC=C printf '%.2f' "$result"
113.10

printf rounds the number following the IEEE 754 implementation

Of course, you can force bc to cut the decimals with a final change of the scale:

$ echo "r=3; pi=4*a(1); res=(4/3)*pi*(r^3); scale=2; res/1" | bc -l
113.09

But that is a bad idea in general.