6

に基づいて、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

私は紙の上でこれの論理を解明しようとしていますが、私は少し立ち往生しています。どうすればこれを修正できますか?

4

2 に答える 2

5

アルゴリズムは健全に見えます、そしてそれは「紙の上で」それを解決しました、そしてそれは正しいようです。これが私の作成したメモですFromRaw(2265950765) * FromRaw(17179869183)(0.52758277510292828083038330078125 * 3.99999999976716935634613037109375 = 2.11033110017888247966766357421875)

x1 = 2265950765
y1 = 17179869183

xlow = 2265950765
xhigh = 0
ylow = 4294967295
yhigh = 3

lowlow = 9732184427755230675
lowhigh = 6797852295
highlow = 0
highhigh = 0

loResult = 2265950764
midResult1 = 6797852295
midResult2 = 0
hiResult = 0

finalResult = 9063803059

これが私が起こっていると私が思うことです:結果が正しく出てくるためにはlowlow 必要ulongですが、あなたが得ているのは符号付きの値だと思います。署名されたものとして解釈され、lowlow最終的に-8714559645954320941(2 ^ 64で低すぎる)、- loResult2029016532(2 ^ 32で低すぎる)、finalResult47688357632 ^ 32で低すぎる)になり、結果の値は次のようになります。次に、1.11033110017888247966766357421875は、予想よりも正確に1つ少なくなります。

一般に、値は符号付きの「上半分」と符号なしの「下半分」を持つものとして扱われる必要があります。highhigh署名されている*署名されている=署名されている; lowhigh符号付きhighlow*符号なし=符号付き; ただし、lowlow符号なし* unsigned=unsignedです。

于 2012-12-24T22:47:52.327 に答える
0

わからない、なぜFromRaw(-1) * FromRaw(-1)帰らなきゃいけないの0?戻るはずです+1

一般的にアルゴリズムについて:分割せず、longsを乗算するだけです。

乗算するとします2.3*4.5。あなたは得るでしょう10.35。しかし、乗算23*45すると、が得られ1035ます。

数字は同じです!

したがって、数値を乗算するには、m_rawValuesを乗算してから、ビットを右にシフトする必要があります。

于 2012-12-24T22:19:21.983 に答える