Siempre supuse que al hacer (a % 256)
el optimizador, naturalmente, usaría una operación eficiente a nivel de bits, como si escribiera (a & 0xFF)
.
Al probar en el explorador del compilador gcc-6.2 (-O3):
// Type your code here, or load an example.
int mod(int num) {
return num % 256;
}
mod(int):
mov edx, edi
sar edx, 31
shr edx, 24
lea eax, [rdi+rdx]
movzx eax, al
sub eax, edx
ret
Y al probar el otro código:
// Type your code here, or load an example.
int mod(int num) {
return num & 0xFF;
}
mod(int):
movzx eax, dil
ret
Parece que me estoy perdiendo algo por completo. ¿Algunas ideas?
c++
optimization
Elad Weiss
fuente
fuente
%
tampoco lo es&
.num
es asíunsigned
?Respuestas:
No es lo mismo. Intente
num = -79
y obtendrá resultados diferentes de ambas operaciones.(-79) % 256 = -79
, mientras que(-79) & 0xff
es un número positivo.Usando
unsigned int
, las operaciones son las mismas, y el código probablemente será el mismo.PD- Alguien comentó
Así no se define en C, C ++, Objective-C (es decir, todos los lenguajes donde se compilaría el código de la pregunta).
fuente
Respuesta corta
-1 % 256
cede-1
y no255
cual es-1 & 0xFF
. Por lo tanto, la optimización sería incorrecta.Respuesta larga
C ++ tiene la convención que
(a/b)*b + a%b == a
parece bastante natural.a/b
siempre devuelve el resultado aritmético sin la parte fraccionaria (truncando hacia 0). Como consecuencia,a%b
tiene el mismo signo quea
o es 0.La división
-1/256
rinde0
y, por-1%256
lo tanto, debe ser-1
para satisfacer la condición anterior ((-1%256)*256 + -1%256 == -1
). Esto es obviamente diferente de lo-1&0xFF
que es0xFF
. Por lo tanto, el compilador no puede optimizar la forma que desea.La sección relevante en el estándar C ++ [expr.mul §4] a partir de N4606 establece:
Habilitando la optimización
Sin embargo, usando
unsigned
tipos, la optimización sería completamente correcta , satisfaciendo la convención anterior:Ver también esto .
Otros idiomas
Esto se maneja de manera muy diferente en diferentes lenguajes de programación, ya que puede consultar Wikipedia .
fuente
Desde C ++ 11,
num % 256
tiene que ser no positivo sinum
es negativo.Por lo tanto, el patrón de bits dependería de la implementación de tipos con signo en su sistema: para un primer argumento negativo, el resultado no es la extracción de los 8 bits menos significativos.
Sería un asunto diferente si
num
en su caso lo fueraunsigned
: en estos días casi esperaría que un compilador haga la optimización que usted cita.fuente
num
es negativo, entoncesnum % 256
es cero o negativo (también conocido como no positivo).(-250+256)%256==6
, pero(-250%256)+(256%256)
debe ser, según el estándar, "no positivo", y por lo tanto no6
. Romper la asociatividad de esa manera tiene efectos secundarios en la vida real: por ejemplo, al calcular la representación de "alejamiento" en coordenadas enteras, uno tiene que desplazar la imagen para que todas las coordenadas no sean negativas.(128+128)%256==0
pero(128%256)+(128%256)==256
. Quizás haya una buena objeción al comportamiento especificado, pero no me queda claro que sea el que usted dijo.256==0
. La clave es tener exactamenteN
los valores posibles en el móduloN
aritmético, que solo es posible si todos los resultados están en el rango0,...,(N-1)
, no-(N-1),...,(N-1)
.No tengo una visión telepática del razonamiento del compilador, pero en el caso de
%
que sea necesario tratar con valores negativos (y las rondas de división hacia cero), mientras que&
el resultado es siempre los 8 bits más bajos.La
sar
instrucción me suena como "desplazamiento aritmético a la derecha", llenando los bits desocupados con el valor del bit de signo.fuente
Matemáticamente hablando, el módulo se define como el siguiente:
a% b = a - b * piso (a / b)
Esto aquí debería aclararlo para ti. Podemos eliminar el piso para enteros porque la división de enteros es equivalente al piso (a / b). Sin embargo, si el compilador usara un truco general como usted sugiere, tendría que funcionar para todos ay todos b. Desafortunadamente, este no es el caso. Hablando matemáticamente, su truco es 100% correcto para enteros sin signo (veo que una respuesta indica que los enteros con signo se rompen, pero puedo confirmar o negar esto ya que -a% b debería ser positivo). Sin embargo, ¿puedes hacer este truco para todos los b? Probablemente no. Es por eso que el compilador no lo hace. Después de todo, si el módulo se escribiera fácilmente como una operación bit a bit, entonces simplemente agregaríamos un circuito de módulo como para la suma y las otras operaciones.
fuente
-2.3
es-3
, mientras que si truncas-2.3
a un entero obtienes-2
. Ver en.wikipedia.org/wiki/Truncation . "para números negativos, el truncamiento no se redondea en la misma dirección que la función de piso". Y el comportamiento de%
los números negativos es precisamente la razón por la que el OP está viendo el comportamiento descrito.