Introdução
Durante o trabalho com o gerador de BMP (bitmap) , enfrento o problema de converter o número em string hexadecimal pequena. Aqui está a função que eu crio em JavaScript - mas me pergunto como o código pequeno pode funcionar da mesma forma
let liEnd= num => num.toString(16).padStart(8,'0').match(/../g).reverse().join``;
console.log(liEnd(304767)) // 304767 dec = 0x4a67f hex
Desafio
Função de gravação que terá um número inteiro não assinado de 32 bits na entrada e produzirá uma seqüência hexadecimal de 8 dígitos com pouca ordem endian. O algoritmo de exemplo que faz o trabalho:
- converter numb para hex string, por exemplo:
304767 -> '4a67f'
- adicione zeros de preenchimento para obter uma string de 8 caracteres:
'0004a67f'
- divida a string em quatro pedaços de 2 caracteres:
'00','04','a6','7f'
- ordem inversa de peças
'7f','a6','04','00'
- juntar peças e retornar como resultado:
'7fa60400'
Exemplo de entrada e saída
O número de entrada (ou sequência com o número dec) está à esquerda de ->
, a sequência hexadecimal de saída está à direita
2141586432 -> 0004a67f
304767 -> 7fa60400
f=lambda n,i=4:i*'1'and'%02x'%(n%256)+f(n>>8,i-1)
salva um byte :)R ,
5453 bytesExperimente online!
Cada grupo de 2 caracteres é na verdade a representação hexadecimal de um dígito na base 256.
scan()%/%256^(0:3)%%256
converte em um número 256 base com 4 dígitos invertidos,...%*%256^(3:0)
une-os como um único número inteiro eformat.hexmode(...,8)
converte esse número em sua representação hexadecimal com 8 dígitos.fonte
JavaScript (ES7),
5957 bytesManipulação de cordas.
Experimente online!
Quão?
Nós primeiro convertido para hexadecimal para se certificar de que todos os principais 's estão incluídos:n + 232. 0 0
Experimente online!
Usamos a expressão regular1
/\B../g
para corresponder a todos os grupos de 2 dígitos, ignorando o graças a ( limite de não palavra ).\B
Experimente online!
Nós
reverse()
ejoin()
para obter a corda final.JavaScript (ES6), 61 bytes
Função recursiva.
Experimente online!
fonte
Zsh , 46 bytes
Experimente online!
fonte
C # (compilador interativo do Visual C #) , 54 bytes
Guardado 4 bytes graças a @PeterCordes
Experimente online!
Explicação
fonte
4278255360
máscara constantemente para16711935
(0xff00ff
) se mudar antes de mascarar? Ou isso custa parênteses extras? Além disso, se não, então0xff00ff00
é do mesmo comprimento, mas muito mais significativo para os seres humanos.>>
tem maior precedência do que o que&
economizou 4 bytes no total. Obrigado!Japonês
-P
, 10 bytesTente
fonte
-P
faz?-P
: Se a saída for uma matriz, as saídas sem separador (por exemplo, unidasP
). ". Portanto, o sinalizador é para uma associação implícita, em vez de explícita, para salvar bytes. :)C (gcc) , 30 bytes
Experimente online!
fonte
Python 2 , 43 bytes
Experimente online!
-4 bytes graças ao benrg
Produz uma lista de caracteres. Calculado recuperando, em ordem, os dígitos hexadecimais da entrada nos índices
6, 7, 4, 5, 2, 3, 0, 1
.fonte
[i^6]for i in range(8)
salva alguns bytes.Agnóstico endian C (gcc) , sem bibliotecas padrão,
9291 bytesh(n)
é uma função inteira de um dígito-> hexadecimal.f(x,p)
pega um número inteiro e umchar[8]
ponteiro. O resultado são 8 bytes dechar
dados. ( Não é terminado em 0, a menos que o chamador faça isso.)Pressupostos: conjunto de caracteres ASCII. O complemento de 2, de
int
modo que a mudança para a direita eventualmente reduz o bit de sinal e a conversão deuint32_t
paraint
não altera o padrão de bits se o bit alto estiver definido.int
tem pelo menos 32 bits. (A Wider pode deixá-lo funcionar nas implementações de complemento 1 ou de magnitude C de sinal).Não-premissas: qualquer coisa sobre a implementação de ordem de bytes ou assinatura de
char
.Experimente online! incluindo o chamador de teste usando
printf("%.8s\n", buf)
para imprimir o buffer de saída sem terminá-lo 0.Ungolfed:
Fazer
n&=15;
dentroh(x)
é o ponto de equilíbrio; 6 bytes lá vs. 3 cada um para&15
isolar a mordidela baixa nos dois sites de chamada.,
é um ponto de sequência (ou equivalente na terminologia moderna), portanto, é seguro fazer*p++= stuff
duas vezes em uma instrução quando separado pelo,
operador.>>
no número inteiro assinado é definido como implementação aritmético ou lógico. O GNU C o define como complemento da aritmética 2. Mas em qualquer máquina de complemento de 2, isso realmente não importa, porque nunca olhamos para os 0s ou cópias do bit de sinal. O MSB original acabará inalterado no byte baixo. Este não é o caso do sinal / magnitude e não tenho certeza sobre o complemento de 1.Portanto, isso pode ser portável apenas para as implementações C do complemento 2. (Ou onde
int
é maior que 32 bits, o bit 31 é apenas parte da magnitude.) A conversão não assinada -> assinada também modifica o padrão de bits para números inteiros negativos, de modo que,&15
em umint
só, extrairíamos petiscos do valor não assinado original no complemento de 2. Novamente, a menos queint
seja maior que 32 bits, todas as entradas são não negativas.A versão com golf tem UB de cair no final de uma função não nula. Não para retornar um valor, apenas para evitar declará-lo em
void
vez do padrãoint
. Compiladores modernos quebram isso com a otimização ativada.Motivação: eu estava pensando em uma resposta x86 ou ARM Thumb asm, pensei que poderia ser divertido fazê-lo manualmente em C, talvez para asm gerado por compilador como ponto de partida. Consulte /programming/53823756/how-to-convert-a-number-to-hex para obter a velocidade x86 asm com eficiência de velocidade, incluindo uma versão AVX512VBMI com apenas duas instruções (mas precisa de vetores de controle para vpmultishiftqb e vpshufb então não seria ótimo para o golfe). Normalmente, é necessário um trabalho extra para o SIMD inverter pela ordem de impressão no x86 little-endian, portanto essa saída hexadecimal invertida é realmente mais fácil do que o normal.
Outras ideias
Eu considerei pegar o número inteiro por referência e fazer um loop sobre seus bytes com
char*
uma implementação C de little endian (como x86 ou ARM). Mas não acho que isso economizaria muito.Usando
sprintf
para executar 1 byte por vez, 64 bytes após jogar golfe:Mas, se estivermos usando funções do tipo printf, é melhor trocar por byte e fazer um
%x
printf da coisa toda, como a resposta do @ JL2210 .fonte
código de máquina SIM86 x86 (AVX512-VBMI), 36 bytes
(16 bytes dos quais são uma tabela de pesquisa hexadecimal)
Essa é uma função que recebe um número inteiro
xmm0
e retorna 8 bytes de dados de caracteres ASCIIxmm0
para que o chamador armazene onde quiser. (por exemplo, na memória de vídeo após intercalar com bytes de atributo ou em uma string em construção ou o que for)Em C, chame-o como
__m128i retval = lehex(_mm_cvtsi32_si128(x))
na convenção de chamada do System V x86-64, ou MS Windowsvectorcall
.Total = 0x24 = 36 bytes.
Consulte Como converter um número em hexadecimal? no SO para saber como isso funciona. (O SSE2 para o shift / punpck e
vpermb
salva o trabalho necessáriopshufb
. O AVX1 em vez do SSE2 / SSSE3 também evita umamovaps
cópia do registro.)Observe que,
punpcklbw
com os operandos de origem nessa ordem, forneceremos a menor quantidade significativa de bytes de entrada baixos no elemento de bytes mais baixos e, em seguida, a menor quantidade significativa de bytes de menor origem. (Na resposta SO, abswap
é usado na entrada para obter um resultado na ordem de impressão padrão apenas com SSE2. Mas aqui queremos essa ordem: mordidela alta no elemento inferior em cada byte, mas ainda na ordem de bytes menos endian).Se tivéssemos mais constantes de dados, poderíamos economizar espaço no modo de endereçamento executando um deles
mov edx, imm32
, usando[rdx+16]
ou qualquer outro modo de endereçamento. Orvpbroadcastb xmm0, [rdx+1]
.Mas acho que um LUT + hexadecimal de 16 bytes
vpermb
ainda é melhor do que implementar an>9 : n+'a'-10 : n+'0'
condição: isso requer 3 constantes e pelo menos 3 instruções com máscara de byte AVX512BW (compare com máscara, máscara devpaddb
mesclagemvpaddb
) ou mais com AVX1 ou SSE2. (Consulte Como converter um número em hexadecimal? No SO para obter uma versão SSE2 disso). E cada instrução AVX512BW tem pelo menos 6 bytes de comprimento (EVEX + opcode + modrm) de 4 bytes, mais com um deslocamento no modo de endereçamento.Na verdade, seriam necessárias pelo menos quatro instruções, porque precisamos limpar o lixo alto com
andps
(ou EVEXvpandd
com um operando de memória de transmissão de 4 bytes) antes da comparação. E cada um deles precisa de uma constante de vetor diferente. O AVX512 possui operandos de memória de difusão, mas apenas para elementos de 32 bits ou mais. por exemplo , o último operando do EVEXvpaddb
é apenasxmm3/m128
, nãoxmm3/m128/m8bcst
. (As portas de carregamento da Intel podem fazer transmissões de 32 e 64 bits gratuitamente, como parte de um carregamento, por isso a Intel projetou o AVX512BW para refletir isso e não conseguir codificar operandos de memória de transmissão de bytes ou de palavra, em vez de oferecer a opção de faça transmissões dword para que você ainda possa comprimir suas constantes em 4 bytes: /.)O motivo pelo qual usei o AVX512VBMI em
vpermb
vez do SSSE3 / AVX1pshufb
é duplo:vpermb
ignora bits altos dos seletores.(v)pshufb
zeros bytes de acordo com o bit alto do vetor de controle e precisaria de um extrapand
ouandps
para realmente isolar mordidelas. Com o tamanho XMM / 16 bytes,vpermb
apenas os 4 bits baixos dos elementos de controle de reprodução aleatória são analisados, ou seja, bits[3:0]
na notação da Intel na seção Operação .vpermb
pode levar os dados a serem embaralhados (a tabela de pesquisa) como um operando de memória.(v)pshufb
O operando xmm / mem é o vetor de controle aleatório.Observe que o AVX512VBMI está disponível apenas no CannonLake / Ice Lake, portanto você provavelmente precisará de um simulador para testar isso, como o SDE da Intel.
fonte
Scala ,
584036 bytesExperimente online!
Ainda usa o builtin para reverter os bytes de um
Int
, mas usaformat
para formatar oInt
como um Hex. Não há necessidade de ligartoHexString
.Removidas as parênteses
format
. Agora, isso significa que o argumento pode ser usado implicitamente_
.fonte
Quarto (gforth) ,
52 5140 bytesExperimente online!
Explicação do código
fonte
Gelatina , 13 bytes
Experimente online!
Um programa completo que usa um número inteiro como argumento e imprime uma string.
fonte
APL + WIN,
3634 bytes2 bytes salvos convertendo para o índice zero
Solicita o número inteiro:
Experimente online! Cortesia de Dyalog Classic
fonte
Excel, 91 bytes
fonte
K4 ,
1211 bytesSolução:
Exemplos:
Explicação:
Praticamente exatamente o que a pergunta faz:
Notas:
fonte
PHP , 31 bytes
Experimente online!
Aproveitando o pacote e a descompactação do PHP , empacotei a entrada não assinada com o formato "32 bits pequenos endian byte order" (
V
) em uma string binária e descompacte-a com o formato "hex string, high nibble first" (H
) e imprimo o resultado.Este parece ser um dos raros casos em que os built-ins do PHP são realmente mais curtos do que implementar um algoritmo simples!
fonte
pack()
/ PHPunpack()
são impressionantes pelas 0 vezes que você precisa delas na maioria dos projetos PHP. Parabéns, você encontrou o uso deles!Carvão , 11 bytes
Experimente online! Link é a versão detalhada do código. Explicação:
19 bytes sem recorrer à formatação Python:
Experimente online! Link é a versão detalhada do código. Explicação:
fonte
Perl 5 (-p), 22 bytes
Experimente online!
fonte
J , 10 bytes
Experimente online!
como
3!:3
é uma "conjunção estrangeira" J para representação hexadecimal, documentada aqui . Ou seja, é um builtin para converter em hexadecimal. No entanto, a saída não é exatamente o que queremos. Por exemplo, executando:produz:
O significado das outras linhas é explicado na página de documento a que vinculei acima. De qualquer forma, é claro que queremos os 8 primeiros caracteres da última linha.
_1{
pegue a última linha.8{.
obtém os 8 primeiros caracteres.fonte
Ruby ,
3127 bytesAcabou sendo uma porta da resposta PHP do Night2 porque o Ruby tem a mesma funcionalidade de empacotar / descompactar.
Experimente online!
Minha resposta original de 31 bytes que não tirou proveito do modo de descompactação H8 porque eu não sabia:
Experimente online!
fonte
Lote do Windows, 90 bytes
Execute a linha de comando com / v para ativar a expansão atrasada.
fonte
código de máquina x86 de 32 bits,
2421 byteschangelog: -3 bytes: substitua add / cmp / jbe / add padrão por um hack do DAS por @peter ferrie
64 bits: ainda 24 bytes. O modo longo removeu o código de operação do DAS.
Modo de 16 bits: o tamanho padrão do operando é de 16 bits, mas a especificação do problema é inerentemente de 32 bits. Incluindo 8 dígitos hexadecimais codificados.
Byte-reverse com
bswap
manual int-> hex em ordem padrão (mordisca mais significativa primeiro, gravando dígitos hexadecimais em um buffer de saída de char em ordem crescente). Isso evita a necessidade de desenrolar o loop para alternar a ordem entre mordidelas em um byte vs. através de bytes.É possível chamar
void lehex(char buf[8] /*edi*/, uint32_t x /*esi*/);
como o x86-64 System V, exceto que isso não funciona no modo de 64 bits. (Ele precisa do ponteiro de saída em EDI parastosb
. O número de entrada pode estar em qualquer registro que não seja ECX ou EAX.)tamanho = 0x15 = 21 bytes.
TIO FASM: caso de teste x86 de 32 bits com um chamador asm que usa uma
write
chamada do sistema para gravar a saída depois de chamá-la duas vezes para acrescentar 2 strings em um buffer. Testa todos os dígitos hexadecimais 0..F, incluindo 9 e A no limite entre o número e a letra.O
DAS
hack - x86 tem uma bandeira de meio transporte, para realizar com a mordidela baixa. Útil para material embalado em BCD, como a instrução DAS, destinada ao uso após a subtração de dois números inteiros BCD de 2 dígitos. Com o baixo custo de AL estar fora do intervalo de 0 a 9, definitivamente estamos abusando dele aqui.Observe a
if (old_AL > 99H) or (old_CF = 1)
ENTÃOAL ← AL − 60H;
parte da seção Operação no manual; O sbb sempre define CF aqui, para que essa parte sempre aconteça. Isso e o intervalo ASCII para letras maiúsculas é o que motiva a escolha desub al, 0x69
cmp 0xD, 0xA
não define CF0xD - 0x69
envolve AL =0xA4
como entrada para o DAS. (E define CF, limpa AF)0x44
o código ASCII para'D'
vs. um numeral:
cmp 0x3, 0xA
define CF3 - 0x69 - 1
= AL = 0x99 e define CF e AF'3'
.Subtrair
0x6a
no SBB definirá AF para cada dígito <= 9 para que todos os números sigam a mesma lógica. E deixe desmarcado para cada dígito hexadecimal alfabético. ou seja, explorar corretamente a manipulação dividida 9 / A do DAS.Normalmente (para desempenho), você usaria uma tabela de pesquisa para um loop escalar ou, possivelmente, um 2x
lea
ecmp/cmov
adição condicional sem ramificações . Mas asal, imm8
instruções de 2 bytes são uma grande vitória para o tamanho do código.Versão da versão x86-64 : apenas a parte que é diferente, entre
and al, 0xf
estosb
.Observe que o
add al, '0'
sempre é executado e a adição condicional apenas adiciona a diferença entre'a'-10
e'0'
, para torná-lo apenas um emif
vez deif
/else
.Testado e funciona, usando o mesmo
main
chamador da minha resposta C , que usachar buf[8]
eprintf("%.8s\n", buf)
.fonte
sys_write
pode emitir strings de comprimento fixo facilmente. Oh, interessante, eu não tinha percebido que o FASM no TIO permite criar executáveis de 32 bits, ao contrário do NASM, que não respeita-felf32
. Prefiro x86-64 de qualquer maneira, e esta resposta não salva nenhum bytes do código de 32 bits.sprintf
? Eu não acho que a libc tenha outras funções int-> string diferentes das baseadas em formato-string, apenas string-> int como strtoul. Mas sim, bswap / printf provavelmente seria mais curto, se você puder descobrir alguma maneira de contar bytes para a entrada GOT para uma função em uma biblioteca dinâmica (além docall [rel printf wrt ..got]
site de chamadas de 6 bytes ); um número mínimo de executáveis vinculados estaticamente pode ser significativamente menor que dinâmico, pelo menos quando feitold
com padrões normais. Mas não acho que seria razoável vinculá-lo estaticamente, mas sem contar o tamanho do código.