约ROUND_UP宏问题(Question about round_up macro)

2019-07-28 22:12发布

#define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S))

有了上面的宏,可能有人请帮助我在理解“(S)”的一部分,为什么?

而且宏,如:

#define PAGE_ROUND_DOWN(x) (((ULONG_PTR)(x)) & (~(PAGE_SIZE-1)))
#define PAGE_ROUND_UP(x) ( (((ULONG_PTR)(x)) + PAGE_SIZE-1)  & (~(PAGE_SIZE-1)) ) 

我知道,“(〜(PAGE_SIZE-1)))”部分将零出最后的五位,但除此之外,我毫无章法,尤其是作用“和”运算符的戏剧。

谢谢,

Answer 1:

ROUND_UP宏依靠整数除法来完成这项工作。 如果两个参数是整数,它才会工作。 我假设N要被倒圆的数目和S是其上应该被舍入的间隔。 即, ROUND_UP(12, 5)应返回15中,由于图15是第5大于12第一间隔。

试想一下,我们不是围捕了下来。 在这种情况下,宏只会是:

#define ROUND_DOWN(N,S) ((N / S) * S)

ROUND_DOWN(12,5)将返回10,因为(12/5)在整数除法是2和2 * 5为10.但我们没有做ROUND_DOWN,我们正在做ROUND_UP。 所以我们做整数除法之前,我们要添加就像我们可以在不丢失精度。 如果我们增加了S ,这将几乎所有情况下工作的; ROUND_UP(11,5)将成为(((11 + 5)/ 5)* 5),并自16/5的整数除法是3,我们会得到15。

当我们通过这已经四舍五入到指定的多了一些问题就来了。 ROUND_UP(10, 5)将返回15,这是错误的。 因此,而不是添加S,我们添加S-1。 这保证了我们永远的东西推到下一个“桶”不必要的。

PAGE_宏与二进制数学做。 我们会假装我们正在处理8位数值为简单起见。 让我们假设PAGE_SIZE0b00100000PAGE_SIZE-1因此0b00011111~(PAGE_SIZE-1)然后, 0b11100000

二进制&将排队两个二进制数,留下一个1任何地方,两个数字有1因此,如果x是0b01100111,操作会是这样的:

  0b01100111  (x)
& 0b11100000  (~(PAGE_SIZE-1))
------------
  0b01100000

你会注意到,该操作真的只有归零出最后5位。 就这样。 但这正是向下舍入到最近的间隔需要的操作PAGE_SIZE 。 请注意,这只是工作,因为PAGE_SIZE正是2的幂这是一个有点像说任何任意的十进制数,你可以简单地归零时的最后两位数字四舍五入到最接近的100。 它完美,而且是很容易做到,但在所有如果你试图舍入到76的最接近倍数是行不通的。

PAGE_ROUND_UP做同样的事情,但它切割它关闭之前添加尽可能多的,因为它可以给网页。 这有点像我如何通过添加99到任意数量, 然后归零时的最后两位数字四舍五入到100最接近的倍数。 (我们添加PAGE_SIZE-1为我们添加了同样的理由S-1以上。)

祝你的虚拟内存!



Answer 2:

使用整数运算,始终将向下取整。 为了解决这个问题,您可以添加尽可能多的,不会影响结果,如果原来的数是整除。 对于S的数量,即尽可能多的为S-1。

四舍五入到2的幂是特殊的,因为你可以用位操作做到这一点。 2个将赠品的多具有零在底部位,4的倍数将始终具有零在底部的两个位等的2的幂的二进制表示是单个比特,接着一串零的; 减去1将清除该位,并设置所有位向右。 反相价值创造与需要被清除的地方零位掩码。 &运算符将在你的价值清除这些位,因此舍入值下降。 加入(PAGE_SIZE-1)到原始值的同一特技使其代替四舍五入组成。



Answer 3:

页面四舍五入宏假定`PAGE_SIZE是二的幂,如:

0x0400    -- 1 KiB
0x0800    -- 2 KiB`
0x1000    -- 4 KiB

的值PAGE_SIZE - 1 ,因此,是一个所有位:

0x03FF
0x07FF
0x0FFF

因此,如果是整数16位(而不是32或64 -它节省了我一些打字),则值~(PAGE_SIZE-1)为:

0xFC00
0xFE00
0xF000

当你把值x (假设,令人难以置信的真实生活,但足以博览会的目的,即ULONG_PTR是一个16位无符号整数)是0xBFAB ,然后

PAGE_SIZE         PAGE_ROUND_DN(0xBFAB)   PAGE_ROUND_UP(0xBFAB)

0x0400     -->    0xBC00                  0xC000
0x0800     -->    0xB800                  0xC000
0x1000     -->    0xB000                  0xC000

宏圆向下和向上的页面大小的最接近倍数。 最后五个比特将只被如果置零PAGE_SIZE == 0x20 (或32)。



Answer 4:

基于目前的标准草案(C99)这个宏是不完全正确的,但要注意为负值N结果几乎肯定会是不正确的。

公式:

#define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S))

利用这样的事实,即整数除法回合下来非负整数,并且使用S - 1部分,迫使它围捕代替。

然而,整数除法舍入到零(C99,6.5.5节。乘法运算符,第6项)。 对于负N ,为“围捕”正确的做法是:“ N / S ”,仅此而已,无所不及。

它会变得更复杂,如果S也允许为负值,但我们甚至没有去那里......(见: 我怎样才能确保整数的除法总是四舍五入?各种错误的更详细的讨论和一个或两个合适的解决方案)



Answer 5:

在与使它如此..好OK,让我们一些二进制数。

(with 1000 being page size)
PAGE_ROUND_UP(01101b)=
01101b+1000b-1b & ~(1000b-1b) =
01101b+111b & ~(111b) =
01101b+111b & ...11000b = (the ... means 1's continuing for size of ULONG)
10100b & 11000b=
10000b

所以,你可以看到(希望)这通过添加PAGE_SIZE到X,然后取与舍了,因此取消了未设置PAGE_SIZE的底位



文章来源: Question about round_up macro
标签: c macros