简体   繁体   English

64位定点乘法错误

[英]64-bit fixed-point multiplication error

I'm implementing a 64-bit fixed-point signed 31.32 numeric type in C#, based on long . 我正在C#中基于long实现64位定点带符号的31.32数字类型。 So far so good for addition and substraction. 到目前为止,对于加法和减法都很好。 Multiplication however has an annoying case I'm trying to solve. 但是,乘法有一个令人烦恼的案例,我正在尝试解决。

My current algorithm consist of splitting each operand into its most and least significant 32 bits, performing 4 multiplications into 4 longs and adding the relevant bits of these longs. 我当前的算法包括将每个操作数分成最高和最低有效32位,对4个long执行4次乘法,然后将这些long的相关位相加。 Here it is 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
}

This works in the general case but fails in a number of scenarios. 在一般情况下,此方法有效,但在许多情况下均失败。 Namely, the result is off by 1.0 (decimal value), often for extremely small or large values of the operands. 即,对于操作数的值非常小或很大,结果通常相差1.0(十进制值)。 Here are some results from my unit tests (FromRaw() is a method that builds a Fix64 directly from a long value, without shifting it): 这是我的单元测试的一些结果(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

I'm trying to work out the logic of this on paper but I'm a bit stuck. 我试图在纸上弄清楚这一点的逻辑,但我有些困惑。 How can I fix this? 我怎样才能解决这个问题?

The algorithm looks sound, and it worked it out "on paper" and it seems right. 该算法看起来不错,并且可以“在纸上”解决,而且看起来很正确。 Here are my worked out notes for FromRaw(2265950765) * FromRaw(17179869183) (0.52758277510292828083038330078125 * 3.99999999976716935634613037109375 = 2.11033110017888247966766357421875) 这是我针对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

Now here's what I suspect is happening: lowlow needs to be a ulong for the result to come out right, but I think that what you're getting is a signed value. 现在,这里是我怀疑正在发生的事情: lowlow 需要ulong的结果出来的权利,但我认为,你得到是一个符号值。 Interpreted as signed, lowlow ends up being -8714559645954320941 (too low by 2^64), loResult ends up being -2029016532 (too low by 2^32), finalResult ends up being 4768835763 (also too low by 2^32), and the resulting value is then 1.11033110017888247966766357421875 which is exactly 1 less than you expect. 解释为签署, lowlow最终被-8714559645954320941(过低除以2 ^ 64), loResult最终被-2029016532(过低由2 ^ 32), finalResult结束是4768835763 (也太低除以2 ^ 32),和则得出的值为1.1103311001788824796676635742121875,比您的预期小1。

In general your values should be treated as having a signed "upper half" and an unsigned "lower half". 通常,应将您的值视为带符号的“上半部”和无符号的“下半部”。 highhigh is signed * signed = signed; highhigh是带符号的*带符号的=带符号的; lowhigh and highlow are signed * unsigned = signed; lowhighhighlow是带符号的* unsigned =带符号的; but lowlow is unsigned * unsigned = unsigned. 但是lowlow是无符号的* unsigned =无符号的。

I don't understand, why FromRaw(-1) * FromRaw(-1) should return 0 ? 我不明白,为什么FromRaw(-1) * FromRaw(-1)应该返回0 It should return +1 它应该返回+1

Generally about algorithm: don't split, just multiply long s. 通常关于算法:不要拆分,只需乘以long s。

Suppose you multiply 2.3*4.5 . 假设您乘以2.3*4.5 You will get 10.35 . 您将得到10.35 But if you multiply 23*45 , you will get 1035 . 但是,如果乘以23*45 ,将得到1035

The figures are the same! 数字是一样的!

So, to multiply your numbers, you should multiply m_rawValue s and then shift bits right. 因此,要乘以数字,应乘以m_rawValue ,然后向右移位。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM