Operadores de desplazamiento y bit a bit (referencia de C#)

Los operadores bit a bit y de desplazamiento incluyen complementos unarios bit a bit, desplazamiento binario a izquierda y derecha, desplazamiento a la derecha sin signo y los operadores OR lógicos binarios AND, OR y OR exclusivos. Estos operandos toman operandos de los tipos numéricos enteros o el tipo char.

Estos operadores se definen para los tipos int, uint, long y ulong. Cuando ambos operandos son de otro tipo entero (sbyte, byte, short, ushort o char), sus valores se convierten en el tipo int, que también es el tipo de resultado de una operación. Cuando los operandos son de tipo entero diferente, sus valores se convierten en el tipo entero más cercano que contenga. Para obtener más información, vea la sección Promociones numéricas de Especificación del lenguaje C#. Los operadores compuestos (como >>=) no convierten sus argumentos en int o tienen el tipo de resultado como int.

Los operadores &, | y ^ también se definen para los operandos de tipo bool. Para obtener más información, vea Operadores lógicos booleanos.

Las operaciones de desplazamiento y bit a bit nunca producen desbordamiento y generan los mismos resultados en contextos Checked y Unchecked.

Operador de complemento bit a bit ~

El operador ~ genera un complemento bit a bit de su operando al invertir cada bit:

uint a = 0b_0000_1111_0000_1111_0000_1111_0000_1100;
uint b = ~a;
Console.WriteLine(Convert.ToString(b, toBase: 2));
// Output:
// 11110000111100001111000011110011

También se puede usar el símbolo ~ para declarar finalizadores. Para obtener más información, vea Finalizadores.

Operador de desplazamiento izquierdo <<

El operador << desplaza el operando izquierdo a la izquierda el número de bits definido por el operando derecho. Para obtener información sobre cómo el operando derecho define el recuento de desplazamiento, vea la sección Recuento de desplazamiento de los operadores de desplazamiento.

La operación de desplazamiento izquierdo descarta los bits de orden superior que están fuera del rango del tipo de resultado y establece las posiciones de bits vacías de orden inferior en cero, como se muestra en el ejemplo siguiente:

uint x = 0b_1100_1001_0000_0000_0000_0000_0001_0001;
Console.WriteLine($"Before: {Convert.ToString(x, toBase: 2)}");

uint y = x << 4;
Console.WriteLine($"After:  {Convert.ToString(y, toBase: 2)}");
// Output:
// Before: 11001001000000000000000000010001
// After:  10010000000000000000000100010000

Dado que los operadores de desplazamiento solo se definen para los tipos int, uint, long y ulong, el resultado de una operación siempre contiene al menos 32 bits. Si el operando izquierdo es de otro tipo entero (sbyte, byte, short, ushort o char), su valor se convierte al tipo int, como se muestra en el ejemplo siguiente:

byte a = 0b_1111_0001;

var b = a << 8;
Console.WriteLine(b.GetType());
Console.WriteLine($"Shifted byte: {Convert.ToString(b, toBase: 2)}");
// Output:
// System.Int32
// Shifted byte: 1111000100000000

Operador de desplazamiento a la derecha >>

El operador >> desplaza el operando izquierdo a la derecha el número de bits definido por el operando derecho. Para obtener información sobre cómo el operando derecho define el recuento de desplazamiento, vea la sección Recuento de desplazamiento de los operadores de desplazamiento.

La operación de desplazamiento derecho descarta los bits de orden inferior, como se muestra en el ejemplo siguiente:

uint x = 0b_1001;
Console.WriteLine($"Before: {Convert.ToString(x, toBase: 2), 4}");

uint y = x >> 2;
Console.WriteLine($"After:  {Convert.ToString(y, toBase: 2).PadLeft(4, '0'), 4}");
// Output:
// Before: 1001
// After:  0010

Las posiciones de bits vacíos de orden superior se establecen basándose en el tipo del operando izquierdo, tal como se indica a continuación:

  • Si el operando izquierdo es de tipo int o long, el operador de desplazamiento a la derecha realiza un desplazamiento aritmético: el valor del bit más significativo (el bit de signo) del operando izquierdo se propaga a las posiciones de bits vacíos de orden superior. Es decir, las posiciones de bits vacíos de orden superior se establecen en cero si el operando izquierdo no es negativo y, en caso de serlo, se establecen en uno.

    int a = int.MinValue;
    Console.WriteLine($"Before: {Convert.ToString(a, toBase: 2)}");
    
    int b = a >> 3;
    Console.WriteLine($"After:  {Convert.ToString(b, toBase: 2)}");
    // Output:
    // Before: 10000000000000000000000000000000
    // After:  11110000000000000000000000000000
    
  • Si el operando izquierdo es de tipo uint o ulong, el operador de desplazamiento a la derecha realiza un desplazamiento lógico: las posiciones de bits vacíos de orden superior se establecen siempre en cero.

    uint c = 0b_1000_0000_0000_0000_0000_0000_0000_0000;
    Console.WriteLine($"Before: {Convert.ToString(c, toBase: 2), 32}");
    
    uint d = c >> 3;
    Console.WriteLine($"After:  {Convert.ToString(d, toBase: 2).PadLeft(32, '0'), 32}");
    // Output:
    // Before: 10000000000000000000000000000000
    // After:  00010000000000000000000000000000
    

Nota

Use el operador de desplazamiento a la derecha sin signo para realizar un desplazamiento lógico en operandos de tipos enteros con signo. Esto es preferible a convertir un operando izquierdo en un tipo sin signo y, a continuación, devolver el resultado de una operación de desplazamiento a un tipo con signo.

Operador de desplazamiento a la derecha sin signo >>>

Disponible en C# 11 y posteriores, el >>> operador desplaza el operando izquierdo a la derecha el número de bits definido por su operando derecho. Para obtener información sobre cómo el operando derecho define el recuento de desplazamiento, vea la sección Recuento de desplazamiento de los operadores de desplazamiento.

El >>> operador siempre realiza un desplazamiento lógico. Es decir, las posiciones de bits vacías de orden alto siempre se establecen en cero, independientemente del tipo del operando izquierdo. El >> operador realiza un desplazamiento aritmético (es decir, el valor del bit más significativo se propaga a las posiciones de bits vacías de orden alto) si el operando izquierdo es de un tipo con signo. En el ejemplo siguiente se muestra la diferencia entre los operadores de >> y >>> para un operando izquierdo negativo:

int x = -8;
Console.WriteLine($"Before:    {x,11}, hex: {x,8:x}, binary: {Convert.ToString(x, toBase: 2), 32}");

int y = x >> 2;
Console.WriteLine($"After  >>: {y,11}, hex: {y,8:x}, binary: {Convert.ToString(y, toBase: 2), 32}");

int z = x >>> 2;
Console.WriteLine($"After >>>: {z,11}, hex: {z,8:x}, binary: {Convert.ToString(z, toBase: 2).PadLeft(32, '0'), 32}");
// Output:
// Before:             -8, hex: fffffff8, binary: 11111111111111111111111111111000
// After  >>:          -2, hex: fffffffe, binary: 11111111111111111111111111111110
// After >>>:  1073741822, hex: 3ffffffe, binary: 00111111111111111111111111111110

Operador AND lógico y

El operador & calcula el AND lógico bit a bit de sus operandos enteros:

uint a = 0b_1111_1000;
uint b = 0b_1001_1101;
uint c = a & b;
Console.WriteLine(Convert.ToString(c, toBase: 2));
// Output:
// 10011000

Para los operandos bool, el operador & calcula el AND lógico de sus operandos. El operador & unario es el operador address-of.

Operador IR exclusivo lógico ^

El operador ^ calcula el OR exclusivo lógico bit a bit, también conocido como XOR lógico bit a bit, de sus operandos enteros:

uint a = 0b_1111_1000;
uint b = 0b_0001_1100;
uint c = a ^ b;
Console.WriteLine(Convert.ToString(c, toBase: 2));
// Output:
// 11100100

Para los operandos bool, el operador ^ calcula el OR exclusivo lógico de sus operandos.

Operador lógico OR |

El operador | calcula el OR lógico bit a bit de sus operandos enteros:

uint a = 0b_1010_0000;
uint b = 0b_1001_0001;
uint c = a | b;
Console.WriteLine(Convert.ToString(c, toBase: 2));
// Output:
// 10110001

Para los operandos bool, el operador | calcula el OR lógico de sus operandos.

Asignación compuesta

Para un operador binario op, una expresión de asignación compuesta con el formato

x op= y

es equivalente a

x = x op y

salvo que x solo se evalúa una vez.

En el ejemplo siguiente se muestra el uso de la asignación compuesta con operadores de desplazamiento y bit a bit:

uint INITIAL_VALUE = 0b_1111_1000;

uint a = INITIAL_VALUE;
a &= 0b_1001_1101; 
Display(a);  // output: 10011000

a = INITIAL_VALUE;
a |= 0b_0011_0001; 
Display(a);  // output: 11111001

a = INITIAL_VALUE;
a ^= 0b_1000_0000;
Display(a);  // output: 01111000

a = INITIAL_VALUE;
a <<= 2;
Display(a);  // output: 1111100000

a = INITIAL_VALUE;
a >>= 4;
Display(a);  // output: 00001111

a = INITIAL_VALUE;
a >>>= 4;
Display(a);  // output: 00001111

void Display(uint x) => Console.WriteLine($"{Convert.ToString(x, toBase: 2).PadLeft(8, '0'), 8}");

A causa de las promociones numéricas, el resultado de la operación op podría no ser convertible de forma implícita en el tipo T de x. En tal caso, si op es un operador predefinido y el resultado de la operación es convertible de forma explícita en el tipo T de x, una expresión de asignación compuesta con el formato x op= y es equivalente a x = (T)(x op y), excepto que x solo se evalúa una vez. En el ejemplo siguiente se muestra ese comportamiento:

byte x = 0b_1111_0001;

int b = x << 8;
Console.WriteLine($"{Convert.ToString(b, toBase: 2)}");  // output: 1111000100000000

x <<= 8;
Console.WriteLine(x);  // output: 0

Prioridad de operadores

En la lista siguiente se ordenan los operadores de desplazamiento y bit a bit desde la prioridad más alta a la más baja:

  • Operador de complemento bit a bit ~
  • Operadores de desplazamiento <<, >>y >>>
  • Operador AND lógico &
  • Operador OR exclusivo lógico ^
  • Operador OR lógico |

Use los paréntesis, (), para cambiar el orden de evaluación impuesto por la prioridad de los operadores:

uint a = 0b_1101;
uint b = 0b_1001;
uint c = 0b_1010;

uint d1 = a | b & c;
Display(d1);  // output: 1101

uint d2 = (a | b) & c;
Display(d2);  // output: 1000

void Display(uint x) => Console.WriteLine($"{Convert.ToString(x, toBase: 2), 4}");

Para obtener la lista completa de los operadores de C# ordenados por nivel de prioridad, vea la sección Prioridad de operadores del artículo Operadores de C#.

Recuento de desplazamiento de los operadores de desplazamiento

Para las expresiones de x << count, x >> county x >>> count , el recuento de desplazamientos real depende del tipo de xde la siguiente manera:

  • Si el tipo de x es int o uint, el recuento de desplazamiento viene definido por los cinco bits de orden inferior del operando derecho. Es decir, el valor de desplazamiento se calcula a partir de count & 0x1F (o count & 0b_1_1111).

  • Si el tipo de x es long o ulong, el recuento de desplazamiento viene definido por los seis bits de orden inferior del operando derecho. Es decir, el valor de desplazamiento se calcula a partir de count & 0x3F (o count & 0b_11_1111).

En el ejemplo siguiente se muestra ese comportamiento:

int count1 = 0b_0000_0001;
int count2 = 0b_1110_0001;

int a = 0b_0001;
Console.WriteLine($"{a} << {count1} is {a << count1}; {a} << {count2} is {a << count2}");
// Output:
// 1 << 1 is 2; 1 << 225 is 2

int b = 0b_0100;
Console.WriteLine($"{b} >> {count1} is {b >> count1}; {b} >> {count2} is {b >> count2}");
// Output:
// 4 >> 1 is 2; 4 >> 225 is 2

int count = -31;
int c = 0b_0001;
Console.WriteLine($"{c} << {count} is {c << count}");
// Output:
// 1 << -31 is 2

Nota

Tal y como se muestra en el ejemplo anterior, el resultado de una operación de desplazamiento puede ser distinto de cero incluso si el valor del operando derecho es mayor que el número de bits del operando izquierdo.

Operadores lógicos de enumeración

Los operadores ~, &, | y ^ también se admiten en cualquier tipo de enumeración. En el caso de los operandos del mismo tipo de enumeración, se realiza una operación lógica en los valores correspondientes del tipo entero subyacente. Por ejemplo, para cualquier x e y de un tipo de enumeración T con un tipo subyacente U, la expresión x & y produce el mismo resultado que la expresión (T)((U)x & (U)y).

Normalmente, los operadores lógicos bit a bit se usan con un tipo de enumeración definido con el atributo Flags. Para obtener más información, vea la sección Tipos de enumeración como marcas de bits del artículo Tipos de enumeración.

Posibilidad de sobrecarga del operador

Un tipo definido por el usuario puede sobrecargar los operadores ~, <<, >>, >>>, &, |y ^ . Cuando se sobrecarga un operador binario, también se sobrecarga de forma implícita el operador de asignación compuesta correspondiente. Un tipo definido por el usuario no puede sobrecargar de forma explícita un operador de asignación compuesta.

Si un tipo definido por el usuario T sobrecarga el operador <<, >>o >>> , el tipo del operando izquierdo debe ser T. En C# 10 y versiones anteriores, el tipo del operando derecho debe ser int; a partir de C# 11, el tipo del operando derecho de un operador de desplazamiento sobrecargado puede ser cualquiera.

especificación del lenguaje C#

Para más información, vea las secciones siguientes de la Especificación del lenguaje C#:

Consulte también