Conversão Little Endian Number em String

13

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
Kamil Kiełczewski
fonte

Respostas:

7

05AB1E , 10 9 bytes

žJ+h¦2ôRJ

Experimente online!

-1 byte por inspiração da resposta Jelly.

žJ+   add 2^32 to input
h     convert to hex
¦     drop leading 1
2ô    split in groups of 2
R     reverse groups
J     and join them
Dorian
fonte
6

Python 3 , 37 bytes

lambda n:n.to_bytes(4,"little").hex()

Experimente online!

Solução recursiva baseada em aritmética ( 50 49 bytes, funciona também para Python 2 ) :

f=lambda n,i=4:i*'1'and"%02x"%(n%256)+f(n>>8,i-1)

Experimente online!

-1 byte graças a @JonathanAllan

Joel
fonte
Eu diria que enviar o recursiva como uma entrada Python 2 :)
Jonathan Allan
f=lambda n,i=4:i*'1'and'%02x'%(n%256)+f(n>>8,i-1)salva um byte :)
Jonathan Allan
@JonathanAllan Thanks. Não estou familiarizado com todos os truques do Python 2 e não vejo como ele pode ser reduzido.
Joel Joel
isso não acontece, mas o 37 não funcionará no py 2
Jonathan Allan
Sim. Alguns desses embutidos são apenas em Python-3.
Joel #
6

R , 54 53 bytes

format.hexmode(scan()%/%256^(0:3)%%256%*%256^(3:0),8)

Experimente online!

Cada grupo de 2 caracteres é na verdade a representação hexadecimal de um dígito na base 256. scan()%/%256^(0:3)%%256converte em um número 256 base com 4 dígitos invertidos, ...%*%256^(3:0)une-os como um único número inteiro e format.hexmode(...,8)converte esse número em sua representação hexadecimal com 8 dígitos.

Robin Ryder
fonte
5

JavaScript (ES7),  59  57 bytes

Manipulação de cordas.

n=>(n+2**32).toString(16).match(/\B../g).reverse().join``

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

(304767 + 2**32).toString(16) // --> '10004a67f'

Experimente online!

Usamos a expressão regular /\B../gpara corresponder a todos os grupos de 2 dígitos, ignorando o graças a ( limite de não palavra ).1\B

'10004a67f'.match(/\B../g) // --> [ '00', '04', 'a6', '7f' ]

Experimente online!

Nós reverse()e join()para obter a corda final.


JavaScript (ES6), 61 bytes

Função recursiva.

f=(n,k=4)=>k?[(x=n&255)>>4&&'']+x.toString(16)+f(n>>8,k-1):''

Experimente online!

Arnauld
fonte
⭐ - você tem estrela para resposta nice - I como ele, curto, mas ainda limpo e "human-redable" :)
Kamil Kiełczewski
5

C # (compilador interativo do Visual C #) , 54 bytes

x=>$"{(x=x>>16|x<<16)>>8&16711935|(x&16711935)<<8:x8}"

Guardado 4 bytes graças a @PeterCordes

Experimente online!

Explicação

x=>                                                    //Lambda taking in an uint
     (x=x>>16|x<<16)                                   //Swap the first two and the last two bytes of the uint (0x7fa60400 -> 0x04007fa6)
                    >>8&16711935|(x&16711935)<<8       //Swap each pair of bytes in every group of 2 bytes (0x04007fa6 -> 0x0004a67f)
  $"{                                           :x8}"  //Format as hex string, padded with leading zeroes to length 8
Modalidade de ignorância
fonte
Você pode reduzir a 4278255360máscara constantemente para 16711935( 0xff00ff) se mudar antes de mascarar? Ou isso custa parênteses extras? Além disso, se não, então 0xff00ff00é do mesmo comprimento, mas muito mais significativo para os seres humanos.
Peter Cordes
@PeterCordes Ele também tem a vantagem de poder remover os colchetes, pois >>tem maior precedência do que o que &economizou 4 bytes no total. Obrigado!
Modalidade de ignorância
Legal. Na sua seção "explicação", sugiro escrever as constantes em hexadecimal.
Peter Cordes
4

Japonês -P , 10 bytes

sG ùT8 ò w

Tente

sG ùT8 ò w     :Implicit input of integer
s              :Convert to string
 G             :  In base-16
   ù           :Left pad
    T          :  With 0
     8         :  To length 8
       ò       :Split into 2s
         w     :Reverse
               :Implicitly join and output
Shaggy
fonte
O que -Pfaz?
SS Anne
🚀 sua resposta está no topo (você pode adicionar explicação?)
Kamil Kiełczewski
@ JL2210 Dos documentos : " -P: Se a saída for uma matriz, as saídas sem separador (por exemplo, unidas P). ". Portanto, o sinalizador é para uma associação implícita, em vez de explícita, para salvar bytes. :)
Kevin Cruijssen
2
@ KamilKiełczewski, explicação adicionada.
Shaggy
4

C (gcc) , 30 bytes

f(x){printf("%.8x",htonl(x));}

Experimente online!

SS Anne
fonte
quando executado em uma máquina big-endian, isso não será convertido em little-endian?
peter ferrie
@peterferrie Ver revisão 3.
SS Anne
4

Python 2 , 43 bytes

lambda n:[("%08x"%n)[i^6]for i in range(8)]

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.

sete negativos
fonte
2
[i^6]for i in range(8)salva alguns bytes.
22919 benrg
É permitido enviar a lista em vez da string?
Qwertiy
saída lista como não se enquadra muito bem para o espírito da questão imo
QWR
3

Agnóstico endian C (gcc) , sem bibliotecas padrão, 92 91 bytes

h(n)é uma função inteira de um dígito-> hexadecimal.
f(x,p)pega um número inteiro e um char[8]ponteiro. O resultado são 8 bytes de chardados. ( Não é terminado em 0, a menos que o chamador faça isso.)

Pressupostos: conjunto de caracteres ASCII. O complemento de 2, de intmodo que a mudança para a direita eventualmente reduz o bit de sinal e a conversão de uint32_tpara intnão altera o padrão de bits se o bit alto estiver definido. inttem 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.

i;h(n){n&=15;return n>9?n+87:n+48;}f(x,p)char*p;{for(i=5;--i;x>>=8)*p++=h(x>>4),*p++=h(x);}

Experimente online! incluindo o chamador de teste usando printf("%.8s\n", buf)para imprimir o buffer de saída sem terminá-lo 0.

Ungolfed:

int h(n){n&=15;return n>9 ? n+'a'-10 : n+'0';}      // single digit integer -> hex

int i;
void ungolfed_f(x,p)char*p;{
    for(i=5; --i; x>>=8)   // LS byte first across bytes
        *p++=h(x>>4),      // MS nibble first within bytes
        *p++=h(x);
}

Fazer n&=15;dentro h(x)é o ponto de equilíbrio; 6 bytes lá vs. 3 cada um para &15isolar a mordidela baixa nos dois sites de chamada.

,é um ponto de sequência (ou equivalente na terminologia moderna), portanto, é seguro fazer *p++= stuffduas 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, &15em um intsó, extrairíamos petiscos do valor não assinado original no complemento de 2. Novamente, a menos que intseja 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 voidvez do padrão int. 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 sprintfpara executar 1 byte por vez, 64 bytes após jogar golfe:

int i;
void f(x,p)char*p;{
        for(i=4;sprintf(p,"%.2x",x&255),--i;x>>=8)
                p+=2;
}

Mas, se estivermos usando funções do tipo printf, é melhor trocar por byte e fazer um %xprintf da coisa toda, como a resposta do @ JL2210 .

Peter Cordes
fonte
⭐ - você obtém estrelas por uma boa resposta #
Kamil Kiełczewski #
3

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 xmm0e retorna 8 bytes de dados de caracteres ASCII xmm0para 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 Windows vectorcall.

# disassembly with machine-code bytes (the answer) and NASM source code.
0000000000401000 <lehex>:
  401000:       c5 f1 72 d0 04          vpsrld      xmm1, xmm0, 4         ; AVX1
  401005:       c5 f1 60 c8             vpunpcklbw  xmm1, xmm1, xmm0      ; AVX1
  401009:    62 f2 75 08 8d 05 01 00 00 00 vpermb  xmm0, xmm1, [rel .hex_lut]
  401013:       c3                      ret    

0000000000401014 <lehex.hex_lut>:
  401014:     30 31 ...  61 62 ...     .hex_lut:  db "0123456789abcdef"

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 vpermbsalva o trabalho necessário pshufb. O AVX1 em vez do SSE2 / SSSE3 também evita uma movapscópia do registro.)

Observe que, punpcklbwcom 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, a bswapé 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. Or vpbroadcastb xmm0, [rdx+1].

Mas acho que um LUT + hexadecimal de 16 bytes vpermbainda é melhor do que implementar a n>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 de vpaddbmesclagem vpaddb) 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 EVEX vpanddcom 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 é apenas xmm3/m128, não xmm3/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 emvpermb vez do SSSE3 / AVX1 pshufbé duplo:

  • vpermbignora bits altos dos seletores. (v)pshufbzeros bytes de acordo com o bit alto do vetor de controle e precisaria de um extra pandou andpspara realmente isolar mordidelas. Com o tamanho XMM / 16 bytes, vpermbapenas 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 .
  • vpermbpode levar os dados a serem embaralhados (a tabela de pesquisa) como um operando de memória. (v)pshufbO 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.

Peter Cordes
fonte
⭐ - você obtém estrelas por uma boa resposta #
Kamil Kiełczewski #
@ KamilKiełczewski: lol obrigado. Converter números em hexadecimal com eficiência é uma das minhas coisas favoritas. É um bom caso de uso para vários truques e manipulação de bits.
Peter Cordes
3

Scala , 58 40 36 bytes

"%08X"format Integer.reverseBytes(_)

Experimente online!

Ainda usa o builtin para reverter os bytes de um Int, mas usa formatpara formatar o Intcomo um Hex. Não há necessidade de ligar toHexString.

Removidas as parênteses format. Agora, isso significa que o argumento pode ser usado implicitamente _.

Ensaboado
fonte
2

Quarto (gforth) , 52 51 40 bytes

: f hex 0 4. do <# # # 0. #> type loop ;

Experimente online!

Explicação do código

: f           \ start a new word definition
  hex         \ set the current base to base 16
  0           \ convert the input number to a double-cell integer
  4. do       \ start a counted loop from 0 to 3
    <# # #    \ start a formatted numeric string and move last 2 digits to format area
    0.        \ move remaining digits down the stack
    #>        \ delete top two stack value and convert format area to string
    type      \ output string
  loop        \ end loop
;             \ end word definition
reffu
fonte
2

Gelatina , 13 bytes

+Ø%b⁴Ḋs2Ṛ‘ịØh

Experimente online!

Um programa completo que usa um número inteiro como argumento e imprime uma string.

Nick Kennedy
fonte
🚀 sua resposta está no topo
Kamil Kiełczewski
2

Excel, 91 bytes

=RIGHT(DEC2HEX(A1,8),2)&MID(DEC2HEX(A1,8),5,2)&MID(DEC2HEX(A1,8),3,2)&LEFT(DEC2HEX(A1,8),2)
Wernisch
fonte
2

K4 , 12 11 bytes

Solução:

,/$|4_0x0\:

Exemplos:

q)k),/$|4_0x0\:304767
"7fa60400"
q)0W
"0004a67f"

Explicação:

Praticamente exatamente o que a pergunta faz:

,/$|4_0x0\: / the solution
      0x0\: / split to bytes
    4_      / drop first 4 bytes
   |        / reverse
  $         / convert to string
,/          / flatten

Notas:

  • -1 byte, pois os números K4 são longos (64 bits) por padrão, eliminando 4 bytes (32 bits)
rua
fonte
🚀 sua resposta está no topo
Kamil Kiełczewski
2

PHP , 31 bytes

<?=unpack(H8,pack(V,$argn))[1];

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!

Night2
fonte
As funções pack()/ PHP unpack()são impressionantes pelas 0 vezes que você precisa delas na maioria dos projetos PHP. Parabéns, você encontrou o uso deles!
640KB 2/03
1

Carvão , 11 bytes

⪫⮌⪪﹪%08xN²ω

Experimente online! Link é a versão detalhada do código. Explicação:

        N   Input as a number
   ﹪%08x    Format using literal string
  ⪪      ²  Split into pairs of characters
 ⮌          Reverse
⪫         ω Join
            Implicitly print

19 bytes sem recorrer à formatação Python:

⪫…⮌⪪⍘⁺X²¦³⁶N¹⁶¦²¦⁴ω

Experimente online! Link é a versão detalhada do código. Explicação:

           N        Input as a number
     ⁺              Plus
       ²            Literal 2
      X             To power
         ³⁶         Literal 36
    ⍘               Convert to base
            ¹⁶      Literal 16
   ⪪           ²    Split into pairs of digits
  ⮌                 Reverse the list
 …               ⁴  Take the first 4 pairs
⪫                 ω Join together
                    Implicitly print
Neil
fonte
🚀 sua resposta está no topo
Kamil Kiełczewski
1

J , 10 bytes

8{._1{3!:3

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:

3!:3 (304767)

produz:

e300000000000000
0400000000000000
0100000000000000
0000000000000000
7fa6040000000000

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.

Jonah
fonte
🚀 sua resposta está no topo
Kamil Kiełczewski
1

Ruby , 31 27 bytes

Acabou sendo uma porta da resposta PHP do Night2 porque o Ruby tem a mesma funcionalidade de empacotar / descompactar.

->*i{i.pack(?V).unpack'H8'}

Experimente online!

Minha resposta original de 31 bytes que não tirou proveito do modo de descompactação H8 porque eu não sabia:

->*i{'%02x'*4%i.pack(?V).bytes}

Experimente online!

Value Ink
fonte
1

Lote do Windows, 90 bytes

@for /l %%x in (24,-8,0)do @set/aa=%1^>^>%%x^&255&cmd/cexit !a!&<nul set/p=!=exitcode:~-2!

Execute a linha de comando com / v para ativar a expansão atrasada.

Peter Ferrie
fonte
1

código de máquina x86 de 32 bits, 24 21 bytes

changelog: -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 bswapmanual 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 para stosb. O número de entrada pode estar em qualquer registro que não seja ECX ou EAX.)

     1                             lehex:
     2 00000000 0FCE                   bswap  esi
     3 00000002 6A08                   push   8            ; 8 hex digits
     4 00000004 59                     pop    ecx
     5                             .loop:                ;do{
     6 00000005 C1C604                 rol    esi, 4       ; rotate high nibble to the bottom
     7                             
     8 00000008 89F0                   mov    eax, esi
     9 0000000A 240F                   and    al, 0x0f     ; isolate low nibble
    10 0000000C 3C0A                   cmp al, 10          ; set CF according to digit <= 9
    11 0000000E 1C69                   sbb al, 0x69        ; read CF, set CF and conditionally set AF
    12 00000010 2F                     das                 ; magic, which happens to work
    13                             
    14 00000011 AA                     stosb               ; *edi++ = al
    15 00000012 E2F1                   loop  .loop       ; }while(--ecx)
    16                             
    17 00000014 C3                     ret

tamanho = 0x15 = 21 bytes.

TIO FASM: caso de teste x86 de 32 bits com um chamador asm que usa uma writechamada 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 DAShack - 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ÃO AL ← 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 CF
  • sbb 0xD - 0x69envolve AL = 0xA4como entrada para o DAS. (E define CF, limpa AF)
  • não AL - = 6 na primeira parte do DAS (porque 4> 9 é falso e AF = 0)
  • AL - = 0x60 na segunda parte, deixando 0x44o código ASCII para'D'

vs. um numeral:

  • cmp 0x3, 0xA define CF
  • sbb 3 - 0x69 - 1= AL = 0x99 e define CF e AF
  • não AL - = 6 na primeira parte do DAS (9> 9 é falso, mas AF está definido), deixando 0x93
  • AL - = 0x60 na segunda parte, deixando 0x33, o código ASCII para '3'.

Subtrair 0x6ano 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 leae cmp/cmovadição condicional sem ramificações . Mas as al, imm8instruçõ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, 0xfe stosb.

;; x86-64 int -> hex  in 8 bytes
    10 0000000C 0430                   add    al, '0'
    11 0000000E 3C39                   cmp    al, '9'
    12 00000010 7602                   jbe  .digit
    13 00000012 0427                     add    al, 'a'-10 - '0'     ; al =  al>9 ? al+'a'-10 : al+'0'
    14                             .digit:

Observe que o add al, '0' sempre é executado e a adição condicional apenas adiciona a diferença entre 'a'-10e '0', para torná-lo apenas um em ifvez de if/ else.

Testado e funciona, usando o mesmo mainchamador da minha resposta C , que usa char buf[8]e printf("%.8s\n", buf).

Peter Cordes
fonte
você pode criar um trecho de trabalho on-line, por exemplo, aqui ?
Kamil Kiełczewski
@ KamilKiełczewski: O TIO torna impossível (AFAIK) escrever o chamador em C para testar uma função asm, por isso muitas vezes não me incomodo, mas com certeza, já que você pediu e sys_writepode 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.
Peter Cordes
⭐ - você tem estrela para resposta agradável
Kamil Kiełczewski
1
@ JL2210: Você quer dizer 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 do call [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 feito ldcom padrões normais. Mas não acho que seria razoável vinculá-lo estaticamente, mas sem contar o tamanho do código.
Peter Cordes
1
@ JL2210: Lembre-se, esta é uma resposta do código da máquina x86 , não do tamanho da fonte de texto. Eu não usei funções libc em respostas anteriores de código de máquina, apenas chamadas de sistema Linux (por exemplo, em Fibonacci) e IDK como eu contaria o custo ou se eu quero escrever respostas de código de máquina com libc . Existem casos de uso para código de máquina x86 em que uma libc não está disponível, por exemplo, em um carregador de inicialização.
Peter Cordes