5

Ok so this is a hackerrank problem (https://www.hackerrank.com/challenges/bash-tutorials---arithmetic-operations). Basically, input is an arithmetic expression and I'm supposed to print out formatted answer (3 decimal places). I tried this at first

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

It passed several tests but not the first one.

5+50*3/20 + (19*2)/7 The answer is 17.929 but my code prints out 17.928. I tried this code instead

read exp  
printf "%.3f\n" `echo $exp | bc -l`

Note: the echo part should be in backticks but I put ' ' to not confuse with block quotes. All tests passed. So what's the difference?

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
tlaminator
  • 946
  • 2
  • 9
  • 23
  • Backticks are only appropriate for code formatting less than a line -- for multiline segments, use indentation for code formatting (which is what the javascript editor will do for you if you click the code button). – Charles Duffy Oct 02 '14 at 20:08
  • ...alternately, use `$(...)` instead of `\`...\`` anyhow -- it's the modern POSIX syntax, and less confusing to nest. – Charles Duffy Oct 02 '14 at 20:11
  • ....also -- `echo "$exp" | bc -l`, not `echo $exp | bc -l`; if your expression contained spaces around the `*`, you'd have very surprising behavior without the quotes (and even without the spaces, you'd have surprising behavior if your shell had the `nullglob` option enabled). – Charles Duffy Oct 02 '14 at 20:12
  • a little more verbose :) ```read a; echo "scale=4; $a" | bc -l | xargs -I {} printf '%.*f\n' 3 {}``` – Yeti Apr 13 '15 at 18:23

4 Answers4

5

The reason that the two differ is that bc always cuts off the numbers instead of rounding them. I. e. echo "scale = 3 ; 8/9" | bc produces 0.888 instead of the correctly rounded 0.889.

Your test case evaluates to 17.928571429, which is rounded to 17.929 with your printf approach, but cut off to 17.928 with the bc approach.

cmaster - reinstate monica
  • 38,891
  • 9
  • 62
  • 106
2

I think the problem is scale = 3; part. For example, if you use

      printf "%.3f\n" ` echo  "scale = 3 ; $exp " | bc -l`

You will get 17.928 again. So the answer is you need to set scale at least to 4 and then print it in three digit.

CS Pei
  • 10,869
  • 1
  • 27
  • 46
2

The problem is rounding and/or significant digits returned by bc. When you use echo with scale=3 you are relying on bc to return only 3 digits. When you tell printf to output only 3 decimal places (using the format %.3f), you are relying on printf to determine which digits. Essentially, the difference is in the rounding done by bc utilizing scale=3 and that done by printf with the double %.3f conversion.

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
0
v=$(sed 's/[[:blank:]]//g')
(echo "scale=4"; echo $v) | bc | xargs printf '%.3f'

The above reads the integer from stdin, removes the blank lines, pipes it to bc to perform the calculation with 4 digits precision, then formats it with correct rounding to 3 digits with the help of printf. My solution to the above mentioned HackerRank problem.

Sasha Pachev
  • 5,162
  • 3
  • 20
  • 20