[英]64-bit fixed-point multiplication error
我正在C#中基于long
实现64位定点带符号的31.32数字类型。 到目前为止,对于加法和减法都很好。 但是,乘法有一个令人烦恼的案例,我正在尝试解决。
我当前的算法包括将每个操作数分成最高和最低有效32位,对4个long执行4次乘法,然后将这些long的相关位相加。 它在代码中:
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(十进制值)。 这是我的单元测试的一些结果(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
我试图在纸上弄清楚这一点的逻辑,但我有些困惑。 我怎样才能解决这个问题?
该算法看起来不错,并且可以“在纸上”解决,而且看起来很正确。 这是我针对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), loResult
最终被-2029016532(过低由2 ^ 32), finalResult
结束是4768835763
(也太低除以2 ^ 32),和则得出的值为1.1103311001788824796676635742121875,比您的预期小1。
通常,应将您的值视为带符号的“上半部”和无符号的“下半部”。 highhigh
是带符号的*带符号的=带符号的; lowhigh
和highlow
是带符号的* unsigned =带符号的; 但是lowlow
是无符号的* unsigned =无符号的。
我不明白,为什么FromRaw(-1) * FromRaw(-1)
应该返回0
? 它应该返回+1
通常关于算法:不要拆分,只需乘以long
s。
假设您乘以2.3*4.5
。 您将得到10.35
。 但是,如果乘以23*45
,将得到1035
。
数字是一样的!
因此,要乘以数字,应乘以m_rawValue
,然后向右移位。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.