#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)))”部分将零出最后的五位,但除此之外,我毫无章法,尤其是作用“和”运算符的戏剧。
谢谢,
该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_SIZE
是0b00100000
。 PAGE_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
以上。)
祝你的虚拟内存!
使用整数运算,始终将向下取整。 为了解决这个问题,您可以添加尽可能多的,不会影响结果,如果原来的数是整除。 对于S的数量,即尽可能多的为S-1。
四舍五入到2的幂是特殊的,因为你可以用位操作做到这一点。 2个将赠品的多具有零在底部位,4的倍数将始终具有零在底部的两个位等的2的幂的二进制表示是单个比特,接着一串零的; 减去1将清除该位,并设置所有位向右。 反相价值创造与需要被清除的地方零位掩码。 &运算符将在你的价值清除这些位,因此舍入值下降。 加入(PAGE_SIZE-1)到原始值的同一特技使其代替四舍五入组成。
页面四舍五入宏假定`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)。
基于目前的标准草案(C99)这个宏是不完全正确的,但要注意为负值N
结果几乎肯定会是不正确的。
公式:
#define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S))
利用这样的事实,即整数除法回合下来非负整数,并且使用S - 1
部分,迫使它围捕代替。
然而,整数除法舍入到零(C99,6.5.5节。乘法运算符,第6项)。 对于负N
,为“围捕”正确的做法是:“ N / S
”,仅此而已,无所不及。
它会变得更复杂,如果S
也允许为负值,但我们甚至没有去那里......(见: 我怎样才能确保整数的除法总是四舍五入?各种错误的更详细的讨论和一个或两个合适的解决方案)
在与使它如此..好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的底位