GNU BC: "modulo" % with scale other than 0
The command manual says this about how BC calculates the modulo:
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.
EDIT: I looked at the source code for GNU BC and found that the mod operator extends the division operator. In other words, the modulo is calculated as a by-product of the division. It relies on integer division to calculate the modulo. When
scale
is set, however integer division does not take place.
Try this in BC:
bc
scale = 0
print 5/2
scale = 5
print 5/2
you should get:
2 << Integer Division
2.50000 << NOT integer division!
Now let's plug in these figures the way BC does. The manual says it uses a-(a/b)*b to calculate. Let's plug in our two results, the one resulting from integer division and the one with a scale
other than 0.
a - ( a/b ) * b
5 - ( 2 ) * 2 = 1 << CORRECT!
5 - ( 2.5 ) * 2 = 0 << VERY WRONG!
Without integer division:
a - ( a/b ) * b == a - ( a ) == 0
This is why scale must be set to 0 for the modulo to work properly.
The issue seems to arise out of the design of BC and how it handles numbers with a 'scale'. In order for the modulo to work correctly we need integer division.
There are other much more advanced tools that are free and open source for this purpose, and I recommend you use them.
I solved it this way:
integer
define int(x) { oldscale=scale; scale=0; x=x/1; scale=oldscale; return( x ); }
modulo
define mod(x,y) { oldscale=scale; scale=1000; x = x - y * int(x/y); scale=oldscale; return( x ); }
HTH
user272970's answer is great. Here's a tweak to it:
define int(x) { auto oldscale; oldscale=scale; scale=0; x=x/1; scale=oldscale; return( x ); }
define fmod(x,y) { auto oldscale; oldscale=scale; scale=1000; x = x - y * int(x/y); scale=oldscale; return( x ); }
This (using auto oldscale
) makes oldscale
local to the function. Without that, setting oldscale
in int()
from fmod() will overwrite the oldscale
that is trying to be saved in fmod()
, leaving scale
set to 1000 instead of whatever you had before calling fmod()
.
I added these functions to ~/.bcrc
and set the BC_ENV_ARGS
environment variable to ~/.bcrc
. That will load these functions every time you run bc. So now I can just run fmod(x,y)
any time I'm in bc without having to manually define those functions every time.
p.s. scale
of 1000 might be overkill in most cases