舍入整数除法(而不是截断)

Dav*_*ave 67 c math int rounding integer-division

我很想知道如何将数字舍入到最接近的第十个整数.例如,如果我有:

int a = 59 / 4;
Run Code Online (Sandbox Code Playgroud)

以浮点计算的14.75; 如何将数字存储为"a"中的15?

Jon*_*ler 120

整数舍入的标准习语是:

int a = (59 + (4 - 1)) / 4;
Run Code Online (Sandbox Code Playgroud)

您将除数减一加到被除数.

  • 呃...那么你必须考虑......添加(n - 1)/ 2,或多或少.对于n == 4,您希望x%n∈{0,1}向下舍入,并且x%n∈{2,3}向上舍入.所以,你需要加2,即n/2.对于n == 5,你希望x%n∈{0,1,2}向下舍入,x%n∈{3,4}向上舍入,所以你需要再加2 ...因此:`int i =(x +(n/2))/ n;`? (9认同)
  • (原始)标题和问题要求两个不同的东西.标题说四舍五入(这是你已经回答的),但身体说到最接近(这是接受的答案尝试). (8认同)
  • 如果你想进行数学轮次(14.75到15,14.25到14)怎么办? (7认同)
  • 这种方法适用于积极的`int`.但如果除数或红利是负数,则会产生错误的答案.对@caf的提示也不起作用. (6认同)
  • 请注意`c =(INT_MAX +(4 - 1))/ 4;`由于整数溢出而给出`c = -536870911` ... (4认同)

0xC*_*ACE 46

int a = 59.0f / 4.0f + 0.5f;
Run Code Online (Sandbox Code Playgroud)

这仅在分配给int时有效,因为它会丢弃'.'之后的任何内容.

编辑: 此解决方案仅适用于最简单的情况.一个更强大的解决方案是:

unsigned int round_closest(unsigned int dividend, unsigned int divisor)
{
    return (dividend + (divisor / 2)) / divisor;
}
Run Code Online (Sandbox Code Playgroud)

  • 请注意,这是FLOATING指针解决方案.我建议你使用整数运算有很多原因. (20认同)
  • 因为在没有FPU的系统上,这会产生非常非常糟糕的代码. (9认同)
  • -1,当sizeof(int)> = sizeof(float)时,这给许多值提供了错误的答案.例如,32位浮点数使用一些位来表示指数,因此它不能精确地表示每个32位int.因此,12584491/3 = 4194830.333 ...,应该向下舍入到4194830,但是,在我的机器上,它不能完全代表12584491浮点数,上面的公式计算结果为4194831,这是错误的.使用double更安全. (7认同)
  • 那就是问题所在.OP的问题是可以解决的,根本不使用任何浮点数,因此不依赖于FPU支持存在或良好.此外,在大多数架构上,包括那些具有非常出色的FPU支持的架构,也更快(在大量这些需要计算的情况下).另请注意,对于较大的数字,您的解决方案可能会出现问题,其中浮点数无法准确表示给定的整数值. (5认同)
  • 真正的问题是为什么你想将这样的值表示为整数类型。double 保存最大 2^53 的完美整数,并允许您轻松指定如何对错误进行舍入。 (2认同)

eri*_*cbn 44

适用于任何红利和除数符号的代码:

int divRoundClosest(const int n, const int d)
{
  return ((n < 0) ^ (d < 0)) ? ((n - d/2)/d) : ((n + d/2)/d);
}
Run Code Online (Sandbox Code Playgroud)

如果您更喜欢宏:

#define DIV_ROUND_CLOSEST(n, d) ((((n) < 0) ^ ((d) < 0)) ? (((n) - (d)/2)/(d)) : (((n) + (d)/2)/(d)))
Run Code Online (Sandbox Code Playgroud)

Linux内核宏DIV_ROUND_CLOSEST不适用于负除数!

  • 除了min/max int附近的`int`值,这是迄今为止最好的解决方案. (6认同)

Way*_*neJ 22

你应该使用这样的东西:

int a = (59 - 1)/ 4 + 1;
Run Code Online (Sandbox Code Playgroud)

我假设你真的想做一些更通用的事情:

int divide(x, y)
{
   int a = (x -1)/y +1;

   return a;
}
Run Code Online (Sandbox Code Playgroud)

x +(y-1)有可能溢出,给出错误的结果; 然而,如果x = min_int,x - 1只会下溢...

  • 如果x = 0,则这不起作用.如果x = 0,则x/y向上舍入的预期结果为0.然而,此解决方案产生的结果为1.另一个解决方案得出正确的答案. (8认同)
  • @nad2000为什么要2.0333 ..圆形_up_为2? (3认同)
  • (被否决,因为作者指出这是不正确的) (2认同)
  • 如果这个答案是 qrong 为什么你不删除它呢? (2认同)

Way*_*neJ 11

(已编辑)使用浮点舍入整数是解决此问题的最简单方法; 但是,根据问题集可能是可能的.例如,在嵌入式系统中,浮点解决方案可能成本太高.

使用整数数学做这件事结果有点难,有点不直观.第一个发布的解决方案对于我使用它的问题没有问题,但是在整数范围内表征结果后,结果总体上非常糟糕.通过几本关于钻头和嵌入式数学的书籍回顾几乎没有什么结果.几个笔记.首先,我只测试了正整数,我的工作不涉及负分子或分母.第二,32位整数的详尽测试是计算禁止的,所以我从8位整数开始,然后确保我得到类似的16位整数结果.

我从之前提出的2个解决方案入手:

#define DIVIDE_WITH_ROUND(N, D) (((N) == 0) ? 0:(((N * 10)/D) + 5)/10)

#define DIVIDE_WITH_ROUND(N, D) (N == 0) ? 0:(N - D/2)/D + 1;

我的想法是,第一个版本会溢出大数字,第二个版本下溢小数字.我没有考虑两件事.1.)第二个问题实际上是递归的,因为要得到正确的答案你必须正确地围绕D/2.2.)在第一种情况下,你经常溢出然后下溢,两者相互抵消.这是两个(不正确)算法的错误图:除以Round1 8位x =分子y =分母

该图显示第一种算法仅对小分母(0 <d <10)不正确.没想到它实际上处理 numerators比第二版更好.

这是第二个算法的图: 8位有符号数第二算法.

正如预期的那样,它对于小分子来说是失败的,但是对于比第一个版本更大的分子也是如此.

显然,这是正确版本的更好起点:

#define DIVIDE_WITH_ROUND(N, D) (((N) == 0) ? 0:(((N * 10)/D) + 5)/10)

如果你的分母大于10,那么这将正常工作.

D == 1需要一个特殊情况,只需返回N. D == 2,= N/2 +(N&1)//需要特殊情况//如果奇数则向上舍入.

一旦N变得足够大,D> = 3也会出现问题.事实证明,较大的分母只有较大分子的问题.对于8位有符号数,问题点是

if (D == 3) && (N > 75))
else if ((D == 4) && (N > 100))
else if ((D == 5) && (N > 125))
else if ((D == 6) && (N > 150))
else if ((D == 7) && (N > 175))
else if ((D == 8) && (N > 200))
else if ((D == 9) && (N > 225))
else if ((D == 10) && (N > 250))

(返回这些的D/N)

所以通常情况下,特定分子变坏的pointe就在某处
N > (MAX_INT - 5) * D/10

这不完全但很接近.当使用16位或更大的数字时,如果您对这些情况进行C除(截断),则误差<1%.

对于16位带符号的数字,测试将是

if ((D == 3) && (N >= 9829))
else if ((D == 4) && (N >= 13106))
else if ((D == 5) && (N >= 16382))
else if ((D == 6) && (N >= 19658))
else if ((D == 7) && (N >= 22935))
else if ((D == 8) && (N >= 26211))
else if ((D == 9) && (N >= 29487))
else if ((D == 10) && (N >= 32763))

当然对于无符号整数,MAX_INT将被MAX_UINT替换.我确信有一个确切的公式可以确定对特定D和位数有效的最大N,但我没有时间处理这个问题...

(我现在似乎错过了这个图,我将在稍后编辑和添加.)这是8位版本的图表,上面有特殊情况:![8位特殊情况签名0 < N <= 10 3

注意,对于8位,图中所有错误的误差为10%或更小,16位<0.1%.

  • 你是对的。第一个宏是不正确的(我通过困难的方式发现了这一点。)事实证明这比我预期的更难。我更新了帖子,承认不正确并包含一些进一步的讨论。 (2认同)

Chr*_*utz 7

如上所述,您正在执行整数运算,它会自动截断任何小数结果.要执行浮点运算,请将常量更改为浮点值:

int a = round(59.0 / 4);
Run Code Online (Sandbox Code Playgroud)

或者将它们转换为float或其他浮点类型:

int a = round((float)59 / 4);
Run Code Online (Sandbox Code Playgroud)

无论哪种方式,您都需要使用标头中的round()函数进行最后的舍入math.h,因此请务必#include <math.h>使用兼容C99的编译器.


Mag*_*ron 5

#define CEIL(a, b) (((a) / (b)) + (((a) % (b)) > 0 ? 1 : 0))
Run Code Online (Sandbox Code Playgroud)

另一个有用的宏(必须有):

#define MIN(a, b)  (((a) < (b)) ? (a) : (b))
#define MAX(a, b)  (((a) > (b)) ? (a) : (b))
#define ABS(a)     (((a) < 0) ? -(a) : (a))
Run Code Online (Sandbox Code Playgroud)

  • 当然,这看起来像是 LISP 的一个糟糕情况,但省略每个参数周围的括号,然后评估 ABS(4 &amp; -1) 会更糟糕。 (13认同)
  • 他想要的是“ROUND”,而不是“CEIL” (5认同)
  • 你的括号让我头晕。 (2认同)

小智 5

从Linux内核(GPLv2):

/*
 * Divide positive or negative dividend by positive divisor and round
 * to closest integer. Result is undefined for negative divisors and
 * for negative dividends if the divisor variable type is unsigned.
 */
#define DIV_ROUND_CLOSEST(x, divisor)(          \
{                           \
    typeof(x) __x = x;              \
    typeof(divisor) __d = divisor;          \
    (((typeof(x))-1) > 0 ||             \
     ((typeof(divisor))-1) > 0 || (__x) > 0) ?  \
        (((__x) + ((__d) / 2)) / (__d)) :   \
        (((__x) - ((__d) / 2)) / (__d));    \
}                           \
)
Run Code Online (Sandbox Code Playgroud)

  • @chux:[这是GCC扩展名](http://tigcc.ticalc.org/doc/gnuexts.html#SEC69)。它不是标准C的一部分。 (4认同)

Gab*_*les 5

自 2023 年 10 月否决以来,该答案已完全重写。

最受欢迎的答案显示了正确的原理,但仅适用于向上舍入,而不是向下舍入或舍入到最接近的值,并且仅适用于正数。

这些是一些很大的限制,所以我将在此处添加一个更完整的答案,以向上舍入(远离零)、向下(向零)或最接近的舍入,并且它将处理正数负数(在分子中) 、分母或两者)。它也适用于 C 和 C++,为了完整起见,我将添加 C++ 中的模板版本。

正数和负数的整数除法(向上/远离零、向下/向零或到最接近的值)

有关我的完整代码,请参阅c/rounding_integer_division我的eRCaGuy_hello_world存储库中的文件夹。特别参见该rounding_integer_division.cpp文件。使用 运行单元测试./run_tests.sh

1.仅代码

// -----------------------------------------------------------------------------
// 1. Macros
// -----------------------------------------------------------------------------

#define DIVIDE_ROUND_AWAY_FROM_ZERO(numer, denom) DIVIDE_ROUNDUP((numer), (denom))
#define DIVIDE_ROUNDUP(numer, denom) (                                                  \
    ((numer) < 0) != ((denom) < 0) ?                                                    \
    (numer) / (denom) :                                                                 \
    ((numer) + ((denom) < 0 ? (denom) + 1 : (denom) - 1)) / (denom)                     \
)

#define DIVIDE_ROUND_TOWARDS_ZERO(numer, denom) DIVIDE_ROUNDDOWN((numer), (denom))
#define DIVIDE_ROUNDDOWN(numer, denom) (                                                \
    ((numer) < 0) != ((denom) < 0) ?                                                    \
    ((numer) - ((denom) < 0 ? (denom) + 1 : (denom) - 1)) / (denom) :                   \
    (numer) / (denom)

#define DIVIDE_ROUNDNEAREST(numer, denom) (                                             \
    ((numer) < 0) != ((denom) < 0) ?                                                    \
    ((numer) - ((denom)/2)) / (denom) :                                                 \
    ((numer) + ((denom)/2)) / (denom)                                                   \
)

// -----------------------------------------------------------------------------
// 2. Statement Expressions (safer than macros)
// -----------------------------------------------------------------------------

#define DIVIDE_ROUND_AWAY_FROM_ZERO2(numer, denom) DIVIDE_ROUNDUP2((numer), (denom))
#define DIVIDE_ROUNDUP2(numer, denom)                                           \
    ({                                                                          \
        __typeof__(numer) numer_ = (numer);                                     \
        __typeof__(denom) denom_ = (denom);                                     \
        ((numer_) < 0) != ((denom_) < 0) ?                                      \
            (numer_) / (denom_) :                                               \
            ((numer_) + ((denom_) < 0 ? (denom_) + 1 : (denom_)-1)) / (denom_); \
    })

#define DIVIDE_ROUND_TOWARDS_ZERO2(numer, denom) DIVIDE_ROUNDDOWN2((numer), (denom))
#define DIVIDE_ROUNDDOWN2(numer, denom)                                          \
    ({                                                                           \
        __typeof__(numer) numer_ = (numer);                                      \
        __typeof__(denom) denom_ = (denom);                                      \
        ((numer_) < 0) != ((denom_) < 0) ?                                       \
            ((numer_) - ((denom_) < 0 ? (denom_) + 1 : (denom_)-1)) / (denom_) : \
            (numer_) / (denom_);                                                 \
    })

#define DIVIDE_ROUNDNEAREST2(numer, denom)                                              \
({                                                                                      \
    __typeof__ (numer) numer_ = (numer);                                                \
    __typeof__ (denom) denom_ = (denom);                                                \
    ((numer_) < 0) != ((denom_) < 0) ?                                                  \
    ((numer_) - ((denom_)/2)) / (denom_) :                                              \
    ((numer_) + ((denom_)/2)) / (denom_);                                               \
})

// -----------------------------------------------------------------------------
// 3. C++ Templated Functions (AKA: Function Templates)
// -----------------------------------------------------------------------------

#ifdef __cplusplus
#include <limits>

template <typename T>
T divide_roundup(T numer, T denom)
{
    static_assert(std::numeric_limits<T>::is_integer, "Only integer types are allowed");

    T result = ((numer) < 0) != ((denom) < 0) ?
        (numer) / (denom) :
        ((numer) + ((denom) < 0 ? (denom) + 1 : (denom) - 1)) / (denom);
    return result;
}

template <typename T>
inline T divide_round_away_from_zero(T numer, T denom)
{
    return divide_roundup(numer, denom);
}

template <typename T>
T divide_rounddown(T numer, T denom)
{
    static_assert(std::numeric_limits<T>::is_integer, "Only integer types are allowed");

    T result = ((numer) < 0) != ((denom) < 0) ?
        ((numer) - ((denom) < 0 ? (denom) + 1 : (denom) - 1)) / (denom) :
        (numer) / (denom);
    return result;
}

template <typename T>
inline T divide_round_towards_zero(T numer, T denom)
{
    return divide_rounddown(numer, denom);
}

template <typename T>
T divide_roundnearest(T numer, T denom)
{
    static_assert(std::numeric_limits<T>::is_integer, "Only integer types are allowed");

    T result = ((numer) < 0) != ((denom) < 0) ?
        ((numer) - ((denom)/2)) / (denom) :
        ((numer) + ((denom)/2)) / (denom);
    return result;
}

#endif
Run Code Online (Sandbox Code Playgroud)

2.带有大量解释性注释的代码

// -----------------------------------------------------------------------------
// 1. Macros
// -----------------------------------------------------------------------------
// Great for C or C++, but some C++ developers hate them since they may have the multiple evaluation
// problem where you pass in an expression as an input parameter and it gets evaluated multiple
// times.

/// @brief      A function-like macro to perform integer division of numer/denom, rounding the
///             result UP (AWAY FROM ZERO) to the next whole integer.
/// @note       This works on *integers only* since it assumes integer truncation will take place
///             automatically during the division! It will NOT work properly on floating point
///             types! Valid types are int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t,
///             int64_t, uint64_t, etc.
/// @details    The concept is this:
///             (1) when the division result will be positive, *add*
///             (abs(denom) - 1) to the numerator *prior to* the division, as this is the
///             equivalent of adding a *tiny bit less than 1* to the result, which will always
///             result in a rounding up once integer truncation takes place. Examples:
///             1/4 = 0.25, but we add (abs(denom) - 1) to the numerator --> (1 + (4 - 1))/4 =
///             (1 + 3)/4 = 4/4 = 1.
///             (2) when the division result will be negative, simply truncating the result by
///             performing division as normal results in a rounding-up effect.
/// @param[in]  numer   The numerator in the division: any positive or negative integer
/// @param[in]  denom   The denominator in the division: any positive or negative integer
/// @return     The result of the (numer/denom) division rounded UP to the next *whole integer*!
#define DIVIDE_ROUND_AWAY_FROM_ZERO(numer, denom) DIVIDE_ROUNDUP((numer), (denom))
#define DIVIDE_ROUNDUP(numer, denom) (                                                  \
    /* NB: `!=` acts as a logical XOR operator */                                       \
    /* See: /sf/answers/111767701/ */                              \
    ((numer) < 0) != ((denom) < 0) ?                                                    \
    /* numer OR denom, but NOT both, is negative, so do this: */                        \
    (numer) / (denom) :                                                                 \
    /* numer AND denom are either *both positive* OR *both negative*, so do this, */    \
    /* acting slightly differently if denom is negative: */                             \
    ((numer) + ((denom) < 0 ? (denom) + 1 : (denom) - 1)) / (denom)                     \
)

/// @brief      A function-like macro to perform integer division of numer/denom, rounding the
///             result DOWN (TOWARDS ZERO) to the next whole integer.
/// @note       This works on *integers only* since it assumes integer truncation will take place
///             automatically during the division! It will NOT work properly on floating point
///             types! Valid types are int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t,
///             int64_t, uint64_t, etc.
/// @details    The concept is this:
///             (1) when the division result will be positive, simply truncating the result by
///             performing division as normal results in a rounding-down effect.
///             (2) When the division result will be negative, *subtract*
///             (abs(denom) - 1) from the numerator *prior to* the division, as this is the
///             equivalent of subtracting a *tiny bit less than 1* from the result, which will
///             always result in a rounding down once integer truncation takes place. Examples:
///             -1/4 = -0.25, but we subtract (abs(denom) - 1) from the numerator -->
///             (-1 - (4 - 1))/4 = (-1 - 3)/4 = -4/4 = -1.
/// @param[in]  numer   The numerator in the division: any positive or negative integer
/// @param[in]  denom   The denominator in the division: any positive or negative integer
/// @return     The result of the (numer/denom) division rounded DOWN to the next *whole integer*!
#define DIVIDE_ROUND_TOWARDS_ZERO(numer, denom) DIVIDE_ROUNDDOWN((numer), (denom))
#define DIVIDE_ROUNDDOWN(numer, denom) (                                                \
    /* NB: `!=` acts as a logical XOR operator */                                       \
    /* See: /sf/answers/111767701/ */                              \
    ((numer) < 0) != ((denom) < 0) ?                                                    \
    /* numer OR denom, but NOT both, is negative, so do this, */                        \
    /* acting slightly differently if denom is negative: */                             \
    ((numer) - ((denom) < 0 ? (denom) + 1 : (denom) - 1)) / (denom) :                   \
    /* numer AND denom are either *both positive* OR *both negative*, so do this: */    \
    (numer) / (denom)

/// @brief      A function-like macro to perform integer division of numer/denom, rounding the
///             result TO THE NEAREST whole integer.
/// @note       This works on *integers only* since it assumes integer truncation will take place
///             automatically during the division! It will NOT work properly on floating point
///             types! Valid types are int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t,
///             int64_t, uint64_t, etc.
/// @details    The concept is this:
///             (1) when the division result will be positive, *add* (denom/2) to
///             the numerator *prior to* the division, as this is the equivalent of adding
///             0.5 to the result, which will always result in rounding to the nearest whole
///             integer once integer truncation takes place. Examples:
///             3/4 = 0.75, but we add (denom/2) to the numerator --> (3 + 4/2)/4 =
///             (3 + 2)/4 = 5/4 = 1.25, which truncates to 1.
///             (2) when the division result will be negative, *subtract* (denom/2) from
///             the numerator *prior to* the division, as this is required to grow it by 0.5
///             in the direction *away from zero* (more negative in this case), which is required
///             for rounding to the nearest whole integer. The same principle as in the positive
///             case applies. Example: -3/4 = -0.75, but we subtract (denom/2) from the numerator
///             --> (-3 - 4/2)/4 = (-3 - 2)/4 = -5/4 = -1.25, which truncates to -1.
/// @param[in]  numer   The numerator in the division: any positive or negative integer
/// @param[in]  denom   The denominator in the division: any positive or negative integer
/// @return     The result of the (numer/denom) division rounded TO THE NEAREST *whole integer*!
#define DIVIDE_ROUNDNEAREST(numer, denom) (                                             \
    /* NB: `!=` acts as a logical XOR operator */                                       \
    /* See: /sf/answers/111767701/ */                              \
    ((numer) < 0) != ((denom) < 0) ?                                                    \
    /* numer OR denom, but NOT both, is negative, so do this: */                        \
    ((numer) - ((denom)/2)) / (denom) :                                                 \
    /* numer AND denom are either *both positive* OR *both negative*, so do this: */    \
    ((numer) + ((denom)/2)) / (denom)                                                   \
)


// -----------------------------------------------------------------------------
// 2. Statement Expressions
// -----------------------------------------------------------------------------
// These solve the multiple evaluation problem of macros perfectly, but are not part of the C or
// C++ standard. Instead, they are gcc and clang compiler extensions to C and C++. These are safer
// to use than macros, but can still have a name pollution risk because variables created inside
// statement expressions are not in their own scope--rather, they are part of the outer scope.
// Nevertheless, prefer them to macros.

/// @brief  *gcc statement expression* form of the above equivalent macro
#define DIVIDE_ROUND_AWAY_FROM_ZERO2(numer, denom) DIVIDE_ROUNDUP2((numer), (denom))
#define DIVIDE_ROUNDUP2(numer, denom)                                           \
    ({                                                                          \
        __typeof__(numer) numer_ = (numer);                                     \
        __typeof__(denom) denom_ = (denom);                                     \
        ((numer_) < 0) != ((denom_) < 0) ?                                      \
            (numer_) / (denom_) :                                               \
            ((numer_) + ((denom_) < 0 ? (denom_) + 1 : (denom_)-1)) / (denom_); \
    })

/// @brief  *gcc statement expression* form of the above equivalent macro
#define DIVIDE_ROUND_TOWARDS_ZERO2(numer, denom) DIVIDE_ROUNDDOWN2((numer), (denom))
#define DIVIDE_ROUNDDOWN2(numer, denom)                                          \
    ({                                                                           \
        __typeof__(numer) numer_ = (numer);                                      \
        __typeof__(denom) denom_ = (denom);                                      \
        ((numer_) < 0) != ((denom_) < 0) ?                                       \
            ((numer_) - ((denom_) < 0 ? (denom_) + 1 : (denom_)-1)) / (denom_) : \
            (numer_) / (denom_);                                                 \
    })

/// @brief  *gcc statement expression* form of the above equivalent macro
#define DIVIDE_ROUNDNEAREST2(numer, denom)                                              \
({                                                                                      \
    __typeof__ (numer) numer_ = (numer);                                                \
    __typeof__ (denom) denom_ = (denom);                                                \
    ((numer_) < 0) != ((denom_) < 0) ?                                                  \
    ((numer_) - ((denom_)/2)) / (denom_) :                                              \
    ((numer_) + ((denom_)/2)) / (denom_);                                               \
})


// -----------------------------------------------------------------------------
// 3. C++ Templated Functions (AKA: Function Templates)
// -----------------------------------------------------------------------------
// Templates work in C++ only. They solve both problems above, and suffer neither from the multiple
// evaluation problem of macros, nor from the name pollution/variable scope problem of statement
// expressions. Since they work only in C++, I'm going to add type checking here too with a
// `static_assert()` using `std::numeric_limits`, but this feature could be *easily* added to both
// macros and statement expressions as well so long as you're using C++. Some C++ developers feel so
// strongly against macros (and are probably not aware of statement expressions) that they won't let
// you merge the above macro versions into their codebase. If this is the case, use templates.

#ifdef __cplusplus
#include <limits>

/// @brief  C++ function template form of the above equivalent macro
template <typename T>
T divide_roundup(T numer, T denom)
{
    // Ensure only integer types are passed in, as this round division technique does NOT work on
    // floating point types since it assumes integer truncation will take place automatically
    // during the division!
    // - The following static assert allows all integer types, including their various `const`,
    //   `volatile`, and `const volatile` variations, but prohibits any floating point type
    //   such as `float`, `double`, and `long double`.
    // - Reference page: https://en.cppreference.com/w/cpp/types/numeric_limits/is_integer
    static_assert(std::numeric_limits<T>::is_integer, "Only integer types are allowed");

    T result = ((numer) < 0) != ((denom) < 0) ?
        (numer) / (denom) :
        ((numer) + ((denom) < 0 ? (denom) + 1 : (denom) - 1)) / (denom);
    return result;
}

template <typename T>
inline T divide_round_away_from_zero(T numer, T denom)
{
    return divide_roundup(numer, denom);
}

/// @brief  C++ function template form of the above equivalent macro
template <typename T>
T divide_rounddown(T numer, T denom)
{
    // Ensure only integer types are passed in, as this round division technique does NOT work on
    // floating point types since it assumes integer truncation will take place automatically
    // during the division!
    // - The following static assert allows all integer types, including their various `const`,
    //   `volatile`, and `const volatile` variations, but prohibits any floating point type
    //   such as `float`, `double`, and `long double`.
    // - Reference page: https://en.cppreference.com/w/cpp/types/numeric_limits/is_integer
    static_assert(std::numeric_limits<T>::is_integer, "Only integer types are allowed");

    T result = ((numer) < 0) != ((denom) < 0) ?
        ((numer) - ((denom) < 0 ? (denom) + 1 : (denom) - 1)) / (denom) :
        (numer) / (denom);
    return result;
}

template <typename T>
inline T divide_round_towards_zero(T numer, T denom)
{
    return divide_rounddown

  • 简短的答案是有缺陷的:“ROUND_DIVIDE(-3, 4)”的计算结果为“0”,这不是最接近的整数。冗长的解释根本没有解决这个问题。`(int)round(-3.0 / 4.0)` 的计算结果为 `-1`。 (2认同)