Difference between printf %.3f and bc rounding behavior
Asked Answered
S

4

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?

Sparrowgrass answered 2/10, 2014 at 19:36 Comment(4)
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).Dropping
...alternately, use $(...) instead of `...` anyhow -- it's the modern POSIX syntax, and less confusing to nest.Dropping
....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).Dropping
a little more verbose :) read a; echo "scale=4; $a" | bc -l | xargs -I {} printf '%.*f\n' 3 {}Dutch
F
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.

Fia answered 2/10, 2014 at 20:0 Comment(0)
W
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.

Wenzel answered 2/10, 2014 at 19:48 Comment(0)
R
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.

Repletion answered 2/10, 2014 at 19:50 Comment(2)
I hope I'm not being presumptuous or heavy-handed with the edit.Dropping
Nope -- looks perfect. Take all the editorial liberty you need :)Repletion
A
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.

Anecdotist answered 7/9, 2015 at 18:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.