Introducción
Durante el trabajo con el generador BMP (mapa de bits) me enfrento al problema de convertir el número a una pequeña cadena hexadecimal endian. Aquí está la función que creo en JavaScript, pero me pregunto cómo puede funcionar el código pequeño de manera similar
let liEnd= num => num.toString(16).padStart(8,'0').match(/../g).reverse().join``;
console.log(liEnd(304767)) // 304767 dec = 0x4a67f hex
Desafío
Función de escritura que tomará un número entero sin signo de 32 bits en la entrada y producirá una cadena hexadecimal de 8 dígitos con poco orden endian. El algoritmo de ejemplo que hace el trabajo:
- convertir entumecido a cadena hexadecimal, por ejemplo:
304767 -> '4a67f'
- agregue ceros de relleno para obtener una cadena de 8 caracteres:
'0004a67f'
- dividir la cuerda en cuatro piezas de 2 caracteres:
'00','04','a6','7f'
- orden inverso de piezas
'7f','a6','04','00'
- unir piezas y devolver como resultado:
'7fa60400'
Ejemplo de entrada y salida
El número de entrada (o cadena con número dec) está a la izquierda de ->
, la cadena hexadecimal de salida está a la derecha
2141586432 -> 0004a67f
304767 -> 7fa60400
f=lambda n,i=4:i*'1'and'%02x'%(n%256)+f(n>>8,i-1)
guarda un byte :)R ,
5453 bytesPruébalo en línea!
Cada grupo de 2 caracteres es en realidad la representación hexadecimal de un dígito en base 256. se
scan()%/%256^(0:3)%%256
convierte en un número base 256 con 4 dígitos invertidos, se...%*%256^(3:0)
une a ellos como un solo entero yformat.hexmode(...,8)
convierte ese número en su representación hexadecimal con 8 dígitos.fuente
JavaScript (ES7),
5957 bytesManipulación de cuerdas.
Pruébalo en línea!
¿Cómo?
Primero convertimos a hexadecimal para asegurarnos de que se incluyan todos los iniciales:n + 232 0 0
Pruébalo en línea!
Usamos la expresión regular1
/\B../g
para unir todos los grupos de 2 dígitos, ignorando el inicial gracias a (sin límite de palabras ).\B
Pruébalo en línea!
Nosotros
reverse()
yjoin()
para obtener la cadena final.JavaScript (ES6), 61 bytes
Función recursiva.
Pruébalo en línea!
fuente
Zsh , 46 bytes
Pruébalo en línea!
fuente
C # (compilador interactivo de Visual C #) , 54 bytes
Guardado 4 bytes gracias a @PeterCordes
Pruébalo en línea!
Explicación
fuente
4278255360
constante de la máscara a16711935
(0xff00ff
) si cambia antes de enmascarar? ¿O eso cuesta padres adicionales? Además, si no,0xff00ff00
es de la misma longitud pero mucho más significativo para los humanos.>>
tiene mayor prioridad que&
, lo que ahorró un total de 4 bytes. ¡Gracias!Japt
-P
, 10 bytesIntentalo
fuente
-P
hacer?-P
: si la salida es una matriz, las salidas sin separador (es decir, unidas conP
) ". Por lo tanto, el indicador es para una unión implícita en lugar de explícita para guardar bytes. :)C (gcc) , 30 bytes
Pruébalo en línea!
fuente
Python 2 , 43 bytes
Pruébalo en línea!
-4 bytes gracias a benrg
Emite una lista de caracteres. Calculado recuperando, en orden, los dígitos hexadecimales de la entrada en los índices
6, 7, 4, 5, 2, 3, 0, 1
.fuente
[i^6]for i in range(8)
Guarda algunos bytes.C (gcc) endian agnóstico, sin bibliotecas estándar,
9291 bytesh(n)
es un entero de un solo dígito-> función auxiliar hexadecimal.f(x,p)
toma un entero y unchar[8]
puntero. El resultado son 8 bytes dechar
datos. ( No termina en 0 a menos que la persona que llama lo haga).Suposiciones: conjunto de caracteres ASCII. El complemento de 2, por
int
lo que el desplazamiento a la derecha finalmente reduce el bit de signo, y la conversión deuint32_t
aint
no modifica el patrón de bits si se establece el bit alto.int
es de al menos 32 bits. (Wider podría permitir que funcione en complementos de 1 o implementaciones de magnitud de signo C).No supuestos: cualquier cosa sobre el orden de bytes de implementación o la firma de
char
.Pruébalo en línea! incluido el llamador de prueba que se utiliza
printf("%.8s\n", buf)
para imprimir el búfer de salida sin terminarlo en 0.Sin golf:
Hacer
n&=15;
adentroh(x)
es un punto de equilibrio; 6 bytes allí frente a 3 cada uno para&15
aislar el mordisco bajo en ambos sitios de llamadas.,
es un punto de secuencia (o equivalente en la terminología moderna), por lo que es seguro hacer*p++= stuff
dos veces en una declaración cuando está separado por el,
operador.>>
en entero con signo se define en la implementación como aritmética o lógica. GNU C lo define como el complemento aritmético 2. Pero en cualquier máquina de complemento de 2 realmente no importa porque nunca miramos los 0 desplazados o las copias del bit de signo. El MSB original eventualmente bajará al byte bajo sin cambios. Este no es el caso en signo / magnitud, y no estoy seguro sobre el complemento de 1.Por lo tanto, esto solo puede ser portátil para las implementaciones C del complemento 2. (O, donde
int
es más ancho de 32 bits de modo bit 31 es sólo una parte de la magnitud.) Sin firmar -> conversión firmado también munges el patrón de bits para los números enteros negativos, por lo que&15
en unaint
única extraería mordiscos del valor sin signo original en complemento a 2. Una vez más, a no ser queint
era más amplia que la de 32 bits para todas las entradas son no negativas.La versión de golf tiene UB por caerse del final de una función no nula. No devolver un valor, solo para evitar declararlo en
void
lugar del valor predeterminadoint
. Los compiladores modernos romperán esto con la optimización habilitada.Motivación: estaba considerando una respuesta x86 o ARM Thumb asm, pensé que podría ser divertido hacerlo manualmente en C, tal vez para el asm generado por el compilador como punto de partida. Consulte /programming/53823756/how-to-convert-a-number-to-hex para obtener una velocidad de asm x86 eficiente, que incluye una versión AVX512VBMI que solo contiene 2 instrucciones (pero necesita vectores de control para vpmultishiftqb y vpshufb así que no sería genial para el golf). Normalmente se necesita trabajo adicional para que SIMD invierta byte en orden de impresión en little endian x86, por lo que esta salida hexadecimal invertida en byte es realmente más fácil de lo normal.
Otras ideas
Pensé en tomar el entero por referencia y recorrer sus bytes con
char*
, en una implementación C poco endian (como x86 o ARM). Pero no creo que eso hubiera ahorrado mucho.Utilizando
sprintf
para hacer 1 byte a la vez, 64 bytes después de jugar al golf:Pero si usamos funciones similares a printf, también podríamos intercambiar byte y hacer una
%x
impresión de todo, como la respuesta de @ JL2210 .fuente
Código de máquina SIMD x86 (AVX512-VBMI), 36 bytes
(16 bytes de los cuales son una tabla de búsqueda hexadecimal)
Esta es una función que toma un número entero
xmm0
y devuelve 8 bytes de datos de caracteres ASCIIxmm0
, para que la persona que llama los almacene donde quiera. (por ejemplo, a la memoria de video después de intercalar con bytes de atributo, o en una cadena en construcción, o lo que sea)Desde C, llámelo como
__m128i retval = lehex(_mm_cvtsi32_si128(x))
con la convención de llamadas x86-64 System V, o MS Windowsvectorcall
.Total = 0x24 = 36 bytes.
Ver ¿Cómo convertir un número a hexadecimal? en SO por cómo funciona esto. (SSE2 para shift / punpck, luego
vpermb
guarda el trabajo que necesitaríamospshufb
. AVX1 en lugar de SSE2 / SSSE3 también evita unamovaps
copia de registro).Observe que
punpcklbw
con los operandos de origen en ese orden nos dará el mordisco más significativo del byte de entrada bajo en el elemento de byte más bajo, luego el mordisco menos significativo del byte de origen más bajo. (En esa respuesta SO,bswap
se usa a en la entrada para obtener un resultado en el orden de impresión estándar con solo SSE2. Pero aquí queremos ese orden: mordisco alto en el elemento inferior dentro de cada byte, pero aún así el orden de los bytes little endian).Si tuviéramos más constantes de datos, podríamos ahorrar espacio en el modo de direccionamiento haciendo uno y
mov edx, imm32
luego utilizando[rdx+16]
o cualquier modo de direccionamiento. Ovpbroadcastb xmm0, [rdx+1]
.Pero creo que un LUT + hexadecimal de 16 bytes
vpermb
es aún mejor que implementar lan>9 : n+'a'-10 : n+'0'
condición: eso requiere 3 constantes y al menos 3 instrucciones con el enmascaramiento de bytes AVX512BW (compárelo en máscaravpaddb
, fusión-enmascaradovpaddb
), o más con AVX1 o SSE2. (Consulte ¿Cómo convertir un número a hexadecimal? En SO para obtener una versión SSE2 de eso). Y cada instrucción AVX512BW tiene al menos 6 bytes de longitud (EVEX de 4 bytes + código de operación + modrm), más larga con un desplazamiento en el modo de direccionamiento.En realidad, se necesitarían al menos 4 instrucciones porque necesitamos limpiar la basura alta con
andps
(o EVEXvpandd
con un operando de memoria de transmisión de 4 bytes) antes de la comparación. Y cada uno de ellos necesita una constante de vector diferente. AVX512 ha emitido operandos de memoria, pero solo para elementos de 32 bits y más. por ejemplo , el último operando de EVEXvpaddb
es soloxmm3/m128
, noxmm3/m128/m8bcst
. (Los puertos de carga de Intel solo pueden hacer transmisiones de 32 y 64 bits de forma gratuita como parte de una carga ascendente, por lo que Intel diseñó AVX512BW para reflejar eso y no poder codificar los operandos de memoria de transmisión de bytes o palabras, en lugar de darles la opción de haz transmisiones de dword para que puedas comprimir tus constantes a 4 bytes: /.)La razón por la que usé AVX512VBMI en
vpermb
lugar de SSSE3 / AVX1pshufb
es doble:vpermb
ignora los bits altos de los selectores.(v)pshufb
ceros bytes de acuerdo con el bit alto del vector de control y hubieran necesitado un extrapand
oandps
realmente aislar nibbles. Con el tamaño XMM / 16 bytes,vpermb
solo mira los 4 bits bajos de los elementos de control aleatorio, es decir, bits[3:0]
en la notación de Intel en la sección Operación .vpermb
puede tomar los datos que se barajan (la tabla de búsqueda) como un operando de memoria.(v)pshufb
El operando xmm / mem es el vector de control aleatorio.Tenga en cuenta que AVX512VBMI solo está disponible en CannonLake / Ice Lake, por lo que probablemente necesite un simulador para probar esto, como el SDE de Intel.
fuente
Scala ,
584036 bytesPruébalo en línea!
Todavía usa el incorporado para revertir los bytes de un
Int
, pero usaformat
para formatearloInt
como un Hex. No hay necesidad de llamartoHexString
.Eliminado los parens en
format
. Esto ahora significa que el argumento se puede tomar implícitamente usando_
.fuente
Adelante (gforth) ,
52 5140 bytesPruébalo en línea!
Explicación del código
fuente
Jalea , 13 bytes
Pruébalo en línea!
Un programa completo que toma un entero como argumento e imprime una cadena.
fuente
APL + WIN,
3634 bytes2 bytes guardados al convertir a índice cero
Solicitudes de entero:
Pruébalo en línea! Cortesía de Dyalog Classic
fuente
Excel, 91 bytes
fuente
K4 ,
1211 bytesSolución:
Ejemplos:
Explicación:
Más o menos exactamente lo que pregunta la pregunta:
Notas:
fuente
PHP , 31 bytes
Pruébalo en línea!
Aprovechando el paquete y desempaquetado de PHP , empaco la entrada sin firmar con el formato "32 bit little endian byte order" (
V
) en una cadena binaria y luego la descomprimo con el formato "cadena hexadecimal, primer nibble alto" (H
) e imprimo el resultado.¡Este parece ser uno de los raros casos en los que las funciones integradas de PHP son más cortas que la implementación de un algoritmo simple!
fuente
pack()
/ de PHPunpack()
son increíbles para las 0 veces que las necesita en la mayoría de los proyectos PHP. ¡Felicidades, has encontrado su uso!Carbón , 11 bytes
Pruébalo en línea! El enlace es a la versión detallada del código. Explicación:
19 bytes sin recurrir al formato Python:
Pruébalo en línea! El enlace es a la versión detallada del código. Explicación:
fuente
Perl 5 (-p), 22 bytes
Pruébalo en línea!
fuente
J , 10 bytes
Pruébalo en línea!
cómo
3!:3
es una J "conjunción extranjera" para representación hexadecimal, documentada aquí . Es decir, es un generador incorporado para convertir a hexadecimal. Sin embargo, su salida no es exactamente lo que queremos. Por ejemplo, corriendo:produce:
El significado de las otras líneas se explica en la página de documentos a la que he vinculado anteriormente. En cualquier caso, está claro que queremos los primeros 8 caracteres de la última línea.
_1{
Consigue la última línea.8{.
obtiene los primeros 8 caracteres de la misma.fuente
Ruby ,
3127 bytesTerminó siendo un puerto de la respuesta PHP de Night2 porque Ruby tiene la misma funcionalidad de empaque / desempaque.
Pruébalo en línea!
Mi respuesta original de 31 bytes que no aprovechó el modo de desempaquetado H8 porque no lo sabía:
Pruébalo en línea!
fuente
Lote de Windows, 90 bytes
Ejecute la línea de comandos con / v para habilitar la expansión retrasada.
fuente
Código de máquina x86 de 32 bits,
2421 bytesregistro de cambios: -3 bytes: reemplace add / cmp / jbe / add estándar con un hack de DAS por @peter ferrie
64 bits: todavía 24 bytes. El modo largo eliminó el código de operación DAS.
Modo de 16 bits: el tamaño de operando predeterminado es de 16 bits, pero la especificación del problema es inherentemente de 32 bits. Incluidos 8 dígitos hexadecimales codificados.
Byte-reverse con
bswap
luego manual int-> hex en orden estándar (el mordisco más significativo primero, escribiendo dígitos hexadecimales en un buffer de salida de caracteres en orden ascendente). Esto evita la necesidad de desenrollar el bucle para cambiar el orden entre mordiscos dentro de un byte vs. a través de bytes.Se puede llamar como
void lehex(char buf[8] /*edi*/, uint32_t x /*esi*/);
x86-64 System V, excepto que esto no funciona en modo de 64 bits. (Necesita el puntero de salida en EDI parastosb
. El número de entrada puede estar en cualquier registro que no sea ECX o EAX).tamaño = 0x15 = 21 bytes.
TIO FASM Caso de prueba x86 de 32 bits con una llamada asm que utiliza una
write
llamada al sistema para escribir la salida después de llamarla dos veces para agregar 2 cadenas a un búfer. Prueba todos los dígitos hexadecimales 0..F, incluidos 9 y A en el límite entre el número y la letra.El
DAS
hack - x86 tiene una bandera de medio acarreo, para llevar a cabo el mordisco bajo. Útil para cosas BCD empaquetadas como la instrucción DAS, pensadas para usar después de restar dos enteros BCD de 2 dígitos. Con el mordisco bajo de AL estando fuera del rango 0-9, definitivamente estamos abusando de esto aquí.Observe
if (old_AL > 99H) or (old_CF = 1)
ENTONCES laAL ← AL − 60H;
parte de la sección Operación en el manual; sbb siempre establece CF aquí para que esa parte siempre suceda. Eso y el rango ASCII para letras mayúsculas es lo que motiva la elección desub al, 0x69
cmp 0xD, 0xA
no establece CF0xD - 0x69
ajusta a AL =0xA4
como entrada a DAS. (Y establece CF, borra AF)0x44
, el código ASCII para'D'
contra un número:
cmp 0x3, 0xA
establece CF3 - 0x69 - 1
= AL = 0x99 y establece CF y AF'3'
.Restar
0x6a
en SBB establecerá AF para cada dígito <= 9, por lo que todos los números siguen la misma lógica. Y déjelo despejado para cada dígito hexadecimal alfabético. es decir, explotar correctamente el manejo dividido 9 / A de DAS.Normalmente (para el rendimiento) usaría una tabla de búsqueda para un bucle escalar, o posiblemente un 2x sin ramificaciones
lea
y unacmp/cmov
adición condicional. Pero lasal, imm8
instrucciones de 2 bytes son una gran victoria para el tamaño del código.Versión de la versión x86-64 : solo la parte que es diferente, entre
and al, 0xf
ystosb
.Tenga en cuenta que
add al, '0'
siempre se ejecuta, y el complemento condicional solo agrega la diferencia entre'a'-10
y'0'
, para que sea solo un enif
lugar deif
/else
.Probado y funciona, usando la misma
main
llamada que mi respuesta C , que usachar buf[8]
yprintf("%.8s\n", buf)
.fuente
sys_write
puede generar cadenas de longitud fija fácilmente. Es interesante, no me había dado cuenta de que FASM en TIO te permitía crear ejecutables de 32 bits, a diferencia de NASM, donde no respeta-felf32
. Prefiero x86-64 de todos modos, y esta respuesta no guarda ningún byte del código de 32 bits.sprintf
? No creo que libc tenga ninguna función práctica int-> string que no sean las basadas en formato-string, solo string-> int como strtoul. Pero sí, bswap / printf probablemente sería más corto, si puede encontrar alguna forma de contar bytes para la entrada GOT para una función en una biblioteca dinámica (además delcall [rel printf wrt ..got]
sitio de llamada de 6 bytes ); Un mínimo de ejecutables enlazados estáticamente puede ser significativamente más pequeño que dinámico, al menos cuando se realizald
con valores predeterminados normales. Pero no creo que sea razonable vincularlo estáticamente, pero no contar el tamaño de su código.