c# - tamaño - tabla de bit
¿Existe una función incorporada para invertir el orden de los bits? (6)
Decidí hacer algunas pruebas de rendimiento sobre los métodos de reversión.
Usando graphics.stanford.edu/~seander/bithacks.html escribí los siguientes métodos:
public static byte[] BitReverseTable =
{
0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
};
public static byte ReverseWithLookupTable(byte toReverse)
{
return BitReverseTable[toReverse];
}
public static byte ReverseBitsWith4Operations(byte b)
{
return (byte)(((b * 0x80200802ul) & 0x0884422110ul) * 0x0101010101ul >> 32);
}
public static byte ReverseBitsWith3Operations(byte b)
{
return (byte)((b * 0x0202020202ul & 0x010884422010ul) % 1023);
}
public static byte ReverseBitsWith7Operations(byte b)
{
return (byte)(((b * 0x0802u & 0x22110u) | (b * 0x8020u & 0x88440u)) * 0x10101u >> 16);
}
public static byte ReverseBitsWithLoop(byte v)
{
byte r = v; // r will be reversed bits of v; first get LSB of v
int s = 7; // extra shift needed at end
for (v >>= 1; v != 0; v >>= 1)
{
r <<= 1;
r |= (byte)(v & 1);
s--;
}
r <<= s; // shift when v''s highest bits are zero
return r;
}
public static byte ReverseWithUnrolledLoop(byte b)
{
byte r = b;
b >>= 1;
r <<= 1;
r |= (byte)(b & 1);
b >>= 1;
r <<= 1;
r |= (byte)(b & 1);
b >>= 1;
r <<= 1;
r |= (byte)(b & 1);
b >>= 1;
r <<= 1;
r |= (byte)(b & 1);
b >>= 1;
r <<= 1;
r |= (byte)(b & 1);
b >>= 1;
r <<= 1;
r |= (byte)(b & 1);
b >>= 1;
r <<= 1;
r |= (byte)(b & 1);
b >>= 1;
return r;
}
Luego lo probé, y aquí están los resultados:
Características de la prueba:
- 100000000 bytes aleatorios para invertir
- SO: Windows 7 x64
- CPU: AMD Phenom II 955 (4 núcleos a 3,2 GHz)
- RAM: 4GB
- IDE: Visual Studio 2010
Marco de destino 3.5
-----------------------------------------------------
| Method | Ticks(x64 mode) | Ticks(x86 mode) |
-----------------------------------------------------
| Loop | 4861859 | 4079554 |
| Unrolled Loop | 3241781 | 2948026 |
| Look-up table | 894809 | 312410 |
| 3-Operations | 2068072 | 6757008 |
| 4-Operations | 893924 | 1972576 |
| 7-Operations | 1219189 | 303499 |
-----------------------------------------------------
Marco de destino 4
-----------------------------------------------------
| Method | Ticks(x64 mode) | Ticks(x86 mode) |
-----------------------------------------------------
| Loop | 4682654 | 4147036 |
| Unrolled Loop | 3154920 | 2851307 |
| Look-up table | 602686 | 313940 |
| 3-Operations | 2067509 | 6661542 |
| 4-Operations | 893406 | 2018334 |
| 7-Operations | 1193200 | 991792 |
-----------------------------------------------------
Por lo tanto, el método de la tabla de consulta no siempre es el más rápido :)
Esto puede ser razonable, ya que el acceso a la memoria es más lento que el de los registros de la CPU, por lo que si algún método se compila y optimiza lo suficiente para evitar el acceso a la memoria (y para hacer pocas operaciones) es más rápido. (De todos modos, la brecha es extremadamente reducida por el almacenamiento en memoria caché de la CPU)
También es interesante ver los diferentes comportamientos en el caso del modo x64 o x86, y cómo los frameworks 3.5 y 4.0 realizan distintas optimizaciones.
He ideado varias formas manuales de hacer esto, pero sigo preguntándome si hay algo integrado en .NET que haga esto.
Básicamente, quiero invertir el orden de los bits en un byte, de modo que el bit menos significativo se convierta en el bit más significativo.
Por ejemplo: 1001 1101 = 9D se convertiría en 1011 1001 = B9
Una de las formas de hacer esto es usar operaciones bitwise si sigue este pseudo código:
for (i = 0; i<8; i++)
{
Y>>1
x= byte & 1
byte >>1
y = x|y;
}
Me pregunto si hay una función en algún lugar que me permita hacer todo esto en una sola línea . Además, ¿conoce el término para una operación de este tipo? Estoy seguro de que existe, pero no lo recuerdo en este momento.
Gracias
No, no hay nada en el BCL para esto.
Pero, asumiendo que quieres algo rápido:
Como solo hay 8 bits, vale la pena desenrollar el bucle (use 4 instrucciones en lugar del bucle for).
Para una solución aún más rápida, cree una tabla de búsqueda de 256 entradas.
Y, por supuesto, puede envolver ambos métodos en una función para que el uso solo tome 1 declaración.
Encontré una página para este problema.
Por favor, vea estos graphics.stanford.edu/~seander/bithacks.html integrales graphics.stanford.edu/~seander/bithacks.html , a saber, que quiere ''Revertir los bits en un byte con 3 operaciones (multiplicación de 64 bits y división de módulo)''
int lVal = 0x9D;
int lNewVal = (int)((((ulong)lVal * 0x0202020202UL) & 0x010884422010UL) % 1023);
System.Diagnostics.Debug.WriteLine(string.Format("{0:X2}", lNewVal));
Cuando ejecute esto, encontrará que el valor se invierte a 0xB9.
Puede encontrar algoritmos de fxtbook bits en el fxtbook . El capítulo 1.14 da estos algoritmos de intercambio de bits:
static uint bitSwap1(uint x) {
uint m = 0x55555555;
return ((x & m) << 1) | ((x & (~m)) >> 1);
}
static uint bitSwap2(uint x) {
uint m = 0x33333333;
return ((x & m) << 2) | ((x & (~m)) >> 2);
}
static uint bitSwap4(uint x) {
uint m = 0x0f0f0f0f;
return ((x & m) << 4) | ((x & (~m)) >> 4);
}
Lo que hace que su valor de bit de inversión de bits:
public static byte swapBits(byte value) {
return (byte)(bitSwap4(bitSwap2(bitSwap1(value))));
}
El compilador JIT x86 no hace un gran trabajo optimizando este código. Si la velocidad es importante, entonces podría usarla para inicializar un byte [] para que sea una búsqueda rápida en su lugar.
Utilizando el enlace @Chads
byte b;
b = 0x9D;
b = (byte)((b * 0x0202020202 & 0x010884422010) % 1023);
Edición : Olvidé el elenco
private UInt32 BitReverse(UInt32 value)
{
UInt32 left = (UInt32)1 << 31;
UInt32 right = 1;
UInt32 result = 0;
for (int i = 31; i >= 1; i -= 2)
{
result |= (value & left) >> i;
result |= (value & right) << i;
left >>= 1;
right <<= 1;
}
return result;
}