通常需要除以整数,但要将结果四舍五入而不是向下舍入。有一段时间了,我一直在使用以下函数来表达这个迷你习语:
template <typename S, typename T>
constexpr inline S div_rounding_up(const S& dividend, const T& divisor)
{
return (dividend + divisor - 1) / divisor;
}
这至少有以下缺陷,或者可能被视为缺陷:
div_rounding_up
- 让这种函数远离零可能更有意义,因为x / y
为负{ {1}}和正x
向零舍入。换句话说,也许我应该实现的是y
,它与倒置是可交换的:让我们div_rounding_away_from_zero
auto f = [&y](const S& x) { return div_rounding_away_from_zero(x,y); }
。f(-x) == -f(x)
。S
时可能会出现奇怪的行为。虽然你可以很容易地想出解决这些方法的方法,但是它们会导致其他可能的缺陷,例如代码中的条件,或者依赖于计算可能很昂贵的模数。
有没有“正确的方法”来实现这个? “正确”是指在语义上令人愉悦,高效,避免上述许多缺陷,并希望广泛使用。
备注:
sizeof(S) > sizeof(T)
和变体是个好主意吗?答案 0 :(得分:1)
sizeof(S) > sizeof(T)
时可能会出现奇怪的行为。
使用单一类型参数可能会更好,并让用户处理他们想要的转换。这是标准库数学函数使用的方法。
在S域的末尾附近溢出。
基于余数的舍入没有这个问题。
依赖于计算可能很昂贵的模量。
你已经在计算一个部门,这很贵。至少在x86上,除法指令将余数存储在寄存器中,std::div
的合适实现将使用它。现代编译器甚至可以优化显式使用除法和余数运算。
在这里使用
std::div
和变体是个好主意吗?
不确定
如果您认为该函数应严格遵循仅适用于非负参数,请说明。
我认为你至少应该要求参数必须具有相同的符号。划分和余数运算符的舍入方向(自C ++ 11以来也是扩展std::div
)是实现定义的。根据这个要求,从零开始舍入和四舍五入之间没有区别,因为没有支持的结果是否定的。
template <typename T> // single type argument
constexpr T // constexpr implies inline
div_round_up
(const T& dividend, const T& divisor)
{
// no overflows, only 1 expensive operation
std::div_t dv = std::div(dividend, divisor);
return dv.quot + (!!dv.rem);
}