Ich implementiere einen 64-Bit-Festkomma-31,32-Zahlentyp in C#, basierend auf long
. So weit, so gut für Addition und Subtraktion. Multiplikation hat jedoch einen ärgerlichen Fall, den ich versuche zu lösen.64-Bit-Festkomma-Multiplikationsfehler
Mein aktueller Algorithmus besteht darin, jeden Operanden in seine höchstwertigen und niedrigstwertigen 32 Bits aufzuteilen, 4 Multiplikationen in 4 Longs durchzuführen und die relevanten Bits dieser Longs hinzuzufügen. Hier ist es in Code:
public static Fix64 operator *(Fix64 x, Fix64 y) {
var xl = x.m_rawValue; // underlying long of x
var yl = y.m_rawValue; // underlying long of y
var xlow = xl & 0x00000000FFFFFFFF; // take the 32 lowest bits of x
var xhigh = xl >> 32; // take the 32 highest bits of x
var ylow = yl & 0x00000000FFFFFFFF; // take the 32 lowest bits of y
var yhigh = yl >> 32; // take the 32 highest bits of y
// perform multiplications
var lowlow = xlow * ylow;
var lowhigh = xlow * yhigh;
var highlow = xhigh * ylow;
var highhigh = xhigh * yhigh;
// take the highest bits of lowlow and the lowest of highhigh
var loResult = lowlow >> 32;
var midResult1 = lowhigh;
var midResult2 = highlow;
var hiResult = highhigh << 32;
// add everything together and build result
var finalResult = loResult + midResult1 + midResult2 + hiResult;
return new Fix64(finalResult); // this constructor just copies the parameter into m_rawValue
}
Dies funktioniert im allgemeinen Fall, aber scheitert in einer Reihe von Szenarien. Das Ergebnis ist nämlich um 1.0 (Dezimalwert) ausgeschaltet, oft für extrem kleine oder große Werte der Operanden. Hier sind einige Ergebnisse aus meinen Unit-Tests (FromRaw() sind eine Methode, die eine Fix64 direkt von einem Long-Wert aufbaut, ohne es zu verschieben):
Failed for FromRaw(-1) * FromRaw(-1): expected 0 but got -1
Failed for FromRaw(-4) * FromRaw(6791302811978701836): expected -1.4726290525868535041809082031 but got -2,4726290525868535041809082031
Failed for FromRaw(2265950765) * FromRaw(17179869183): expected 2.1103311001788824796676635742 but got 1,1103311001788824796676635742
Ich versuche, die Logik dieses auf dem Papier zu arbeiten, aber ich bin ein bisschen fest. Wie kann ich das beheben?
Was machst du mit den Übertragsbits? Außerdem verstehe ich die Übersetzung nicht ganz. Was ist der äquivalente numerische Wert von '2265950765'? – mellamokb
Ja, was ist mit den Übertragsbits? –
Ich bin nicht vertraut mit C# 's Integer-Promotion-Regeln - sind die Werte 'Lowlow' usw. 32-Bit, oder gibt 32x32-Multiplikation automatisch ein 64-Bit-Ergebnis? – hobbs