我正在基于long
在C#中实现64位定点签名31.32数字类型。到目前为止,添加和减法都很好。然而,乘法有一个我想要解决的烦人案例。
我当前的算法包括将每个操作数分成最高和最低有效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(十进制值),通常用于极小或大的操作数值。以下是我的单元测试的一些结果(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
我正试图在纸上弄清楚这个逻辑,但我有点卡住了。我该如何解决这个问题?
答案 0 :(得分: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),loResult
最终为-2029016532(太低了2 ^ 32),finalResult
最终成为4768835763
(也太低了2 ^ 32),结果值是1.11033110017888247966766357421875,这比你预期的要少1。
通常,您的值应被视为具有签名的“上半部分”和未签名的“下半部分”。 highhigh
已签名*已签名=已签名; lowhigh
和highlow
已签名* unsigned =已签名;但是lowlow
是无符号的* unsigned = unsigned。
答案 1 :(得分:0)
我不明白,为什么FromRaw(-1) * FromRaw(-1)
应该返回0
?它应该返回+1
一般来说算法:不要拆分,只需乘以long
s。
假设您乘以2.3*4.5
。你会得到10.35
。但如果你乘以23*45
,你将获得1035
。
数字是一样的!
所以,要乘以你的数字,你应该乘以m_rawValue
s然后向右移位。