当前位置: 首页 > 工具软件 > Roundup > 使用案例 >

【ARM 嵌入式 C 入门及渐进 2 -- 向上取整&向下取整 linux roundup/round_up】

柳向明
2023-12-01

1.1 向上/下取整

我们在代码中经常可以看到如下定义:

#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));                              \
}                                                       \
)

1.1.1 向上取整使用背景

如果有一个动态增长的缓冲区,增长的步长是 d, 某一次缓冲区申请的大小是 n,这个时候,就可以用这个算法,计算出缓冲区的一个合适大小了,正好可以容纳 n,并且不会过于得多,多余部分不会比 d 多。

1.1.2 round_up/round_down

先看下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。

 类似资料: