bash, bc modulo does not work with -l flag
Asked Answered
C

5

14

So I am trying to use bc to calculate some logarithms but I also need to use it to calculate the modulus for something. Whilst making my script, I launched bc to test it.

Without any flags, bc <<< "3%5" of course returns 3.

But with bc -l (loads math library so I can compute logarithms) any calculation of a%b returns 0 where a and b can be any number but 0.

What's happening?

Corabelle answered 14/12, 2014 at 14:16 Comment(0)
I
18

That's because, from the manual:

   expr % expr
          The result of the expression is the "remainder" and it  is  com‐
          puted  in  the following way.  To compute a%b, first a/b is com‐
          puted to scale digits.  That result is used to compute a-(a/b)*b
          to  the scale of the maximum of scale+scale(b) and scale(a).  If
          scale is set to zero and  both  expressions  are  integers  this
          expression is the integer remainder function.

When you run bc with the -l flag, scale is set to 20. To fix this:

bc -l <<< "oldscale=scale; scale=0; 3%5; scale=oldscale; l(2)"

We first save scale in variable oldscale, then set scale to 0 to perform some arithmetic operations, and to compute a ln we set scale back to its old value. This will output:

3
.69314718055994530941

as wanted.

Incandesce answered 14/12, 2014 at 14:24 Comment(0)
G
9

You could define a function that works in math mode by temporarily setting scale to zero.

I have bc aliased like this:

alias bc='bc -l ~/.bcrc'

Thus ~/.bcrc is evaluated before any other expressions, so you can define functions in ~/.bcrc. For example a modulus function:

define mod(x,y) { 
  tmp   = scale
  scale = 0
  ret   = x%y
  scale = tmp
  return ret
}

Now you can do modulo like this:

echo 'mod(5,2)' | bc

Output:

1
Gendron answered 16/6, 2017 at 11:36 Comment(0)
B
8

According to the bc manual,

   expr % expr
          The result of the expression is the "remainder" and it is computed 
          in the following way.  To compute a%b, first a/b is computed to
          scale digits.   That  result  is used to compute a-(a/b)*b to the 
          scale of the maximum of scale+scale(b) and scale(a).  If scale is
          set to zero and both expressions are integers this expression is 
          the integer remainder function.

So what happens is that it tries to evaluate a-(a/b)*b using the current scale settings. The default scale is 0 so you get the remainder. When you run bc -l you get scale=20 and the expression a-(a/b)*b evaluates to zero when using 20 fractional digits.

To see how it works, try some other fractions:

$ bc -l
1%3
.00000000000000000001

To make a long story short, just compare three outputs:

Default scale with -l enabled (20):

scale
20

3%5
0

1%4
0

Let's set scale to 1:

scale=1

3%5
0

1%4
.2

Or to zero (default without -l):

scale=0

3%5
3

1%4
1
Bathrobe answered 14/12, 2014 at 14:25 Comment(0)
G
4

For what it's worth, when I use bc -l, I have the following functions defined:

define trunc(x)   {auto s; s=scale; scale=0; x=x/1; scale=s; return x}
define mod(x,y)   {return x-(y*trunc(x/y))}

That should give you a proper MOD function, while keeping your scale intact. Of course, it won't help if you NEED to use the % operator for some reason.

(That TRUNC function is quite handy too, forming the basis for many other useful functions that are outside the scope of this answer.)

Gillette answered 21/6, 2018 at 23:14 Comment(0)
T
3

man bc :

If bc is invoked with the -l option, a math library is preloaded and the default scale is set to 20.

So maybe you should set the scale to 0 :

#bc
scale=0
10%3
1
Tass answered 14/12, 2014 at 14:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.