【C语言笔记】【linux宏定义系列】 四舍五入除法 DIV_ROUND_CLOSEST
linux宏定义系列内容。用于记录在linux之中各式各样的宏定义☺。
宏定义说明
用于除法运算,将正或负的被除数除以正的除数,得到的结果会四舍五入到最接近的整数。
例如 4 ÷ 10 = 0 4 \div 10 = 0 4÷10=0, 5 ÷ 10 = 1 5 \div 10 = 1 5÷10=1, 47 ÷ 5 = 9 47 \div 5 = 9 47÷5=9, 47 ÷ 5 = 10 47 \div 5 = 10 47÷5=10。
该宏定义来自linux kernel 3.10。
实现代码
#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)); \
} \
)
宏定义中:
x
表示被除数,被除数可以是正数或者负数。
divisor
表示除数,除数只能是正数。
注意:如果被除数是负数,那么除数divisor
的变量类型不能是无符号的,否者结果是未定义的。
示例程序
int main(int argc, char* argv[])
{
int x, y;
x = 4;
y = 5;
printf("x %d y %d result %d\n", x, y, DIV_ROUND_CLOSEST(x, y));
x = 2;
y = 5;
printf("x %d y %d result %d\n", x, y, DIV_ROUND_CLOSEST(x, y));
x = -4;
y = 5;
printf("x %d y %d result %d\n", x, y, DIV_ROUND_CLOSEST(x, y));
x = -2;
y = 5;
printf("x %d y %d result %d\n", x, y, DIV_ROUND_CLOSEST(x, y));
return 0;
}
运行后,结果为
x 4 y 5 result 1
x 2 y 5 result 0
x -4 y 5 result -1
x -2 y 5 result 0
实现过程
-
typeof(x) __x = x;
typeof(divisor) __d = divisor;
使用一个与传入的参数相同数据类型的临时变量来保存参数的值,避免类似于自增或自减带来的影响。
-
(((typeof(x))-1) > 0 || \ ((typeof(divisor))-1) > 0 || (__x) > 0)
这个里面有三个部分
-
((typeof(x))-1) > 0
用来判断被除数的类型。当
x
为有符号数时,将-1
强制转换成typeof(x)
类型,这个值还是负数,不会大于0,((typeof(x))-1) > 0
的结果为假,也就是为0;当
x
为无符号数时,将-1
强制转换成typeof(x)
类型,这个值会变成正数,值会大于0,((typeof(x))-1) > 0
的结果为真,也就是为1; -
((typeof(divisor))-1) > 0
用来判断除数的类型。方式同上。当
divisor
为有符号数时,结果为假;当divisor
为无符号数时,结果为真。 -
(__x) > 0)
用来判断被除数的值是否大于0。
上面三种情况,任意一种大于0,就执行
(((__x) + ((__d) / 2)) / (__d))
,否者就执行(((__x) - ((__d) / 2)) / (__d))
。 -
-
(((__x) + ((__d) / 2)) / (__d))
(((__x) - ((__d) / 2)) / (__d))
当
x
的类型为无符号数,或者当divisor
的类型为无符号数,再或者被除数__x
的值大于0时,执行(((__x) + ((__d) / 2)) / (__d))
。能运行这句,就表示被除数与除数都是正数。这里__x
先加上(__d) / 2
,再除以__d
,这样结果就会四舍五入到最接近的整数。这与正常的除法区别就是被除数要先加上除数的一半。为什么要先加上除数的一半,这是因为,如果需要向下舍入的数,加上除数的一半,除法的结果还是一样的,比如正常除法运算4 / 10 = 0
,如果加上除数的一半,变为(4 + 10/2) / 10 = (4 + 5) / 10 = 9 / 10 = 0
,结果不变;而需要向上取整的数,加上除数的一半,除法的结果会加1,比如正常除法运算5 / 10 = 0
,如果加上除数的一半,变为(5 + 10/2) / 10 = (5 + 5) / 10 = 10 / 10 = 1
,实现向上取整。当
x
的类型为有符号数,并且divisor
的类型为有符号数,同时被除数__x
的值小于等于0时,执行(((__x) - ((__d) / 2)) / (__d))
。能运行这句,就表示被除数为负数,而除数是正数。这里__x
先减去(__d) / 2
,再除以__d
,这样结果就会四舍五入到最接近的整数。原因与上面相同,但是由于被除数为负数,所以加上(__d) / 2
变为了减去(__d) / 2
。
[参考资料]
linux kernel 3.10
/include/linux/kernel.h
【GNU笔记】【C扩展系列】用typeof来引用一个类型 Referring to a Type with typeof
本文链接:https://blog.csdn.net/u012028275/article/details/118864192