2009-11-19 18 views
8

¿alguien puede explicar cómo funciona esto? (Asz + 7) & ~ 7; Redondea asz al siguiente múltiplo más alto de 8.Bit Hack - Redondee a múltiples de 8

Es fácil ver que ~ 7 produce 11111000 (representación de 8 bits) y por lo tanto desconecta los últimos 3 bits, por lo tanto, cualquier número que se produzca es un múltiplo de 8

Mi pregunta es ¿cómo la adición de asz a 7 antes de enmascarar [editar] produce el siguiente más alto [editar final] múltiplo de 8? He intentado escribir en un papel

como:

1 + 7 = 8 = 1|000 (& ~7) -> 1000 
2 + 7 = 9 = 1|001 (& ~7) -> 1000 
3 + 7 = 10 = 1|010 (& ~7) -> 1000 
4 + 7 = 11 = 1|011 (& ~7) -> 1000 
5 + 7 = 12 = 1|100 (& ~7) -> 1000 
6 + 7 = 13 = 1|101 (& ~7) -> 1000 
7 + 7 = 14 = 1|110 (& ~7) -> 1000 
8 + 7 = 15 = 1|111 (& ~7) -> 1000 

Un patrón parece emerger con claridad a la que ha sido explotado .Can alguien por favor me ayude a cabo?

Gracias a todos por las respuestas. Me ayudó a confirmar lo que estaba pensando. Continué escribiendo el patrón de arriba y cuando crucé 10, pude ver claramente que los números se promocionan al siguiente "bloque de 8" si puedo decirlo.

Gracias de nuevo.

Respuesta

17

Bueno, si estuviera tratando de redondear abajo, no necesitarías la adición. Simplemente haciendo el paso de enmascaramiento limpiaría las partes inferiores y se redondearía al siguiente múltiplo inferior.

Si desea redondear hasta, primero tiene que sumar lo suficiente para "pasar" el siguiente múltiplo de 8. Luego, el mismo paso de enmascaramiento lo lleva de vuelta al múltiplo de 8. El motivo por el que elige 7 es que es el único número garantizado que es "lo suficientemente grande" para obtener de cualquier número hasta el siguiente múltiplo de 8 sin aumentar un múltiplo adicional si su número original ya era un múltiplo de 8.

En general, para redondear a una potencia de dos:

unsigned int roundTo(unsigned int value, unsigned int roundTo) 
{ 
    return (value + (roundTo - 1)) & ~(roundTo - 1); 
} 
+1

redondeando un entero n hasta cualquier número entero m se puede lograr con '(n/m) * m' –

+1

El bit' & ~ 'solo funciona si el roundTo es una potencia de 2. En general, se necesita' return ((value + roundTo - 1)/roundTo) * roundTo; 'en su lugar. –

+0

@GregRogers, ¿tiene alguna idea de lo costosa que es una instrucción dividida? – Johan

3

Bueno, la máscara produciría un múltiplo exacto de 8 por sí mismo. Agregar 7 a asz le asegura que obtendrá siguiente múltiplo más alto.

15

En realidad está agregando 7 al número y redondeando hacia abajo.

Esto tiene el efecto deseado de redondeo al siguiente múltiplo de 8. (La adición de 8 en lugar de 7 chocaría un valor de 8 a 16.)

+0

Dicho sea de paso, este truco no funcionará si el número que desea redondear a un múltiplo de no es un múltiplo de la base del número. Versión corta: No puede agregar 8 y redondear a 9, pero puede agregar 15 y redondear a 16. – Broam

+0

Broam, no entiendo muy bien lo que dice. Agregar 8 y redondear parece funcionar bien. – Joost

4

El +7 no es para producir un múltiplo exacto de 8, es para asegurarse de obtener el siguiente múltiplo más alto de ocho.

editar: Vencido por 16 segundos y varias órdenes de calidad. Oh bueno, volviendo a acechar.

0

Uhh, ¿acabas de responder a tu propia pregunta? agregando 7, está garantizando que el resultado será igual o superior al siguiente múltiplo de 8. truncar le da ese múltiplo.

1

Sin el 7 será el mayor múltiplo de 8 menor o igual a su número de orig

1

Adición 7 no produce un múltiplo de 8. El múltiplo de 8 es producida por anding con ~ 7. ~ 7 es el complemento de 7, que es 0xffff fff8 (excepto si se usan muchos bits en un int). Esto trunca o redondea hacia abajo.

Agregar 7 antes de hacer eso asegura que no se devuelve ningún valor inferior a asz. Ya has descubierto cómo funciona eso.