に基づいて、C#で64ビットの固定小数点符号付き31.32数値型を実装していlong
ます。これまでのところ、足し算と引き算に適しています。しかし、掛け算には、私が解決しようとしている厄介なケースがあります。
私の現在のアルゴリズムは、各オペランドを最上位および最下位の32ビットに分割し、4つの乗算を4つのロングに実行し、これらのロングの関連ビットを加算することで構成されています。ここにそれはコードにあります:
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
}
これは一般的なケースでは機能しますが、多くのシナリオで失敗します。つまり、結果は1.0(10進値)ずれています。多くの場合、オペランドの値が極端に小さいか大きい場合です。これが私の単体テストの結果です(FromRaw()は、長い値からシフトせずに直接Fix64を構築するメソッドです):
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
私は紙の上でこれの論理を解明しようとしていますが、私は少し立ち往生しています。どうすればこれを修正できますか?