这个乘除函数是否正确
Is this multiply-divide function correct?
我试图避免在某些计算中long long
s和整数溢出,所以我想出了下面的函数来计算(a * b) / c
(由于截断整数除法,顺序很重要)。
unsigned muldiv(unsigned a, unsigned b, unsigned c)
{
return a * (b / c) + (a * (b % c)) / c;
}
是否有任何边缘情况无法按预期工作?
编辑:这对于原始明显逻辑正确的超集值是正确的。如果c
> b
,它可能在其他条件下,它仍然不会给你买任何东西。也许您对自己的c
价值观有所了解,但这可能没有您期望的那么多帮助。a
、b
、c
的某些组合仍然会溢出。
编辑:假设您出于严格的C++98可移植性原因避免long long
,您可以通过将unsigned
提升到恰好具有整数值进行数学运算的double
来获得大约52位的精度。 使用double
数学实际上可能比做三个整数除法更快。
这在很多情况下都失败了。 最明显的是当a
很大时,a * (b % c)
溢出。 在这种情况下,您可以尝试交换a
和b
,但如果a
、b
和c
都很大,这仍然会失败。 考虑 a
= b
= 2^25-1 和 c = 2^24,其中 32 位无符号。 正确的结果是 2^26-4,但 a * (b % c)
和 b * (a % c)
都会溢出。 甚至(a % c) * (b % c)
也会溢出。
的最简单方法是有一个加宽的乘法,这样你就可以得到更高精度的中间产品。 如果你没有这个,你需要用较小的乘法和除法来合成它,这与实现你自己的 biginteger 库几乎是一回事。
如果您可以保证c
足够小,以至于(c-1)*(c-1)
不会溢出未签名的,则可以使用:
unsigned muldiv(unsigned a, unsigned b, unsigned c) {
return (a/c)*(b/c)*c + (a%c)*(b/c) + (a/c)*(b%c) + (a%c)*(b%c)/c;
}
这实际上会给你所有a和b的"正确"答案 - (a * b)/c % (UINT_MAX+1)
为了避免溢出,你必须预先除法,然后乘以某个因素。
最好的使用因子是 c,只要 a 和 b 中的一个(或两个)大于 c。 这就是克里斯·多德的函数所做的。 它的最大中间体为((a % c) * (b % c)),正如Chris所指出的,它小于或等于((c-1)*(c-1))。
如果你遇到a和b都小于c的情况,但(a * b)仍然可能溢出(当c接近单词大小的限制时可能是这种情况),那么最好的使用因素是2的大幂,将乘法和除法转换为移位。 尝试将单词大小移半。
请注意,使用预除法然后后乘法等效于在没有更长单词可用时使用较长的单词。 假设您不丢弃低阶位,而只是将它们添加为另一个术语,那么您只是使用几个单词而不是一个较大的单词。
我会让你填写代码。
- 在C++STL中是否有Polyval(Matlab函数)等价物?
- 是否有C++编译器选项允许激进地删除所有函数调用,并将参数传递给具有空体的函数
- 是否有类似std::lower_bound的函数,而不需要排序/分区输入
- 函数作为模板参数,是否对返回类型强制约束
- visual是否可以在c++中创建一个接收无限数量相同类型(或至少相当数量)参数的函数
- 编译器如何在使用SFINAE的函数和标准函数之间确定两者是否可行
- 函数是否可以访问传递给main()的参数
- 是否可以将llvm::FunctionType转换为C/C++原始函数指针
- 在这种情况下,java对象是否可以调用本机函数
- 检查函数返回类型是否与STL容器类型值相同
- 根据某个函数是否存在启用模板
- 在C++中,使用带有 std::optional 参数的函数<T>来表示可选参数是否有意义?
- 无论如何,我可以确定构造函数是否存在吗?
- 是否可以将函数导入命名空间,但不能导出它?
- 返回指向对象的指针的函数调用是否为 prvalue?
- 是否可以依赖函数范围的静态变量来执行程序关闭期间调用的方法?
- 重载运算符的范围是什么?它是否会影响作为类成员的集合的插入函数?
- 是否有任何建议来统一函数类型限定符并简化可恶的函数类型?
- 在函数范围内在堆栈上分配的数组在离开函数时是否总是被释放?
- 表达式 SFINAE:如何根据类型是否包含具有一个或多个参数的函数来选择模板版本