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

我很想知道如何将一个数字舍去到最接近的整数。 例如,如果我有:

int a = 59 / 4; 

以浮点计算得到14.75; 我怎么能把这个数字作为15存储在“a”中?

 int a = 59.0f / 4.0f + 0.5f; 

这只适用于分配给int,因为它放弃了'。'之后的任何东西。

编辑:此解决scheme将只在最简单的情况下工作。 更健壮的解决scheme是:

 unsigned int round_closest(unsigned int dividend, unsigned int divisor) { return (dividend + (divisor / 2)) / divisor; } 

整数四舍五入的标准成语是:

 int a = (59 + (4 - 1)) / 4; 

除红利加上除数1。

适用于任何股息和除数login的代码:

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

如果你喜欢一个macros:

 #define DIV_ROUND_CLOSEST(n, d) ((((n) < 0) ^ ((d) < 0)) ? (((n) - (d)/2)/(d)) : (((n) + (d)/2)/(d))) 

Linux内核macrosDIV_ROUND_CLOSEST不适用于负数因子!

你应该使用这样的东西:

 int a = (59 - 1)/ 4 + 1; 

我假设你真的想做更一般的事情:

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

x +(y-1)有可能溢出给出不正确的结果; 而x – 1只会下溢,如果x = min_int …

(编辑)用浮点四舍五入整数是解决这个问题的最简单的方法。 然而,取决于问题集是可能的。 例如,在embedded式系统中,浮点解决scheme可能太昂贵了。

使用整数math来做这件事情会变得困难而且有点不直观。 第一个发布的解决scheme对我曾经使用过的这个问题起到了很好的作用,但是在对整个范围内的结果进行特征化之后,结果是非常糟糕的。 通过几本有关旋转和embeddedmath的书籍来看,几乎没有什么结果。 几个笔记。 首先,我只testing了正整数,我的工作不涉及负分子或分母。 其次,对32位整数的详尽testing是计算过度的,所以我从8位整数开始,然后确定我得到了16位整数的相似结果。

我从以前提出的两个解决scheme开始:

#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.)第二个问题实际上是recursion的,因为要得到正确的答案,你必须正确地舍入D / 2。 2.)在第一种情况下,你经常溢出然后下溢,二者相互抵消掉。 这是两个(不正确)algorithm的错误图: 除以Round1 8位x =分子y =分母

该图显示第一个algorithm对于小分母(0 <d <10)是不正确的。 意外的是,它实际上处理大分子比第二版更好。

这里是第二个algorithm的情节: 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也会有问题。 事实certificate,更大的分母只有分子更大的问题。 对于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)

所以一般而言,特定分子变坏的普通情况在某处
N > (MAX_INT - 5) * D/10

这不是确切但接近。 当使用16位或更大的数字时,如果对这些情况进行C分割(截断),则错误<1%。

对于16位有符号数的testing将是

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将被replace为MAX_UINT。 我相信有一个确切的公式来确定最大的N将适用于特定的D和位数,但我没有更多的时间来解决这个问题…

(我现在似乎错过了这个图表,我将在后面进行编辑和添加。)这是一个8位版本的图表,其中包含上述特殊情况:![8位带有特殊情况的0 < N <= 10 3

请注意,对于图中的所有错误,对于8位,错误为10%或更less,16位为<0.1%。

正如所写,你正在执行整数算术,它会自动截断任何十进制结果。 要执行浮点运算,请将常量更改为浮点值:

 int a = round(59.0 / 4); 

或者将它们转换为floattypes或其他浮点types:

 int a = round((float)59 / 4); 

无论哪种方式,都需要使用math.h头文件中的round()函数进行最后的四舍五入,因此请确保#include <math.h>并使用兼容C99的编译器。

 #define CEIL(a, b) (((a) / (b)) + (((a) % (b)) > 0 ? 1 : 0)) 

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

 #define MIN(a, b) (((a) < (b)) ? (a) : (b)) #define MAX(a, b) (((a) > (b)) ? (a) : (b)) #define ABS(a) (((a) < 0) ? -(a) : (a)) 

从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)); \ } \ ) 
 int a, b; int c = a / b; if(a % b) { c++; } 

检查是否有余数允许您手动综合整数除法的商。

这是我的解决scheme。 我喜欢它,因为我觉得它更具可读性,因为它没有分支(既不是if也不是三元组)。

 int32_t divide(int32_t a, int32_t b) { int32_t resultIsNegative = ((a ^ b) & 0x80000000) >> 31; int32_t sign = resultIsNegative*-2+1; return (a + (b / 2 * sign)) / b; } 

完整的testing程序,说明预期的行为:

 #include <stdint.h> #include <assert.h> int32_t divide(int32_t a, int32_t b) { int32_t resultIsNegative = ((a ^ b) & 0x80000000) >> 31; int32_t sign = resultIsNegative*-2+1; return (a + (b / 2 * sign)) / b; } int main() { assert(divide(0, 3) == 0); assert(divide(1, 3) == 0); assert(divide(5, 3) == 2); assert(divide(-1, 3) == 0); assert(divide(-5, 3) == -2); assert(divide(1, -3) == 0); assert(divide(5, -3) == -2); assert(divide(-1, -3) == 0); assert(divide(-5, -3) == 2); } 

借用从@ericbn我prefere定义像

 #define DIV_ROUND_INT(n,d) ((((n) < 0) ^ ((d) < 0)) ? (((n) - (d)/2)/(d)) : (((n) + (d)/2)/(d))) or if you work only with unsigned ints #define DIV_ROUND_UINT(n,d) ((((n) + (d)/2)/(d))) 
 int divide(x,y){ int quotient = x/y; int remainder = x%y; if(remainder==0) return quotient; int tempY = divide(y,2); if(remainder>=tempY) quotient++; return quotient; } 

例如59/4商数= 14,tempY = 2,余数= 3,余数> = tempY因此商= 15;

 double a=59.0/4; int b=59/4; if(ab>=0.5){ b++; } printf("%d",b); 
  1. 让精确浮点值59.0 / 4为x(这里是14.750000)
  2. 让小于x的最小整数为y(这里是14)
  3. 如果xy <0.5那么y就是解决scheme
  4. 否则y + 1是解决scheme

尝试使用math细胞function,使四舍五入。 mathCeil !

如果你正在分正整数,你可以把它移开,做分割,然后检查右边的位b0。 换句话说,100/8是12.5,但是会返回12.如果你这样做(100 << 1)/ 8,你可以检查b0,然后把结果转回来。

对于某些algorithm,当“最接近”是一条平行线时,您需要一致的偏见。

 // round-to-nearest with mid-value bias towards positive infinity int div_nearest( int n, int d ) { if (d<0) n*=-1, d*=-1; return (abs(n)+((d-(n<0?1:0))>>1))/d * ((n<0)?-1:+1); } 

无论分子或分母的符号如何,这都是有效的。


如果你想匹配round(N/(double)D) (浮点除法和舍入)的结果,下面是几个变体,都产生相同的结果:

 int div_nearest( int n, int d ) { int r=(n<0?-1:+1)*(abs(d)>>1); // eliminates a division // int r=((n<0)^(d<0)?-1:+1)*(d/2); // basically the same as @ericbn // int r=(n*d<0?-1:+1)*(d/2); // small variation from @ericbn return (n+r)/d; } 

注: (abs(d)>>1)(d/2)的相对速度很可能取决于平台。

我遇到了同样的困难。 下面的代码应该为正整数。

我还没有编译它,但我testing了谷歌电子表格(我知道,跆拳道)algorithm,它工作。

 unsigned int integer_div_round_nearest(unsigned int numerator, unsigned int denominator) { unsigned int rem; unsigned int floor; unsigned int denom_div_2; // check error cases if(denominator == 0) return 0; if(denominator == 1) return numerator; // Compute integer division and remainder floor = numerator/denominator; rem = numerator%denominator; // Get the rounded value of the denominator divided by two denom_div_2 = denominator/2; if(denominator%2) denom_div_2++; // If the remainder is bigger than half of the denominator, adjust value if(rem >= denom_div_2) return floor+1; else return floor; } 

更安全的C代码(除非你有其他方法处理/ 0):

return (_divisor > 0) ? ((_dividend + (_divisor - 1)) / _divisor) : _dividend;

这不会处理由于无效的input数据而导致返回值不正确的问题。