significa - operadores unarios c++
¿Los operadores de cambio(<<,>>) son aritméticos o lógicos en C? (11)
TL; DR
Considere que
i
y
n
son los operandos izquierdo y derecho respectivamente de un operador de cambio;
El tipo de
i
, después de la promoción de enteros, será
T
Suponiendo que
n
esté en
[0, sizeof(i) * CHAR_BIT)
- indefinido de lo contrario - tenemos estos casos:
| Direction | Type | Value (i) | Result |
| ---------- | -------- | --------- | ------------------------ |
| Right (>>) | unsigned | ≥ 0 | −∞ ← (i ÷ 2ⁿ) |
| Right | signed | ≥ 0 | −∞ ← (i ÷ 2ⁿ) |
| Right | signed | < 0 | Implementation-defined† |
| Left (<<) | unsigned | ≥ 0 | (i * 2ⁿ) % (T_MAX + 1) |
| Left | signed | ≥ 0 | (i * 2ⁿ) ‡ |
| Left | signed | < 0 | Undefined |
† la mayoría de los compiladores implementan esto como un cambio aritmético
‡ no definido si el valor desborda el tipo de resultado T;
tipo de i promovido
Cambiando
Primero está la diferencia entre los cambios lógicos y aritméticos desde un punto de vista matemático, sin preocuparse por el tamaño del tipo de datos. Los cambios lógicos siempre llenan los bits descartados con ceros, mientras que los cambios aritméticos los llenan con ceros solo para el desplazamiento a la izquierda, pero para los cambios a la derecha copian el MSB, conservando así el signo del operando (asumiendo una codificación de complemento a dos para valores negativos).
En otras palabras, el cambio lógico considera el operando desplazado como un simple flujo de bits y los mueve, sin preocuparse por el signo del valor resultante. El cambio aritmético lo considera como un número (firmado) y conserva el signo a medida que se realizan los cambios.
Un desplazamiento aritmético izquierdo de un número X por n es equivalente a multiplicar X por 2 n y, por lo tanto, es equivalente a un desplazamiento lógico hacia la izquierda; un cambio lógico también daría el mismo resultado, ya que MSB se cae del final y no hay nada que conservar.
¡Un desplazamiento aritmético a la derecha de un número X por n es equivalente a la división entera de X por 2 n SOLAMENTE si X no es negativo! La división entera no es más que la división matemática y round hacia 0 ( trunc ).
Para los números negativos, representados por la codificación de complemento a dos, el desplazamiento hacia la derecha por n bits tiene el efecto de dividirlo matemáticamente por 2n y redondearlo hacia −∞ ( floor ); por lo tanto, el desplazamiento a la derecha es diferente para los valores negativos y no negativos.
para X ≥ 0, X >> n = X / 2 n = trunc (X ÷ 2 n )
para X <0, X >> n = piso (X 2 n )
donde
÷
es división matemática,
/
es división entera.
Veamos un ejemplo:
37) 10 = 100101) 2
37 ÷ 2 = 18.5
37/2 = 18 (redondeando 18.5 hacia 0) = 10010) 2 [resultado del desplazamiento aritmético a la derecha]
-37) 10 = 11011011) 2 (considerando el complemento de dos, representación de 8 bits)
-37 ÷ 2 = -18.5
-37 / 2 = -18 (redondeando 18.5 hacia 0) = 11101110) 2 [NO es el resultado del desplazamiento aritmético a la derecha]
-37 >> 1 = -19 (redondeando 18.5 hacia −∞) = 11101101) 2 [resultado del desplazamiento aritmético a la derecha]
Como señaló Guy Steele , esta discrepancia ha llevado a Wikipedia . Aquí, los valores no negativos (matemáticos) se pueden asignar a valores no negativos firmados y sin firmar (C); ambos se tratan de la misma manera y el desplazamiento a la derecha se realiza por división entera.
Por lo tanto, la lógica y la aritmética son equivalentes en el desplazamiento a la izquierda y para los valores no negativos en el desplazamiento a la derecha; Es en el correcto desplazamiento de los valores negativos que difieren.
Operando y tipos de resultados
Norma C99 §6.5.7 :
Cada uno de los operandos tendrá tipos enteros.
Las promociones enteras se realizan en cada uno de los operandos. El tipo de resultado es el del operando izquierdo promovido. Si el valor del operando derecho es negativo o es mayor o igual que el ancho del operando izquierdo promovido, el comportamiento no está definido.
short E1 = 1, E2 = 3;
int R = E1 << E2;
En el fragmento de código anterior, ambos operandos se convierten en
int
(debido a la promoción de enteros);
si
E2
fue negativo o
E2 ≥ sizeof(int) * CHAR_BIT
entonces la operación no está definida.
Esto se debe a que desplazar más de los bits disponibles seguramente se va a desbordar.
Si
R
hubiera sido declarado como
short
, el resultado
int
de la operación de cambio se convertiría implícitamente a
short
;
una conversión restringida, que puede llevar a un comportamiento definido por la implementación si el valor no es representable en el tipo de destino.
Shift izquierdo
El resultado de E1 << E2 es E1 posiciones de bit E2 desplazadas a la izquierda; bits vacíos están llenos de ceros. Si E1 tiene un tipo sin signo, el valor del resultado es E1 × 2 E2 , módulo reducido uno más que el valor máximo representable en el tipo de resultado. Si E1 tiene un tipo con signo y un valor no negativo, y E1 × 2 E2 se puede representar en el tipo de resultado, ese es el valor resultante; de lo contrario, el comportamiento es indefinido.
Como los desplazamientos a la izquierda son iguales para ambos, los bits vacíos simplemente se rellenan con ceros. A continuación, establece que para los tipos sin signo y con signo es un cambio aritmético. Lo estoy interpretando como un cambio aritmético, ya que los cambios lógicos no se preocupan por el valor representado por los bits, solo lo ve como un flujo de bits; pero el estándar no habla en términos de bits, sino definiéndolo en términos del valor obtenido por el producto de E1 con 2 E2 .
La advertencia aquí es que para los tipos con signo, el valor debe ser no negativo y el valor resultante debe ser representable en el tipo de resultado. De lo contrario, la operación no está definida. El tipo de resultado sería el tipo de E1 después de aplicar la promoción integral y no el tipo de destino (la variable que mantendrá el resultado). El valor resultante se convierte implícitamente al tipo de destino; si no se puede representar en ese tipo, entonces la conversión está definida por la implementación (C99 §6.3.1.3 / 3).
Si E1 es un tipo firmado con un valor negativo, entonces el comportamiento del desplazamiento a la izquierda no está definido. Esta es una ruta fácil hacia un comportamiento indefinido que puede pasarse por alto fácilmente.
Giro a la derecha
El resultado de E1 >> E2 es E1 posiciones de bit E2 desplazadas a la derecha. Si E1 tiene un tipo sin signo o si E1 tiene un tipo con signo y un valor no negativo, el valor del resultado es la parte integral del cociente de E1 / 2 E2 . Si E1 tiene un tipo con signo y un valor negativo, el valor resultante se define por la implementación.
El cambio a la derecha para valores no negativos firmados y sin firmar es bastante sencillo; Los bits vacantes están llenos de ceros. Para valores negativos firmados, el resultado del desplazamiento a la derecha está definido por la implementación. Dicho esto, la mayoría de las implementaciones como GCC y Visual C ++ implementan el desplazamiento a la derecha como un cambio aritmético al preservar el bit de signo.
Conclusión
A diferencia de Java, que tiene un operador especial
>>>
para el cambio lógico aparte del habitual
>>
y
<<
, C y C ++ solo tienen cambio aritmético con algunas áreas que quedan indefinidas y definidas por la implementación.
La razón por la que los considero aritméticos se debe a la redacción estándar de la operación matemática en lugar de tratar al operando desplazado como un flujo de bits;
esta es quizás la razón por la que deja esas áreas sin definir / implementadas en lugar de definir todos los casos como cambios lógicos.
En C, ¿los operadores de desplazamiento (
<<
,
>>
) son aritméticos o lógicos?
Aquí hay funciones para garantizar el desplazamiento lógico hacia la derecha y el desplazamiento hacia la derecha aritmética de un int en C:
int logicalRightShift(int x, int n) {
return (unsigned)x >> n;
}
int arithmeticRightShift(int x, int n) {
if (x < 0 && n > 0)
return x >> n | ~(~0U >> n);
else
return x >> n;
}
Bueno, lo busqué en wikipedia , y tienen esto para decir:
C, sin embargo, tiene un solo operador de cambio a la derecha, >>. Muchos compiladores de C eligen el cambio a la derecha para realizar dependiendo del tipo de entero que se está cambiando; los enteros a menudo con signo se desplazan utilizando el cambio aritmético, y los enteros sin signo se desplazan utilizando el cambio lógico.
Así que suena como que depende de tu compilador. También en ese artículo, tenga en cuenta que el desplazamiento a la izquierda es el mismo para la aritmética y la lógica. Recomendaría realizar una prueba simple con algunos números firmados y sin firmar en el caso del borde (conjunto de bits altos, por supuesto) y ver cuál es el resultado en su compilador. También recomendaría evitar depender de que sea uno u otro, ya que parece que C no tiene un estándar, al menos si es razonable y posible evitar tal dependencia.
Cuando lo hagas, el desplazamiento a la izquierda por 1 lo multiplicas por 2, el desplazamiento a la derecha por 1 lo divides por 2
x = 5
x >> 1
x = 2 ( x=5/2)
x = 5
x << 1
x = 10 (x=5*2)
Cuando se desplaza a la izquierda, no hay diferencia entre el cambio aritmético y lógico. Cuando se desplaza a la derecha, el tipo de cambio depende del tipo de valor que se desplaza.
(Como fondo para aquellos lectores que no están familiarizados con la diferencia, un cambio "lógico" a la derecha en un bit desplaza todos los bits a la derecha y completa el bit de la izquierda con un 0. Un cambio "aritmético" deja el valor original en el bit de la izquierda La diferencia cobra importancia cuando se trata de números negativos.
Cuando se desplaza un valor sin signo, el operador >> en C es un cambio lógico. Cuando se desplaza un valor con signo, el operador >> es un cambio aritmético.
Por ejemplo, suponiendo una máquina de 32 bits:
signed int x1 = 5;
assert((x1 >> 1) == 2);
signed int x2 = -5;
assert((x2 >> 1) == -3);
unsigned int x3 = (unsigned int)-5;
assert((x3 >> 1) == 0x7FFFFFFD);
Desplazamiento a la izquierda
<<
Esto es de alguna manera fácil y siempre que use el operador de cambio, siempre es una operación de bit-bit, por lo que no podemos usarlo con una operación de doble flotación.
Cada vez que dejamos el desplazamiento uno cero, se agrega al bit menos significativo (
LSB
).
Pero en el turno derecho
>>
tenemos que seguir una regla adicional y esa regla se llama "signo de copia de bits".
El significado de "signo de copia de bit" es si el bit más significativo (
MSB
) se establece, luego de un cambio a la derecha nuevamente, el
MSB
se configurará si se restableció y luego se restablece, significa que si el valor anterior era cero, luego de cambiar nuevamente , el bit es cero si el bit anterior era uno, luego del cambio es nuevamente uno.
Esta regla no es aplicable para un turno de izquierda.
El ejemplo más importante en el desplazamiento a la derecha si cambia cualquier número negativo al desplazamiento a la derecha, luego de algunos cambios, el valor finalmente llega a cero y luego, si cambia este -1 cualquier cantidad de veces, el valor permanecerá igual. Por favor, compruebe.
En términos del tipo de cambio que recibe, lo importante es el tipo de valor que está cambiando. Una fuente clásica de errores es cuando se desplaza un literal a, digamos, enmascarar bits. Por ejemplo, si desea eliminar el bit más a la izquierda de un entero sin signo, puede probar esto como su máscara:
~0 >> 1
Desafortunadamente, esto lo pondrá en problemas porque la máscara tendrá todos sus bits establecidos porque el valor que se está cambiando (~ 0) está firmado, por lo que se realiza un cambio aritmético. En su lugar, querría forzar un cambio lógico declarando explícitamente el valor como no firmado, es decir, haciendo algo como esto:
~0U >> 1;
GCC hace
-
para -ve -> Cambio aritmético
-
Para + ve -> Cambio lógico
Según la 2ª edición de K&R, los resultados dependen de la implementación para los cambios a la derecha de los valores firmados.
Wikipedia dice que C / C ++ ''generalmente'' implementa un cambio aritmético en los valores firmados.
Básicamente necesitas probar tu compilador o no confiar en él. Mi ayuda de VS2008 para el compilador actual de MS C ++ dice que su compilador hace un cambio aritmético.
Según muchos compiladores de c :
-
<<
es un desplazamiento aritmético a la izquierda o desplazamiento a la izquierda en modo bit -
>>
es un shittor derecho aritmético a la derecha a la derecha.
gcc usualmente usará cambios lógicos en variables sin signo y para cambios a la izquierda en variables con signo. El cambio a la derecha aritmética es el verdaderamente importante porque significará que extenderá la variable.
gcc utilizará esto cuando corresponda, ya que es probable que otros compiladores lo hagan.