La macro ROUND_UP
se basa en la división de enteros para realizar el trabajo. Solo funcionará si ambos parámetros son enteros. Supongo que N
es el número que se redondeará y S
es el intervalo en el que debe redondearse. Es decir, ROUND_UP(12, 5)
debe devolver 15, ya que 15 es el primer intervalo de 5 más grande que 12.
Imagine que estábamos redondeando hacia abajo en lugar de hacia arriba. En ese caso, la macro sería simplemente:
#define ROUND_DOWN(N,S) ((N/S) * S)
ROUND_DOWN(12,5)
regresarían 10, porque (12/5)
en la división entera es 2, y 2 * 5 es 10. Pero no estamos haciendo ROUND_DOWN, que estamos haciendo ROUND_UP . Entonces, antes de hacer la división de enteros, queremos agregar todo lo que podamos sin perder precisión. Si agregamos S
, funcionaría en casi todos los casos; ROUND_UP(11,5)
se convertiría en (((11 + 5)/5) * 5), y como 16/5 en la división de enteros es 3, obtendríamos 15.
El problema surge cuando pasamos un número que ya está redondeado a el múltiple especificado ROUND_UP(10, 5)
devolvería 15, y eso está mal. Entonces, en lugar de agregar S, agregamos S-1. Esto garantiza que nunca empujemos algo al siguiente "cubo" innecesariamente.
Las macros PAGE_
tienen que ver con la matemática binaria. Fingiremos que estamos tratando con valores de 8 bits por simplicidad. Supongamos que PAGE_SIZE
es 0b00100000
. PAGE_SIZE-1
es así 0b00011111
. ~(PAGE_SIZE-1)
es entonces 0b11100000
.
Un &
binario alineará dos números binarios y dejará un 1 en cualquier lugar donde ambos números tengan un 1.Por lo tanto, si x
fue 0b01100111, la operación sería algo así:
0b01100111 (x)
& 0b11100000 (~(PAGE_SIZE-1))
------------
0b01100000
Se habrá dado cuenta de que la operación realmente sólo pone a cero de salida los últimos 5 bits. Eso es todo. Pero esa fue exactamente la operación que se necesitó para redondear al intervalo más cercano de PAGE_SIZE
. Tenga en cuenta que esto solo funcionó porque PAGE_SIZE
era exactamente una potencia de 2. Es como decir que para cualquier número decimal arbitrario, puede redondear al 100 más cercano simplemente poniendo en cero los últimos dos dígitos. Funciona perfectamente, y es realmente fácil de hacer, pero no funcionaría en absoluto si estuviera intentando redondear al múltiplo 76 más cercano.
PAGE_ROUND_UP
hace lo mismo, pero agrega todo lo que puede a la página antes de cortarla Es como hacer un redondeo al múltiplo de 100 más cercano agregando 99 a cualquier número y , luego poniendo a cero los últimos dos dígitos. (Añadimos PAGE_SIZE-1
por la misma razón que agregamos S-1
arriba.)
¡Buena suerte con su memoria virtual!