我们在代码中经常可以看到如下定义:
#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
#define ROUND_UP(x, align) (DIV_ROUND_UP(x, align) * (align))
#define ROUND_DOWN(n,d) ((n) / (d)) * d)
向上取整宏 ROUND_UP(n, d)
宏依靠 整数除法 来完成,它只有在两个参数都是整数时才有效。例如:ROUND_UP(12, 5)
会返回 15
, 因为 15
是第一个以 5 为间隔且大于 12
的数值。
向下取整宏 ROUND_DOWN(12, 5)
将返回 10, 因为 (12 / 5) 整数除法是 2。
Q: DIV_ROUND_UP(n, d) 定义中为何需要做 “(d) -1” 的操作 而不是直接使用 “(d)
”?
A: 如果不做 “(d) -1
” 的操作,直接使用 #define DIV_ROUND_UP(n, d) (((n) + (d) ) / (d))
, 那么当我们传递一个已经舍入到指定倍数的数字时, 就会有问题,比如: ROUND_UP(10, 5)
会回 15
, 这个是错误的。因此, 我们不是添加 (d)
, 而是添加 d-1
。
参见 linux 中的实现:
include/linux/kernel.h
/**
* roundup - round up to the next specified multiple
* @x: the value to up
* @y: multiple to round up to
*
* Rounds @x up to next multiple of @y. If @y will always be a power
* of 2, consider using the faster round_up().
*
* The `const' here prevents gcc-3.3 from calling __divdi3
*/
#define roundup(x, y) ( \
{ \
const typeof(y) __y = y; \
(((x) + (__y - 1)) / __y) * __y; \
} \
)
/**
* rounddown - round down to next specified multiple
* @x: the value to round
* @y: multiple to round down to
*
* Rounds @x down to next multiple of @y. If @y will always be
* a power of 2, consider using the faster round_down().
*/
#define rounddown(x, y) ( \
{ \
typeof(x) __x = (x); \
__x - (__x % (y)); \
} \
)
如果有一个动态增长的缓冲区,增长的步长是 d, 某一次缓冲区申请的大小是 n,这个时候,就可以用这个算法,计算出缓冲区的一个合适大小了,正好可以容纳 n,并且不会过于得多,多余部分不会比 d 多。
先看下Linux 对这两个函数的实现:
include/linux/kernel.h
/*
* This looks more complex than it should be. But we need to
* get the type for the ~ right in round_down (it needs to be
* as wide as the result!), and we want to evaluate the macro
* arguments just once each.
*/
#define __round_mask(x, y) ((__typeof__(x))((y)-1))
/**
* round_up - round up to next specified power of 2
* @x: the value to round
* @y: multiple to round up to (must be a power of 2)
*
* Rounds @x up to next multiple of @y (which must be a power of 2).
* To perform arbitrary rounding up, use roundup() below.
*/
#define round_up(x, y) ((((x)-1) | __round_mask(x, y))+1)
/**
* round_down - round down to next specified power of 2
* @x: the value to round
* @y: multiple to round down to (must be a power of 2)
*
* Rounds @x down to next multiple of @y (which must be a power of 2).
* To perform arbitrary rounding down, use rounddown() below.
*/
#define round_down(x, y) ((x) & ~__round_mask(x, y))
从上面的解释可以看出来,y 的值必须是 2 的幂次方,由于 Linux内存管理大部分时候是以 K(1024bytes=2^10)
为单位进行的,所以这里要求 y 的值必须是 2 的幂次方,
x 的值即为要取整的数,间隔跨度是 y 的值,比如 x 为 3.5K,y的值为1K,那么使用 round_up(3.5K, 1K)
之后, 返回的值为 4K,如果使用 round_down(3.5K, 1K)
那么返回的值为 3K。