我知道,我知道,人们可能会说“只是切换到浮点”,但由于我正在研究的项目的性质,目前这不是一个选项。我正在帮助用C ++编写一种编程语言,我目前很难尝试获得一个非常精确的乘法算法,我有一个VM,主要是mod / smod,div / sdiv的操作(即签名数字不是这里的关注点),mul,一个完全分数的减半数和一个推动的移位数,我乘以并除以得到我的移位。为简单起见,假设我正在使用32字节的空间。我的算法适用于几乎涉及整数的任何事情,只是当我的小数部分超过16个字节时,我遇到精度问题,如果我要绕它,数字会相当准确,但我想要它尽可能准确,甚至愿意牺牲一点性能,只要它保持固定点并且不进入浮点土地。我关心的算法将以某种伪代码映射出来。我喜欢任何有关如何使这更好的见解,或任何推理,为什么通过计算科学的法则,我要求的是一个徒劳无功的努力。
对于完全小数(所有字节都是小数):
A = num1 / halfShift //truncates the number down to 16 so that when multiplied, we get a full 32 byte num
B = num2 / halfShift
finalNum = A * B
对于大于16字节的其余数字,我使用此算法:
this algorithm can essentially be broken down into the int.frac form
essentially A.B * C.D taking the mathematic form of
D*B/shift + C*A*shift + D*A + C*B
if the fractional numbers are larger than the integer, I halve them, then multiply them together in my D*B/shift
just like in the fully fractional example above
我应该注意某种“神奇”的舍入方法吗?请告诉我。
答案 0 :(得分:2)
产品的小数位数等于操作数中小数位数的总和。您必须执行乘法到该精度,然后根据所需的目标精度进行舍入或截断。
答案 1 :(得分:2)
如果先进行乘法运算,然后按比例缩放,则可获得最准确的结果。当然这意味着,您需要将乘法结果存储在64位int类型中。 如果这不是一个选项,那么提前转换的方法是有道理的。但你肯定会失去精确度。
无论哪种方式,如果你进行圆形而不是截断,你可以稍微提高一点。
我支持阿空加瓜的建议,以舍入到最近的。 为此,您需要在应用除法之前添加将被截断的最高位。
在你的情况下看起来像这样:
A = (num1 + 1<<(halfshift-1)) >> halfshift
B = (num2 + 1<<(halfshift-1)) >> halfshift
finalNum = A * B
修改强>
如何根据因子的值动态缩放因子和结果的示例(这可以提高分辨率,从而提高结果的准确性):
需要设置shiftA和shiftB,使A和B各为16字节小数,因此32字节结果不会溢出。如果事先不知道shiftA和shiftB,可以通过计算num1和num2的前导零来确定。
A = (num1 + 1<<(shiftA-1)) >> shiftA
B = (num2 + 1<<(shiftB-1)) >> shiftB
finalNum = (A * B) >> (fullshift - (shiftA + shiftB))