C语言 舍入整数除法(而不是截断)
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2422712/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Rounding integer division (instead of truncating)
提问by Dave
I was curious to know how I can round a number to the nearest whole number. For instance, if I had:
我很想知道如何将数字四舍五入到最接近的整数。例如,如果我有:
int a = 59 / 4;
which would be 14.75 if calculated in floating point; how can I store the result as 15 in "a"?
如果以浮点计算,则为 14.75;如何将结果存储为“a”中的 15?
采纳答案by 0xC0DEFACE
int a = 59.0f / 4.0f + 0.5f;
This only works when assigning to an int as it discards anything after the '.'
这仅在分配给 int 时有效,因为它会丢弃 '.' 之后的任何内容。
Edit:This solution will only work in the simplest of cases. A more robust solution would be:
编辑:此解决方案仅适用于最简单的情况。更强大的解决方案是:
unsigned int round_closest(unsigned int dividend, unsigned int divisor)
{
return (dividend + (divisor / 2)) / divisor;
}
回答by Jonathan Leffler
The standard idiom for integer rounding up is:
整数四舍五入的标准习惯用法是:
int a = (59 + (4 - 1)) / 4;
You add the divisor minus one to the dividend.
您将除数减去一添加到被除数中。
回答by ericbn
A code that works for any sign in dividend and divisor:
适用于任何被除数和除数符号的代码:
int divRoundClosest(const int n, const int d)
{
return ((n < 0) ^ (d < 0)) ? ((n - d/2)/d) : ((n + d/2)/d);
}
In response to a comment "Why is this actually working?", we can break this apart. First, observe that n/dwould be the quotient, but it is truncated towards zero, not rounded. You get a rounded result if you add half of the denominator to the numerator before dividing, but only if numerator and denominator have the same sign. If the signs differ, you must subtract half of the denominator before dividing. Putting all that together:
为了回应“为什么这实际上有效?”的评论,我们可以将其分解。首先,观察这n/d将是商,但它被截断为零,而不是四舍五入。如果在除法之前将分母的一半加到分子上,您会得到一个四舍五入的结果,但前提是分子和分母具有相同的符号。如果符号不同,则必须在除法之前减去分母的一半。把所有这些放在一起:
(n < 0) is false (zero) if n is non-negative
(d < 0) is false (zero) if d is non-negative
((n < 0) ^ (d < 0)) is true if n and d have opposite signs
(n + d/2)/d is the rounded quotient when n and d have the same sign
(n - d/2)/d is the rounded quotient when n and d have opposite signs
If you prefer a macro:
如果您更喜欢宏:
#define DIV_ROUND_CLOSEST(n, d) ((((n) < 0) ^ ((d) < 0)) ? (((n) - (d)/2)/(d)) : (((n) + (d)/2)/(d)))
The linux kernel macro DIV_ROUND_CLOSEST doesn't work for negative divisors!
linux 内核宏 DIV_ROUND_CLOSEST 不适用于负除数!
EDIT: This will work without overflow:
编辑:这将工作而不会溢出:
int divRoundClosest( int A, int B )
{
if(A<0)
if(B<0)
return (A + (-B+1)/2) / B + 1;
else
return (A + ( B+1)/2) / B - 1;
else
if(B<0)
return (A - (-B+1)/2) / B - 1;
else
return (A - ( B+1)/2) / B + 1;
}
回答by WayneJ
You should instead use something like this:
你应该使用这样的东西:
int a = (59 - 1)/ 4 + 1;
I assume that you are really trying to do something more general:
我假设您真的在尝试做一些更一般的事情:
int divide(x, y)
{
int a = (x -1)/y +1;
return a;
}
x + (y-1) has the potential to overflow giving the incorrect result; whereas, x - 1 will only underflow if x = min_int...
x + (y-1) 有可能溢出给出不正确的结果;而 x - 1 只有在 x = min_int 时才会下溢...
回答by WayneJ
(Edited) Rounding integers with floating point is the easiest solution to this problem; however, depending on the problem set is may be possible. For example, in embedded systems the floating point solution may be too costly.
(已编辑)使用浮点数舍入整数是解决此问题的最简单方法;然而,根据问题集是可能的。例如,在嵌入式系统中,浮点解决方案可能过于昂贵。
Doing this using integer math turns out to be kind of hard and a little unintuitive. The first posted solution worked okay for the the problem I had used it for but after characterizing the results over the range of integers it turned out to be very bad in general. Looking through several books on bit twiddling and embedded math return few results. A couple of notes. First, I only tested for positive integers, my work does not involve negative numerators or denominators. Second, and exhaustive test of 32 bit integers is computational prohibitive so I started with 8 bit integers and then mades sure that I got similar results with 16 bit integers.
事实证明,使用整数数学来做这件事有点困难,而且有点不直观。第一个发布的解决方案对于我用它解决的问题来说还可以,但是在对整数范围内的结果进行表征后,结果证明总的来说非常糟糕。翻阅几本关于 bit twiddling 和嵌入式数学的书,结果很少。一些注意事项。首先,我只测试了正整数,我的工作不涉及负分子或分母。其次,对 32 位整数的详尽测试是计算上的,所以我从 8 位整数开始,然后确保我用 16 位整数得到类似的结果。
I started with the 2 solutions that I had previously proposed:
我从我之前提出的 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 * 10)/D) + 5)/10)
#define DIVIDE_WITH_ROUND(N, D) (N == 0) ? 0:(N - D/2)/D + 1;
#define DIVIDE_WITH_ROUND(N, D) (N == 0) ? 0:(N - D/2)/D + 1;
My thought was that the first version would overflow with big numbers and the second underflow with small numbers. I did not take 2 things into consideration. 1.) the 2nd problem is actually recursive since to get the correct answer you have to properly round D/2. 2.) In the first case you often overflow and then underflow, the two canceling each other out.
Here is an error plot of the two (incorrect) algorithms:
我的想法是第一个版本会以大数字溢出,而第二个版本会以小数字溢出。我没有考虑两件事。1.) 第二个问题实际上是递归的,因为要得到正确的答案,您必须正确地舍入 D/2。2.) 在第一种情况下,您经常溢出然后下溢,两者相互抵消。这是两种(不正确的)算法的误差图:
This plot shows that the first algorithm is only incorrect for small denominators (0 < d < 10). Unexpectedly it actually handles largenumerators better than the 2nd version.
该图显示第一个算法仅对小分母 (0 < d < 10) 不正确。没想到它实际上处理大numerators比第二版更好。
Here is a plot of the 2nd algorithm:

这是第二个算法的图:

As expected it fails for small numerators but also fails for more large numerators than the 1st version.
正如预期的那样,它对于小分子失败,但对于比第一个版本更大的分子也失败。
Clearly this is the better starting point for a correct version:
显然,这是正确版本的更好起点:
#define DIVIDE_WITH_ROUND(N, D) (((N) == 0) ? 0:(((N * 10)/D) + 5)/10)
#define DIVIDE_WITH_ROUND(N, D) (((N) == 0) ? 0:(((N * 10)/D) + 5)/10)
If your denominators is > 10 then this will work correctly.
如果您的分母 > 10,那么这将正常工作。
A special case is needed for D == 1, simply return N. A special case is needed for D== 2, = N/2 + (N & 1) // Round up if odd.
D == 1 需要特殊情况,只需返回 N。 D== 2, = N/2 + (N & 1) // 如果奇数则向上舍入。
D >= 3 also has problems once N gets big enough. It turns out that larger denominators only have problems with larger numerators. For 8 bit signed number the problem points are
一旦 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))
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))
(return D/N for these)
(为这些返回 D/N)
So in general the the pointe where a particular numerator gets bad is somewhere aroundN > (MAX_INT - 5) * D/10
所以一般来说,特定分子变坏的指针在某处N > (MAX_INT - 5) * D/10
This is not exact but close. When working with 16 bit or bigger numbers the error < 1% if you just do a C divide (truncation) for these cases.
这并不准确,但很接近。当使用 16 位或更大的数字时,如果您只是对这些情况进行 C 除法(截断),则误差 < 1%。
For 16 bit signed numbers the tests would be
对于 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))
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))
Of course for unsigned integers MAX_INT would be replaced with MAX_UINT. I am sure there is an exact formula for determining the largest N that will work for a particular D and number of bits but I don't have any more time to work on this problem...
当然,对于无符号整数,MAX_INT 将替换为 MAX_UINT。我确信有一个精确的公式可以确定适用于特定 D 和位数的最大 N,但我没有更多时间来解决这个问题......
(I seem to be missing this graph at the moment, I will edit and add later.)
This is a graph of the 8 bit version with the special cases noted above:
