64位定点乘法错误

Asi*_*sik 5 c# math binary fixed-point

我正在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
}
Run Code Online (Sandbox Code Playgroud)

这在一般情况下有效,但在许多情况下都失败了.也就是说,结果偏离1.0(十进制值),通常用于极小或大的操作数值.以下是我的单元测试的一些结果(FromRaw()是一种直接从long值构建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
Run Code Online (Sandbox Code Playgroud)

我试图在纸上弄清楚这个逻辑,但我有点卡住了.我怎样才能解决这个问题?

hob*_*bbs 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
Run Code Online (Sandbox Code Playgroud)

现在这就是我怀疑正在发生的事情:lowlow 需要成为一个ulong正确的结果,但我认为你得到的是一个有符号的价值.解释为签名,lowlow最终为-8714559645954320941(太低了2 ^ 64),loResult最终为-2029016532(太低了2 ^ 32),finalResult最终成为4768835763(也太低了2 ^ 32),结果值为然后1.11033110017888247966766357421875这比您预期的少1.

通常,您的值应被视为具有签名的"上半部分"和未签名的"下半部分".highhigh签名*签名=签名; lowhigh并且highlow签名*unsigned =签名; 但是lowlow是无符号的*unsigned = unsigned.