所以我的目标是拥有一个签名如下的函数:
template<typename int_t, int_t numerator, int_t denominator>
int_t Multiply(int_t x);
类型是一个整数类型,它既是一个参数的类型,也是返回类型。
另外两个模板参数是分数的分子和分母。
此函数的目标是将数字“x”乘以任意分数,该分数位于两个模板值中。一般来说答案应该是:
floor(x*n/d) mod (int_t_max+1)
这种天真的方法是首先将“x”乘以分子,然后除以。
查看具体案例,可以说int_t
= uint8_t
,“x”为30,分子和分母分别为119和255.
采用这种天真的路线失败是因为(30 * 119)mod 256 = 242,除以255然后被覆盖为0.真正的答案应该是14。
下一步是为中间值使用更大的整数。所以不是在mod 256中进行30 * 119计算,而是在mod 65536中进行。这在某种程度上有效,但是当我们尝试在Multiply
函数中使用最大整数时,它会失败。 / p>
下一步是只使用一些BigInt
类型来保存值,使无法溢出。这也可行,但是获得模板参数的全部原因是,这可能非常快,并且使用BigInt
可能会失败。
所以这就是问题:
是否有一个算法只涉及移位,乘法,除法,加法,减法和余数运算符,可以预先形成这个数学函数,而不会引起溢出问题?
答案 0 :(得分:0)
对于Windows平台,我建议您查看large integers上的这篇文章,该文章目前包含对最多128位整数值的支持。您可以根据int_t
的位与您的模板进行专门化,以作为这些操作系统功能的代理。
为乘法实现“shift-and-add”可能提供了一个足够好的替代方案,但是一个部门肯定会否定你可能希望的任何性能提升。
然后有“快捷方式”,例如试图查看numerator
和denominator
是否可以通过减少分数来简化,例如乘以35/49与乘以5/7相同。
另一个想到的选择是“逐渐”乘以“分数”。这个需要一些解释:
假设您乘以1234567/89012
。我将使用十进制表示法来表示可读性,但同样适用于(自然地)二进制数学。
所以我们所拥有的是一个值x
,需要乘以该分数。因为我们正在处理整数运算,所以让我们重新包装那个分数:
1234567/89012 = A + B/10 + C/100 + D/1000...
= 1157156/89012 + ((71210*10)/89012)/10 + ((5341*100)/89012)/100 + ((801*1000)/89012)/1000...
= 13 + 8/10 + 6/100 + 9/1000...
事实上,在这一点上,你的主要问题是“我在计算中有多精确?”。根据该问题的答案,您将拥有该长序列的适当数量的成员。
这将为您提供所需的精度,并为计算产品提供通用的“无溢出”方法,但计算成本是多少?