0

This is a self-answered question, the research that is reasonable to ask for a question goes in the answer part, Please do not downvote because you believe I have not researched enough for an answer. Thanks. In any case, there is no description (that I can find) of this characteristic of bc in this site.

When using bc, the % operator is claimed to calculate the "remainder", and yes, it works for integer numbers and when the scale is zero:

$ bc <<<' scale=0; 27 % 7 '
6

But it fails to give the "integer remainder" if the scale is not zero:

$ bc <<<' scale=10; 27 % 7 '
.0000000003

Why (or how) is this definition of the % modulo useful?

0

The % operator is clearly defined in the bc manual as [a] :

# Internal % operator definition:
define internalmod(n,d,s) { auto r,oldscale;
                            oldscale=scale; r=n/d;
                            s=max(s+scale(d),scale(n)); 
                            scale=s; r = n-(r)*d;
                            scale=oldscale; return(r) }

Assuming max has been defined as:

define max(x,y){ if(x>y){return(x)};return(y) }

How is that long definition useful?

  1. Integer remainder.
    I'll show both the internalmod function and the % operator results to prove that they are equivalent for some of the next operations.

    If the numbers are integer, and scale is set to 0, it is the integer remainder function.

    $ bc <<<'n=17; d=3; scale=0;a=internalmod(n,d,scale);b=n%d;print a," ",b,"\n"'
    2 2
    $ bc <<<'n=17; d=6; scale=0;a=internalmod(n,d,scale);b=n%d;print a," ",b,"\n"'
    5 5
    

That is not the same as the math mod function. I'll solve that below.

  1. Decimal remainder.
    If the number n is a longer decimal number, and we modify the scale, we get:

    $ bc <<<'n=17.123456789;d=1; scale=0 ;a=internalmod(n,d,scale);b=n%d;
                      print a," ",b,"\n"'
    .123456789 .123456789
    
    $ bc <<<'n=17.123456789;d=1; scale=3 ;a=internalmod(n,d,scale);b=n%d;
                      print a," ",b,"\n"'
    .000456789 .000456789
    

    Note that here, the first 3 decimal digits were removed and the remainder given is from the fourth decimal digit.

    $ bc <<<'n=17.123456789;d=1; scale=7 ;a=internalmod(n,d,scale);b=n%d;
                      print a," ",b,"\n"'
    .000000089 .000000089
    

    That shows that the remainder is made more versatile by that definition.

Now it is: the remainder after the value of scale.

  1. Scale change The change of scale is required because the number d (divisor) may have more decimal digits than n. In that case, more decimals are needed to have a more precise result from the division:

    $ bc <<<'n=17.123456789; d=1.00000000001; scale=0;
             a=internalmod(n,d,scale);   b=n%d;
             print a," ",scale(a)," -- ", b," ",scale(b),"\n"'
    .12345678883 11 -- .12345678883 11
    

    And, if the scale change:

    $ bc <<<'n=17.123456789; d=1.00000000001; scale=5;
             a=internalmod(n,d,scale);    b=n%d;
             print a," ",scale(a)," -- ", b," ",scale(b),"\n"'
    .0000067888287655 16 -- .0000067888287655 16
    

    As can be seen above, the scale value changes to present a reasonably precise result of the division for any value of n, d and scale.

I'll assume that by the comparison between the internalmod and the % operator both have been proven to be equivalent.

  1. Confusion. Be careful because playing with the value of d may become confusing:

    $ bc <<<'n=17.123456789; d=10; scale=3; a=n%d;
                      print a," ",scale(a),"\n"'
    .003456789 9
    

    And:

    $ bc <<<'n=17.123456789; d=1000; scale=3; a=n%d;
                      print a," ",scale(a),"\n"'
    .123456789 9
    

    That is: the value of d (above 1) will modify the effect of the value of scale set.

Probably, for values of d different than 1 you should use scale=0 (unless you really know what you are doing).

  1. Math mod.
    Since we are taking such a deep dive into mod functions, we probably should clarify the real effect of % in bc. The % operator in bc is using a "truncating division". One that rounds toward 0. That is important for negative values of both n and/or d:

    $ bc <<<'scale=0; n=13; d=7; n%d; '
    6
    
    $ bc <<<'scale=0; n=13; d=-7; n%d; '
    6
    

    The sign of the remainder follows the sign of the dividend.

    $ bc <<<'scale=0; n=-13; d=7; n%d; '
    -6
    
    $ bc <<<'scale=0; n=-13; d=-7; n%d; '
    -6
    

    While a correct math mod should give an always positive remainder.

    To get that (integer) mod function, use:

    # Module with an always positive remainder (euclid division).
    define modeuclid(x,div)  {  if(div!=int(div)){
                                "error: divisor should be an integer ";return(0)};
                                return(x - div*int(x/div))  }
    

    And (then) this will work:

    $ bc <<<"n=7.123456789; d=5; modeuclid(34.123456789,7)"
    6.123456789
    

[a]

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.


For the bc code that follows the point where this footnote was introduced to work correctly, define an alias as:

$ alias bc='bc -l "$HOME/.func.bc"'

And create a file named $HOME/.func.bc that contains (at least):

# Internal % operator definition:
define internalmod(n,d,s) { auto r,oldscale;
                            oldscale=scale; r=n/d;
                            s=max(s+scale(d),scale(n)); 
                            scale=s; r = n-(r)*d;
                            scale=oldscale; return(r) } 
# Max function
define max(x,y){ if(x>y){return(x)};return(y) }

# Integer part of a number toward 0:  -1.99 -> -1, 0.99 -> 0
define int(x)        {  auto os;os=scale;scale=0;
                        x=sgn(x)*abs(x)/1;scale=os;return(x)  }

define sgn (x)       {  if (x<0){x=-1};if(x>0){x=1};return(x) };
define abs (x)       {  if (x<0) x=-x; return x }; 

# Module with an always positive remainder (euclid division).
define modeuclid(x,div)  {  if(div!=int(div)){
                            "error: divisor should be an integer ";return(0)};
                            return(x - div*int(x/div))  }

A mod function for any number (integer or not) may be defined as:

# Module with an always positive remainder (euclid division).
define modeuclid(x,div)  {  div=abs(div);return(x - div*floor(x/div))  }

# Round down to integer below x (toward -inf).
define floor (x)         {  auto os,y;os=scale;scale=0;
            y=x/1;if(y>x){y-=1};scale=os;return(y) };

This definition is perfectly valid and correct by math rules, however, it may become quite confusing when trying to apply it in real cases, just saying.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.