2009-06-18 96 views
10
#define ROUND_UP(N, S) ((((N) + (S) - 1)/(S)) * (S)) 

有了上面的宏,有人能帮我理解“(s)-1”部分,为什么?关于round_up宏的问题

,也像宏:

#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)))”相比,部分将零出最后的五位,但其他我一无所知,尤其是角色'&'操作员播放。

谢谢,

回答

15

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,它几乎可以在任何情况下工作; (((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相同。)

祝您的虚拟内存好运!

4

使用整数算术,总是向下舍入。为了解决这个问题,如果原始数字是可以被整除的,那么添加不会影响结果的最大可能数字。对于数字S,最大可能数字是S-1。

取整为2的幂是特殊的,因为可以通过位操作来完成。 2的倍数在底部位将总是为零,4的倍数在底部的两位总是等于零,等等。2的幂的二进制表示是一个位,后面是一串零;减1将清除该位,并将所有位设置为右侧。反转该值会在需要清除的位置创建一个带零的位掩码。运算符将清除您的值中的那些位,从而将值舍入。将(PAGE_SIZE-1)添加到原始值的相同技巧会使其四舍五入而不是减小。

0

&使它如此..好吧,让我们取一些二进制数。

 
(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的底位未设置

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),最后五位只会被清零。

1

根据当前的草案标准(C99),该宏不完全正确,但请注意,对于负值N,结果几乎肯定是不正确的。

的公式:

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

利用的整数除法回合下来非负整数,并使用S - 1部分,迫使它围捕,而不是事实。

但是,整数除法向零舍入(C99,第6.5.5节。乘法运算符,第6项)。对于负数N,“四舍五入”的正确方法是:'N/S',仅此而已。

如果S也允许为负值,但我们甚至没有去那里它会变得更复杂......(见:How can I ensure that a division of integers is always rounded up?为错误的各种更详细的讨论和一个或两个合适的解决方案)