GNU BC: Hoe is de “modulo” (%) met een andere schaal dan 0 nuttig?

Dit is een zelfbeantwoorde vraag, het onderzoek dat redelijk is om voor een vraag te stellen, gaat in het antwoordgedeelte. Gelieve niet naar beneden te stemmen omdat u denkt dat ik niet genoeg onderzoek heb gedaan voor een antwoord. Bedankt. In elk geval is er geen beschrijving (die ik kan vinden) van deze eigenschap van bc op deze site.

Bij gebruik van bc, de % operator berekent de “rest”, en ja, het werkt voor gehele getallen en wanneer de schaal nul is :

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

Maar het geeft geen” integer rest “als de schaal niet nul is:

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

Waarom (of hoe) is deze definitie van de % modulo nuttig?

Antwoord

De % operator is duidelijk gedefinieerd in de bc handleiding als [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) } 

Ervan uitgaande dat max is gedefinieerd als:

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

Hoe is die lange definitie bruikbaar?

  1. Geheel getal rest .
    Ik “zal beide internalmod -functie en de % -operatieresultaten om te bewijzen dat ze equivalent zijn voor enkele van de volgende bewerkingen.

    Als de getallen gehele getallen zijn, en schaal is ingesteld op 0, het is de functie van de gehele rest.

    $ 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 

Dat is niet hetzelfde als de wiskundige mod-functie. Dat los ik hieronder op.

  1. Decimale rest.
    Als het getal n een langer decimaal getal is, en we wijzigen de schaal, krijgen we:

    $ 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 

    Merk op dat hier de eerste 3 decimale cijfers zijn verwijderd en de rest die is opgegeven vanaf het vierde decimale cijfer.

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

    Dat toont aan dat de rest wordt door die definitie veelzijdiger gemaakt.

Nu is het: de rest na de waarde van schaal.

  1. Schaalwijziging De schaalwijziging is vereist omdat het getal d (deler) meer decimale cijfers kan hebben dan n. In dat geval er zijn meer decimalen nodig om een nauwkeuriger resultaat van de deling te krijgen:

    $ 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 

    En, als de schaal verandert e:

    $ 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 

    Zoals hierboven te zien is, verandert de schaalwaarde om een redelijk nauwkeurig resultaat te geven van de deling voor elke waarde van n, d en scale.

I” Ik neem aan dat door de vergelijking tussen de internalmod en de % operator is bewezen dat beide gelijkwaardig zijn.

  1. Verwarring . Wees voorzichtig, want spelen met de waarde van d kan verwarrend worden:

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

    En:

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

    Dat is: de waarde van d (boven 1) zal het effect van de waarde van scale set wijzigen.

Waarschijnlijk moet u voor waarden van d anders dan 1 gebruiken scale = 0 (tenzij u echt weet wat u doet).

  1. Wiskundige mod .
    Sinds we zijn door zo diep in de mod-functies te duiken, moeten we waarschijnlijk het echte effect van % in bc verduidelijken. De % -operator in bc gebruikt een “truncating divisie”. Een die rondloopt in de richting van 0. Dat is belangrijk voor negatieve waarden van zowel n en / of d:

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

    Het teken van de rest volgt het teken van de dividend.

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

    Hoewel een correcte math mod zou een altijd positieve rest moeten geven .

    Om die (integer) mod-functie te krijgen, gebruik:

    # 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)) } 

    En (dan) zal dit werken:

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

[a]

expr% expr
Het resultaat van de uitdrukking is de “rest” en wordt als volgt berekend manier. Om a% b te berekenen, wordt eerst a / b berekend om cijfers te schalen.Dat resultaat wordt gebruikt om a- (a / b) * b te berekenen naar de schaal van het maximum van schaal + schaal (b) en schaal (a).
Als schaal is ingesteld op nul en beide uitdrukkingen zijn gehele getallen, deze uitdrukking is de gehele restfunctie.


Voor de bc code die volgt op het punt waar deze voetnoot werd geïntroduceerd om correct te werken, definieer dan een alias als:

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

En maak een bestand met de naam $HOME/.func.bc dat ( tenminste):

# 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)) } 

Een mod-functie voor elk getal (geheel getal of niet) kan worden gedefinieerd als:

# 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) }; 

Deze definitie is volkomen geldig en correct volgens wiskundige regels, maar het kan behoorlijk verwarrend worden wanneer je het in echte gevallen probeert toe te passen door gewoon te zeggen.

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *