【问题标题】:What's the right way to implement integer division-rounding-up?实现整数除法舍入的正确方法是什么?
【发布时间】:2017-06-11 18:29:13
【问题描述】:

需要对整数进行除法但将结果向上取整而不是向下取整是很常见的。一段时间以来,我一直在为那个小成语使用以下函数:

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 用于负值 x 和正值y 向零舍入。换句话说,也许我应该实现的是div_rounding_away_from_zero,它可以与反转交换:让auto f = [&amp;y](const S&amp; x) { return div_rounding_away_from_zero(x,y); } 我们将有f(-x) == -f(x)
  • S 的域末尾附近溢出。
  • sizeof(S) &gt; sizeof(T) 时可能出现奇怪的行为。
  • 长函数名...

虽然您可以很容易地想到解决这些问题的方法,但它们会导致其他可能的缺陷,例如代码中的条件,或者依赖于计算可能很昂贵的模数。

那么有没有“正确的方法”来实现它?我所说的“正确”是指语义上令人愉悦、高效、避免上述许多缺陷,并有望被广泛使用。

注意事项:

  • 如果您认为该函数应严格限制为只能使用非负参数,请说出来。恕我直言,这有点问题,因为我们不想将类型限制为无符号,而且我们不想检查操作数的符号。这似乎是我会使用联系人的东西 - 而 C++ 还没有。
  • 在这里使用std::div 和变体是个好主意吗?
  • 性能比可读性更重要。最坏的情况是可以添加评论。
  • 代码不应是特定于单一架构的(但如果您想为不同的架构使用 ifdef-else,那么请做我的客人)。
  • 代码不应假定特定的编译器。

【问题讨论】:

  • 不需要将除数和除数相加:将除数分配到该总和中。
  • @Bathsheba 如果我理解你的话,你的建议是否适用于整数除法?
  • 对于不同类型的问题,也许你可以对两种类型都使用T,并要求调用者传递相同的类型
  • @A.s.h.是的,因为你会从表达式中取出一个整数。
  • @M.M:我没这么说。只是对于负操作数,我想要/必须这样做是非常罕见的。

标签: c++ integer-division idioms rounding


【解决方案1】:

sizeof(S) &gt; 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);
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-11-06
    • 2018-08-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多