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
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
Antworten:
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.
scale
eingestellt 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 scale
anderen 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.
scale+scale(b)
da das scale
von a / b im Allgemeinen Null sein sollte, damit es eine aussagekräftige Ausgabe liefert.
Ich habe es so gelöst:
definiere int (x) {oldscale = scale; Skala = 0; x = x / 1; scale = oldscale; return (x); }}
definiere mod (x, y) {oldscale = scale; Skala = 1000; x = x - y * int (x / y); scale = oldscale; return (x); }}
HTH
define mod(x,y) { auto oldscale=scale; scale=0; x = x%y; scale=oldscale; return( x ); }
?
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 oldscale
lokal für die Funktion. Ohne diese Einstellung oldscale
in int()
von fmod () überschreibt die oldscale
in gespeichert werden versucht fmod()
, so dass scale
Satz bis 1000 statt , was Sie vor dem Aufruf hatten fmod()
.
Ich habe diese Funktionen hinzugefügt ~/.bcrc
und die BC_ENV_ARGS
Umgebungsvariable 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 scale
von 1000 können in den meisten Fällen übertrieben sein
Der %
Bediener ist im bc
Handbuch 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, max
wurde definiert als:
define max(x,y){ if(x>y){return(x)};return(y) }
1. Ganzzahliger Rest .
Ich werde sowohl die internalmod
Funktion 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 n
eine 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
, d
und scale
.
Ich gehe davon aus, dass sich durch den Vergleich zwischen dem internalmod
und dem %
Bediener beide als gleichwertig erwiesen haben.
4. Verwirrung . Seien Sie vorsichtig, da das Spielen mit dem Wert von d
verwirrend 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 d
andere 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 n
und / 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.
bc
Definieren 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.
Wie andere Antworten gesagt haben, ist es das Ergebnis der Definition a%b
als (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/13
als 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
define mod(x,base){oldscale=scale; scale=0; result=x%base; scale=oldscale; return result }