
Wenn die Skala von Null abweicht, geben Berechnungen mit %-Werten wie 3 %2 und 46 %4 tendenziell 0 aus. Wie ist der Algorithmus bei einer Skala ungleich 0 aufgebaut?
bc
scale=10
print 4%3 // output 0
Antwort1
DerBefehlshandbuchsagt Folgendes darüber, wie BC den Modulo berechnet:
Das Ergebnis des Ausdrucks ist der "Rest" und wird folgendermaßen berechnet. Um a%b zu berechnen, müssen Sie zunächsta/bwird auf Skalenziffern berechnet. Dieses Ergebnis wird verwendet, um zu berechnena - ( a/b ) * bauf den Maßstab des Maximums von Maßstab+Maßstab(b) und Maßstab(a).Wenn die Skalierung auf Null gesetzt ist und beide Ausdrücke Ganzzahlen sind, ist dieser Ausdruck die ganzzahlige Restfunktion.
BEARBEITEN: 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. Zur Berechnung des Modulos wird die Ganzzahldivision verwendet. Wenn
scale
jedoch gesetzt ist, findet keine Ganzzahldivision statt.
Versuchen Sie Folgendes in BC:
bc
scale = 0
print 5/2
scale = 5
print 5/2
du solltest bekommen:
2 << Integer Division
2.50000 << NOT integer division!
Nun setzen wir diese Zahlen ein, wie BC es tut. Im Handbuch steht, dass esa-(a/b)*bzu berechnen. Lassen Sie uns unsere beiden Ergebnisse einsetzen, das aus der Ganzzahldivision resultierende und das mit einem Wert scale
ungleich 0.
a - ( a/b ) * b
5 - ( 2 ) * 2 = 1 << CORRECT!
5 - ( 2.5 ) * 2 = 0 << VERY WRONG!
Ohne Ganzzahldivision:
a - ( a/b ) * b == a - ( a ) == 0
Aus diesem Grund muss die Skalierung auf 0 gesetzt werden, damit das Modulo richtig funktioniert.
Das Problem scheint aus dem Design von BC und der Art und Weise zu resultieren, wie es Zahlen mit einer „Skalierung“ behandelt. Damit das Modulo richtig funktioniertwir brauchen ganzzahlige Division.
Es gibtanderevielfortgeschrittenere Werkzeugedie für diesen Zweck kostenlos und Open Source sind, und ich empfehle Ihnen, sie zu verwenden.
Antwort2
Die Antwort von user272970 ist großartig. Hier ist eine Verbesserung:
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
die Funktion lokal. Ohne dies überschreibt die Einstellung oldscale
in von fmod() die Einstellung, die in gespeichert werden soll , und lässt den Wert auf 1000 anstatt auf den Wert, den Sie vor dem Aufruf hatten, stehen .int()
oldscale
fmod()
scale
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 sie also jederzeit ausführen, wenn fmod(x,y)
ich in bc bin, ohne diese Funktionen jedes Mal manuell definieren zu müssen.
ps scale
von 1000 könnten in den meisten Fällen übertrieben sein
Antwort3
Ich habe es folgendermaßen gelöst:
ganze Zahl
definiere int(x) { alter Maßstab=Maßstab; Maßstab=0; x=x/1; Maßstab=alter Maßstab; return( x ); }
Modulo
definiere mod(x,y) { alter Maßstab=Maßstab; Maßstab=1000; x = x - y * int(x/y); Maßstab=alter Maßstab; return( x ); }
HTH
Antwort4
Wie in anderen Antworten bereits gesagt wurde, ist es das Ergebnis der Definition a%b
als (a-(a/b)*b)
, ausgewertet beim aktuellen scale
. Das bedeutet, dass Sie es mit verwenden müssen, wenn es als ganzzahliger Modul fungieren soll 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 es 7/13
als vierstellige Dezimalzahl darstellen .5384
?
scale=4
7/13
> .5384
7%13
> .0008
Scheinbar 0.0008/13
.
Und da es nicht auf der Verwendung ganzer Zahlen beruht, kann es schließlich zum Extrahieren eines Teils einer Dezimalzahl verwendet werden.
scale=1
123.456/1
> 123.4
123.456%1
> .056