2010-01-27 10 views
7

Sé que puedo dividir entre 2 usando el desplazamiento a la derecha.Desplazar a la derecha para realizar la división por 2 en -1

Por simplicidad, echar un sistema de números de 4 bits

-1 - 1111 
-2 - 1110 
-3 - 1101 
-4 - 1100 
-5 - 1011 
-6 - 1010 
-7 - 1001 
-8 - 1000 
7 - 0111 
6 - 0110 
5 - 0101 
4 - 0100 
3 - 0011 
2 - 0010 
1 - 0001 
0 - 0000 

Si trato de realizar

6/2 = 0110 >> 1 = 0011 = 3 
-6/ 2 = 1010 >> 1 = 1101 = -3 

es válido para ambos + ve y -ve número

Sin embargo, cuando venga a 1

1/2 = 0001 >> 1 = 0000 = 0 
-1/ 2 = 1111 >> 1 = 1111 = -1 

Parece que hay un caso especial en -1, como desplazamiento a la derecha para moverlo a infinito negativo.

Actualmente, tengo que poner una especial si cheque para esto, ya que estoy esperando -1/2 = 0.

Me preguntaba ¿cómo se controla esta excepción chico en su código? ¿Tu hombre puso un cheque si?

Respuesta

18

Cualquier número impar negativo no va a funcionar. Sin embargo, para responder a su pregunta, si sabe que puede tener números negativos, simplemente divida por 2. Esto se convierte en un cambio con una corrección por parte del jit/compilador.

+2

+1 para señalar que, si desea dividir por 2, puede probar el operador de división y ver qué tan bien funciona. –

+2

+1 - gracias también por destacar que las suboptimizaciones a menudo no superan el trabajo realizado por el compilador, y la opción "ingenua" suele ser eficiente y correcta en cosas básicas como esta. – Matt

+0

Cualquier número impar negativo dará como resultado (como con cualquier número impar positivo) n/2 redondeado hacia -inf. Solo sucede que para n == -1, eso es -1. – Vatine

0

En el caso impar, ambas operaciones dan como resultado una operación de piso en el resultado.

  • 3/2 -> baja (1,5) => 1
  • -3/2 -> baja (-1,5) => -2

Se puede poner un cheque, algo así como \

if (isOdd(number) && isNegative(number)) 
    result++; 
+0

Pero esto es incorrecto. '(-3)/2' es' -1' porque la división entera redondea hacia cero, que no es lo mismo que 'floor'. –

4

Si desplazamiento a la derecha para dividir por dos, que siempre terminan "redondeo" abajo - hacia cero si es positivo, lejos de ella si es negativo.

Si esto no es lo que quiere, se puede corregir para ello:

if (n & 1 > 0 && n < 0) 
    result += 1; 
+0

+1: pero ¿por qué tienes que marcar 'n & 1> 0'. Solo basta con comprobar 'n <0' Esto funciona:' int result = x >> 1; \t \t if (x> = 0) { \t \t \t resultado de devolución; \t \t} \t \t else { \t \t \t retorno ++ resultado;} ' – eagertoLearn

+0

@eagertoLearn: Sólo enteros impares negativos dan el resultado deseado. – Zantier

4

Odio decirlo, pero no lo manejo en mi código, ya que no uso el cambio de bit para multiplicar o dividir. Esto me huele a premature optimisation.

¿Por qué crees que tienes que hacer la división con cambio de bit en lugar de la más legible x/2?

12

@Anon es técnicamente correcto.

Sin embargo, es mejor práctica utilizar el operador / para la división y dejar la micro-optimización en el compilador JIT. El compilador JIT es capaz de optimizar las divisiones por constantes como secuencias shift/add ... cuando esto es lo mejor para la plataforma de ejecución.

Hacer este tipo de cosas es (probablemente) una optimización prematura, y puede ser una anti-optimización si su código necesita ejecutarse rápidamente en múltiples plataformas Java.

+0

Esta es realmente la mejor respuesta aquí. Este tipo de cambio de código es al menos el tercer paso del proceso: el primero consiste en perfilar el código para determinar que se trata de un cuello de botella significativo, y el segundo ser consciente de cuál es el bytecode/JIT resultante y reconocer que es subóptimo . – corsiKa

3

Me aburrí un día y describí divisiones vs turnos para el poder de 2 cosas; pensé que lo publicaría aquí para cualquier persona interesada.

En HotSpot VM 1.6 en Windows, usando j /= 4 en -100000000 a 100000000 funcionó en aproximadamente 12 segundos mientras se usa j = (j >= 0) ? j >> 2 : ~(~j+1 >> 2) + 1; funcionó en solo 2.5s.

OpenJDK VM 1.6 en Linux tiene 5.5s para divisiones y 1.5s para turnos.

Esto sugeriría que el compilador JIT realmente no hace nada especial para el poder de 2 divisiones.

GCC logró optimizar la división para que fuera más rápido que los cambios y cambios.

~(~j+1 >> 2) + 1 usa el complemento de dos para voltear el número positivo, cambiarlo y voltearlo.

long j = 0; 
for (long i = -100000000; i < 100000000; i++) { 
    j = i; 
    j /= 4; 
} 
System.out.println(j);` 

vs

long j = 0; 
for (long i = -100000000; i < 100000000; i++) { 
    j = i; 
    j = (j >= 0) ? j >> 2 : ~(~j+1 >> 2) + 1; 
} 
System.out.println(j);` 
+0

puede publicar el punto de referencia? – bestsss

+0

¿qué quieres decir? el código de evaluación comparativa, las especificaciones de la computadora o el tiempo más detallado? – nik3daz

+0

el código de evaluación comparativa, ¡gracias! – bestsss

Cuestiones relacionadas