# bc scale: How to avoid rounding? (Calculate small binomial probability)

Following code calculates the Binomial Probability of a success event k out of n trials:

``````n=144
prob=\$(echo "0.0139" | bc)

echo -e "Enter no.:"
read passedno

k=\$passedno
nCk2() {
num=1
den=1
for((i = 1; i <= \$2; ++i)); do
((num *= \$1 + 1 - i)) && ((den *= i))
done
echo \$((num / den))
}

binomcoef=\$(nCk2 \$n \$k)

binprobab=\$(echo "scale=8; \$binomcoef*(\$prob^\$k)*((1-\$prob)^(\$n-\$k))" | bc)

echo \$binprobab
``````

When for \$passedno (=k) "5" is entered, then the result is shown as 0 (instead of "0.03566482") whereas with "4" passed I get ".07261898".

How can I print the output with given precision of 8 decimal digits without getting the rounded value of the output?

## 5 Answers

FWIW,

``````prob=\$(echo "0.0139" | bc)
``````

is unnecessary - you can just do

``````prob=0.0139
``````

Eg,

``````\$ prob=0.0139; echo "scale=5;1/\$prob" | bc
71.94244
``````

There's another problem with your code, apart from the underflow issue. Bash arithmetic may not be adequate to handle the large numbers in your `nCk2` function. Eg, on a 32 bit system passing 10 to that function returns a negative number, -133461297271.

To handle the underflow issue you need to calculate at a larger scale, as mentioned in the other answers. For the parameters given in the OP a scale of 25 to 30 is adequate.

I've re-written your code to do all the arithmetic in `bc`. Rather than just piping commands into `bc` via `echo`, I've written a full `bc` script as a here document inside a Bash script, since that makes it easy to pass parameters from Bash to `bc`.

``````#!/usr/bin/env bash

# Binomial probability calculations using bc
# Written by PM 2Ring 2015.07.30

n=144
p='1/72'
m=16
scale=30

bc << EOF
define ncr(n, r)
{
auto v,i

v = 1
for(i=1; i<=r; i++)
{
v *= n--
v /= i
}
return v
}

define binprob(p, n, r)
{
auto v

v = ncr(n, r)
v *= (1 - p) ^ (n - r)
v *= p ^ r
return v
}

sc = \$scale
scale = sc
outscale = 8

n = \$n
p = \$p
m = \$m

for(i=0; i<=m; i++)
{
v = binprob(p, n, i)
scale = outscale
print i,": ", v/1, "\n"
scale = sc
}
EOF
``````

output

``````0: .13345127
1: .27066174
2: .27256781
3: .18171187
4: .09021610
5: .03557818
6: .01160884
7: .00322338
8: .00077747
9: .00016547
10: .00003146
11: .00000539
12: .00000084
13: .00000012
14: .00000001
15: 0
16: 0
``````

### The Intro

Say you want to calculate what percentage 7 is of 116.
You simply divide 7 by 116 and multiply the result by 100.
It should go something like this: ``````bc -l <<< '(7/116)*100'
6.03448275862068965500``````

### The Problem

You want it accurate to two decimal places so you add `scale=2;`, expecting this: But you get some strange results:

``````bc -l <<< 'scale=2; 100*(7/116)'
6.00``````

You try increasing the `scale` value:

``````bc -l <<< 'scale=3; 100*(7/116)'
6.000``````

For some reason `scale=4;` is accurate to 2 places, but has these trailing `0`s

``````bc -l <<< 'scale=4; 100*(7/116)'
6.0300``````

### The Reason

The first part of the calculation is accurate to 2 decimal places and the rest gets chopped off.  ### The Solution

The ideal solution would be for `bash`/`sh` to support floating point calculations natively. Why don't they already? I have no idea. Seems stupid not to. Until then you can make do with these:

bc

1. Define a variable.
2. Set the `scale`.
3. Divide by 1.
``````bc -l <<< "x=(7/116)*100; scale=2; x/1"
6.03``````

printf

1. Wrap the `bc` command with `"\$(`...`)"` for command substitution.
2. Pass it to `printf`.
3. Use `%.2f` in the format string (`%f` says float, `.2` says accurate to 2 decimal places (like `scale=2`)).
``````printf '%.2f\n' "\$(bc -l <<< '(7/116)*100')"
6.03``````

awk

1. FYI: `awk` can do arithmetic too
2. I think `echo` breaks the interactive loop somehow. There's probably a better way to do it.
3. It doesn't give you control over the floating point. I'm sure it can be done though somehow.
``````echo | awk '{print 100*(7/116)}'
6.03448``````
``````n=144
prob=\$(echo "0.0139" | bc)

echo -e "Enter no.:"
read passedno

k=\$passedno
nCk2() {
num=1
den=1
for((i = 1; i <= \$2; ++i)); do
((num *= \$1 + 1 - i)) && ((den *= i))
done
echo \$((num / den))
}

binomcoef=\$(nCk2 \$n \$k)
binprobab=\$(echo "\$binomcoef*(\$prob^\$k)*((1-\$prob)^(\$n-\$k))" | bc -l)
printf "%0.8f\n" \$binprobab
``````

As you know it's because `0.0139^5 = .00000000051888844699` is too small and rounded to 0 with `scale=8`. How about doing intermediate calculations with greater scale, then round the last value with `scale=8`?

``````binprobab=\$(echo "scale=20; a=\$binomcoef*(\$prob^\$k)*((1-\$prob)^(\$n-\$k)); scale=8; a/1" | bc)
``````

Dividing by 1 is commonly used to round a number with `scale` in bc.

Thanks for the explanation @voices. The awk solution with printf:

``````awk 'BEGIN {printf("%.2f\n",100*7/116)}'
``````
• This answer is not even touching on `bc`, which was the topic of the question. – Kusalananda Dec 14 '19 at 14:26
• @Kusalananda: I should use reply to voices, becouse his awk 3 solution is not perfect. I am a newbie :P – gabor.zed Dec 15 '19 at 0:40
• @jesse_b: it's not > but <, awk wants input, maybe there is a switch to disable it, but it was simplier :P – gabor.zed Dec 15 '19 at 0:45
• @Kusalananda: I don't have enogh reputation to comment. lol. I just tried to help others. well. fork. – gabor.zed Dec 15 '19 at 0:50
• @jesse_b: my bad. it really doesn't. thanks – gabor.zed Dec 15 '19 at 1:36