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

13237
KronoS

Wenn die Skala nicht Null ist, neigen Berechnungen mit%, wie 3% 2 und 46% 4, zur Ausgabe von 0. Wie wird der Algorithmus mit einer anderen Skala als 0 entworfen?

bc scale=10 print 4%3 // output 0 
7
Für diejenigen, die nur etwas wollen, das funktioniert: `define mod (x, base) ` Hello World vor 9 Jahren 0

5 Antworten auf die Frage

10
jweede

Das Befehlshandbuch sagt dazu aus, wie BC das Modulo berechnet:

Das Ergebnis des Ausdrucks ist der "Rest" und wird auf folgende Weise berechnet. Um a% b zu berechnen, wird zunächst a / b berechnet, um die Ziffern zu skalieren. Dieses Ergebnis wird verwendet, um a - (a / b) * b auf die Skala von Maximum von Skala + Skala (b) und Skala (a) zu berechnen . Wenn scale auf null gesetzt ist und beide Ausdrücke ganze Zahlen sind, ist dieser Ausdruck die ganzzahlige Restfunktion.


EDIT: Ich habe mir den Quellcode für GNU BC angesehen und festgestellt, dass der Mod-Operator den Divisionsoperator erweitert. Mit anderen Worten, das Modulo wird als Nebenprodukt der Division berechnet. Es basiert auf der Ganzzahldivision, um das Modulo zu berechnen. Wenn scalegesetzt wird, findet jedoch keine ganzzahlige Division 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 so einstecken, wie es BC tut. Das Handbuch sagt, es verwendet a- (a / b) * b zum Berechnen. Stecken Sie unsere beiden Ergebnisse ein, nämlich das Ergebnis einer Ganzzahldivision und eines mit einer scaleanderen Zahl 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 Skala auf 0 gesetzt werden, damit das Modulo ordnungsgemäß funktioniert.
Das Problem scheint sich aus dem Design von BC zu ergeben und wie Zahlen mit einer Skala behandelt werden. Damit das Modulo korrekt funktioniert, benötigen wir eine Ganzzahlteilung .

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

Ich bekomme auch die gleichen Ergebnisse. tj111 vor 14 Jahren 0
Ihre Skala ist 0, nicht "anders als Null". Stellen Sie es beispielsweise mit "scale = 10" ein und versuchen Sie "3% 2". vor 14 Jahren 0
Was bedeutet "Skala + Skala (B)"? Soll es "Skala (a) + Skala (b)" sein? Oder vielleicht skalieren Sie (a) +1 und Skala (b) +1 für gute obere Schranken. Ich kann ihren Zweck sonst nicht verstehen. vor 14 Jahren 0
Zitieren ist genau von: http://www.gnu.org/software/bc/manual/html_chapter/bc_3.html#SEC10 vor 14 Jahren 0
Wenn es Skalierung ohne Klammern sagt, bezieht es sich auf die globale Variable Skalierung. jweede vor 14 Jahren 2
Es macht immer noch keinen Sinn. Warum ist es nicht "Maßstab + Maßstab (b) und Maßstab + Maßstab (a)"? vor 14 Jahren 0
Was meint der Autor mit "a- (a / b) * b"? 0? vor 14 Jahren 0
Reihenfolge in der Berechnung: @ 1st. k = a / b @ 2. a - k * b vor 14 Jahren 0
Weitere Erklärungen wurden hinzugefügt. jweede vor 14 Jahren 0
Ich bin nicht sicher, warum es "scale + scale (b)" sein muss, da die "Skala" von a / b im Allgemeinen Null sein sollte, damit sie eine sinnvolle Ausgabe liefert. jweede vor 14 Jahren 1
Natürlich macht das Design BC für das kleine "Debugging" effizienter, aber nicht sicher, wie viel. Ihre referenzierten anderen Tools können mit dieser Art von Dingen besser umgehen, jedoch auf Kosten der Effizienz. +1 vor 14 Jahren 1
3
user272970

Ich habe es so gelöst:

ganze Zahl

definiere int (x)

modulo

definiere mod (x, y)

HTH

3
Juan

Die Antwort von user272970 ist großartig. Hier ist ein Tweak:

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

Diese (Verwendung auto oldscale) macht oldscaledie Funktion lokal. Ohne diese Einstellung oldscalein int()von fmod () wird das überschreiben, oldscaledass in gespeichert werden versucht fmod(), so dass scalebis 1000 festgelegt, anstatt alles, 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. So kann ich jetzt fmod(x,y)jedes Mal laufen, 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

0
zebediah49

Wie andere Antworten bereits gesagt haben, ist dies das Ergebnis der Definition von a%bas (a-(a/b)*b), die zum aktuellen Zeitpunkt ausgewertet wird scale. Das bedeutet, wenn Sie möchten, dass es als Ganzzahlmodul fungiert, müssen Sie es mit verwenden scale=0.

Ich stimme jedoch nicht zu, 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/13die 4-stellige Dezimalzahl darstellen .5384?

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

Anscheinend 0.0008/13.

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

scale=1 123.456/1 > 123.4 123.456%1 > .056 
0
Isaac

Der %Operator ist im bcHandbuch eindeutig als [a] definiert :

# 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(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 Operationen unten gleich sind

Wenn die Zahlen ganzzahlig sind und die Skalierung auf 0 gesetzt ist, handelt es sich um 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 das Gleiche wie die mathematische Mod-Funktion. Ich werde das unten lösen.

2. Dezimalrest.
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 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 

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

3. Skalenwechsel Der Skalenwechsel ist erforderlich, da die Zahl d(Divisor) mehr Dezimalstellen als haben kann 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 die Skala ä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 durch den Vergleich zwischen dem Operator internalmodund dem %Operator beide gleichwertig sind.

4. Verwirrung . Seien Sie vorsichtig, denn das Spiel mit dem Wert von dkann verwirrend werden:

$ 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 Skalierungswerts.

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

5. Math mod .
Da wir uns so intensiv mit Mod-Funktionen beschäftigen, sollten wir wahrscheinlich die tatsächliche Wirkung von %in klären bc. Der %Operator in bc verwendet eine "abschneidende Division". Eine, die auf sich 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 ergeben sollte .

Um diese (Ganzzahl-) 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]

expr% expr
Das Ergebnis des Ausdrucks ist der "Rest" und wird auf folgende Weise berechnet. Um a% b zu berechnen, wird zunächst a / b berechnet, um die 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 scale auf null gesetzt ist und beide Ausdrücke ganze Zahlen sind, ist dieser Ausdruck die ganzzahlige Restfunktion.


bcDefinieren Sie für den Code, der dem Punkt folgt, an dem diese Fußnote korrekt eingefügt wurde, einen Alias ​​als:

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

Und erstellen Sie eine Datei mit dem Namen $HOME/.func.bc(mindestens):

# 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(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);if(x>0);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);scale=os;return(y) }; 

Diese Definition ist durch mathematische Regeln vollkommen gültig und richtig, kann jedoch recht verwirrend werden, wenn versucht wird, sie in realen Fällen anzuwenden.