GNU BC: "Modulo"% mit einer anderen Skala als 0


12

Wenn die Skala nicht Null ist, geben Berechnungen mit%, z. B. 3% 2 und 46% 4, tendenziell 0 aus. Wie ist der Algorithmus mit einer anderen Skala als 0 ausgelegt?

bc
scale=10
print 4%3   // output 0

1
Für diejenigen, die nur etwas wollen, das funktioniert: define mod(x,base){oldscale=scale; scale=0; result=x%base; scale=oldscale; return result }
Hallo Welt

Antworten:


13

In der Befehlsanleitung wird Folgendes darüber beschrieben, wie BC das Modulo berechnet:

Das Ergebnis des Ausdrucks ist der "Rest" und wird auf folgende Weise berechnet. Um a% b zu berechnen, wird zuerst a / b berechnet, um Ziffern zu skalieren. Dieses Ergebnis wird verwendet, um a - (a / b) * b auf die Skala des Maximums von Skala + Skala (b) und Skala (a) zu berechnen . Wenn die Skalierung auf Null gesetzt ist und beide Ausdrücke Ganzzahlen sind, ist dieser Ausdruck die Ganzzahl-Restfunktion.


EDIT: Ich habe mir den Quellcode für GNU BC angesehen und festgestellt, dass der Mod-Operator den Divisionsoperator erweitert. Mit anderen Worten wird das Modulo als Nebenprodukt der Division berechnet. Es basiert auf einer ganzzahligen Division, um das Modulo zu berechnen. Wenn scaleeingestellt ist, findet jedoch keine Ganzzahldivision statt.

Versuchen Sie dies in BC:

bc
scale = 0
print 5/2

scale = 5
print 5/2

du solltest bekommen:

2        << Integer Division
2.50000  << NOT integer division!

Lassen Sie uns nun diese Zahlen wie BC einstecken. Das Handbuch sagt, dass es a- (a / b) * b verwendet, um zu berechnen. Lassen Sie uns unsere beiden Ergebnisse einfügen, das aus der Ganzzahldivision und das mit einer scaleanderen als 0.

a - ( a/b ) * b
5 - ( 2   ) * 2  = 1  << CORRECT!
5 - ( 2.5 ) * 2  = 0  << VERY WRONG!

Ohne ganzzahlige Division:

a - ( a/b ) * b == a - (  a  ) == 0

Aus diesem Grund muss die Skalierung auf 0 gesetzt werden, damit das Modulo ordnungsgemäß funktioniert.
Das Problem scheint sich aus dem Design von BC und dem Umgang mit Zahlen mit einer „Skala“ zu ergeben. Damit das Modulo richtig funktioniert, benötigen wir eine Ganzzahldivision .

Es gibt andere viel fortgeschrittenere Tools , die für diesen Zweck kostenlos und Open Source sind, und ich empfehle Ihnen, sie zu verwenden.


Ich bekomme auch die gleichen Ergebnisse.
TJ L

Ihre Skala ist 0, nicht "anders als Null". Bitte setzen Sie es zum Beispiel mit "scale = 10" und versuchen Sie es dann mit "3% 2".

2
Wenn dort "Skala" ohne Klammern steht, bezieht es sich auf die globale Variable "Skala".
Jweede

1
Ich bin mir nicht sicher, warum es sein muss, scale+scale(b)da das scalevon a / b im Allgemeinen Null sein sollte, damit es eine aussagekräftige Ausgabe liefert.
Jweede

1
Natürlich macht das Design BC effizienter für kleine "Debugging", obwohl nicht sicher, wie viel. Ihre anderen Tools, auf die verwiesen wird, können diese Art von Dingen möglicherweise besser handhaben, jedoch auf Kosten der Effizienz. +1

4

Ich habe es so gelöst:

ganze Zahl

definiere int (x) {oldscale = scale; Skala = 0; x = x / 1; scale = oldscale; return (x); }}

Modulo

definiere mod (x, y) {oldscale = scale; Skala = 1000; x = x - y * int (x / y); scale = oldscale; return (x); }}

HTH


Warum nicht define mod(x,y) { auto oldscale=scale; scale=0; x = x%y; scale=oldscale; return( x ); }?
SebMa

4

Die Antwort von user272970 ist großartig. Hier ist eine Optimierung:

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

Dies (mit auto oldscale) macht oldscalelokal für die Funktion. Ohne diese Einstellung oldscalein int()von fmod () überschreibt die oldscalein gespeichert werden versucht fmod(), so dass scaleSatz bis 1000 statt , was Sie vor dem Aufruf hatten fmod().

Ich habe diese Funktionen hinzugefügt ~/.bcrcund die BC_ENV_ARGSUmgebungsvariable auf gesetzt ~/.bcrc. Dadurch werden diese Funktionen jedes Mal geladen, wenn Sie bc ausführen. Jetzt kann ich einfach fmod(x,y)jedes Mal ausgeführt werden, wenn ich in bc bin, ohne diese Funktionen jedes Mal manuell definieren zu müssen.

ps scalevon 1000 können in den meisten Fällen übertrieben sein


1

Der %Bediener ist im bcHandbuch klar definiert 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) }

Angenommen, maxwurde definiert als:

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

Wie ist diese lange Definition nützlich?

1. Ganzzahliger Rest .
Ich werde sowohl die internalmodFunktion als auch den %Operator verwenden, um zu zeigen, dass sie für einige der folgenden Operationen gleichwertig sind

Wenn die Zahlen ganzzahlig sind und die Skalierung auf 0 gesetzt ist, ist dies die ganzzahlige Restfunktion.

$ 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

Das ist nicht dasselbe wie die Math Mod Funktion. Ich werde das unten lösen.

2. Dezimaler Rest.
Wenn die Zahl neine längere Dezimalzahl ist und wir die Skala ändern, erhalten wir:

$ 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

Beachten Sie, dass hier die ersten 3 Dezimalstellen entfernt wurden und der angegebene Rest von der vierten Dezimalstelle stammt.

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

Dies zeigt, dass der Rest durch diese Definition vielseitiger wird.

3. Skalierungsänderung Die Skalierungsänderung ist erforderlich, da die Zahl d(Divisor) möglicherweise mehr Dezimalstellen als enthält n. In diesem Fall sind mehr Dezimalstellen erforderlich, um ein genaueres Ergebnis der Division zu erhalten:

$ 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

Und wenn sich der Maßstab ändert:

$ 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

Wie oben zu sehen ist, dehnt sich der Skalenwert eine einigermaßen genaue Ergebnis der Division für jeden Wert von präsentieren n, dund scale.

Ich gehe davon aus, dass sich durch den Vergleich zwischen dem internalmodund dem %Bediener beide als gleichwertig erwiesen haben.

4. Verwirrung . Seien Sie vorsichtig, da das Spielen mit dem Wert von dverwirrend werden kann:

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

Und:

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

Das heißt: Der Wert von d(über 1) ändert den Effekt des Wertes der eingestellten Skala.

Wahrscheinlich sollten Sie für dandere Werte als 1 scale = 0 verwenden (es sei denn, Sie wissen wirklich, was Sie tun).

5. Math mod .
Da wir so tief in Mod-Funktionen eintauchen, sollten wir wahrscheinlich den wirklichen Effekt von %in klären bc. Der %Operator in bc verwendet eine "abschneidende Division". Eine, die auf sie zukommt 0. Das ist wichtig für negative Werte von beiden nund / oder d:

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

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

Das Zeichen des Restes folgt dem Zeichen des dividend.

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

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

Während ein korrekter Mathe- Mod einen immer positiven Rest geben sollte .

Um diese (ganzzahlige) Mod-Funktion zu erhalten, verwenden Sie:

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

Und das wird funktionieren:

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

[ein]

Ausdruck% Ausdruck
Das Ergebnis des Ausdrucks ist der "Rest" und wird auf folgende Weise berechnet. Um a% b zu berechnen, wird zuerst a / b berechnet, um Ziffern zu skalieren. Dieses Ergebnis wird verwendet, um a- (a / b) * b auf die Skala des Maximums von Skala + Skala (b) und Skala (a) zu berechnen.
Wenn die Skalierung auf Null gesetzt ist und beide Ausdrücke Ganzzahlen sind, ist dieser Ausdruck die Ganzzahl-Restfunktion.


bcDefinieren Sie einen Alias für den Code, der dem Punkt folgt, an dem diese Fußnote eingeführt wurde, um ordnungsgemäß zu funktionieren:

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

Und erstellen Sie eine Datei mit dem Namen $HOME/.func.bc, die (mindestens) enthält:

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

Eine Mod-Funktion für eine beliebige Zahl (Ganzzahl oder nicht) kann definiert werden 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) };

Diese Definition ist nach mathematischen Regeln vollkommen gültig und korrekt. Sie kann jedoch ziemlich verwirrend werden, wenn Sie versuchen, sie in realen Fällen anzuwenden.


0

Wie andere Antworten gesagt haben, ist es das Ergebnis der Definition a%bals (a-(a/b)*b), bewertet zum aktuellen Zeitpunkt scale. Dies bedeutet, dass Sie es verwenden müssen, wenn Sie möchten, dass es als Ganzzahlmodul fungiert scale=0.

Ich bin jedoch nicht der Meinung, dass es "falsch" ist. Es ist ein potenziell nützliches Werkzeug, insbesondere zur Fehlerbewertung.

scale=5
1/3
> .33333
1%3
> .00001

Was verlieren wir, wenn wir 7/13als 4-stellige Dezimalstelle darstellen .5384?

scale=4
7/13
> .5384
7%13
> .0008

Anscheinend 0.0008/13.

Und schließlich kann es verwendet werden, um einen Teil einer Dezimalstelle zu extrahieren, da es nicht auf der Verwendung von Ganzzahlen besteht.

scale=1
123.456/1
> 123.4
123.456%1
> .056
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.