Little Endian Number to String Conversion

13

pengantar

Selama bekerja dengan generator BMP (bitmap) saya menghadapi masalah dalam mengubah angka menjadi sedikit string hex endian. Berikut adalah fungsi yang saya buat dalam JavaScript - tetapi bertanya-tanya bagaimana kode kecil dapat bekerja dengan cara yang sama

let liEnd= num => num.toString(16).padStart(8,'0').match(/../g).reverse().join``;
console.log(liEnd(304767)) // 304767 dec = 0x4a67f hex

Tantangan

Tulis fungsi yang akan mengambil nomor integer 32bit tanpa tanda pada input, dan menghasilkan 8-digit string heksadesimal dengan sedikit urutan endian. Contoh algoritma yang melakukan pekerjaan:

  • konversi numb ke hex string misalnya: 304767 -> '4a67f'
  • tambahkan padding nol untuk mendapatkan string 8-char: '0004a67f'
  • pisahkan string menjadi empat bagian 2-char: '00','04','a6','7f'
  • urutan terbalik '7f','a6','04','00'
  • bergabunglah dan kembalikan sebagai hasilnya: '7fa60400'

Contoh Input dan Output

Nomor input (atau string dengan angka dec) ada di sebelah kiri ->, string hex output ada di sebelah kanan

2141586432 -> 0004a67f
304767     -> 7fa60400
Kamil Kiełczewski
sumber

Jawaban:

7

05AB1E , 10 9 byte

žJ+h¦2ôRJ

Cobalah online!

-1 byte dengan inspirasi jawaban 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
sumber
6

Python 3 , 37 byte

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

Cobalah online!

Solusi rekursif berbasis aritmatika ( 50 49 byte, juga berfungsi untuk Python 2 ) :

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

Cobalah online!

-1 byte terima kasih kepada @JonathanAllan

Joel
sumber
Saya akan mengatakan kirimkan yang rekursif sebagai entri Python 2 :)
Jonathan Allan
f=lambda n,i=4:i*'1'and'%02x'%(n%256)+f(n>>8,i-1)menghemat satu byte :)
Jonathan Allan
@Jonathan Allan Terima kasih. Saya tidak terbiasa dengan semua trik Python 2 dan tidak melihat bagaimana itu bisa dibuat lebih pendek.
Joel
itu tidak tetapi 37 tidak akan bekerja di py 2
Jonathan Allan
Ya. Beberapa built-in adalah Python-3-only.
Joel
6

R , 54 53 byte

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

Cobalah online!

Setiap kelompok 2 karakter sebenarnya adalah representasi heksa dari digit dalam basis 256. scan()%/%256^(0:3)%%256mengkonversi ke nomor 256 basis dengan 4 digit terbalik, ...%*%256^(3:0)bergabung dengan mereka sebagai integer tunggal, dan format.hexmode(...,8)mengkonversi angka itu ke representasi hex dengan 8 digit.

Robin Ryder
sumber
5

JavaScript (ES7),  59  57 byte

Manipulasi string.

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

Cobalah online!

Bagaimana?

Kami pertama-tama mengonversi ke heksadesimal untuk memastikan bahwa semua terdepan disertakan:n+2320

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

Cobalah online!

Kami menggunakan persamaan reguler /\B../guntuk mencocokkan semua grup yang terdiri dari 2 digit, mengabaikan terimakasih atas ( batas non- kata ).1\B

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

Cobalah online!

Kami reverse()dan join()untuk mendapatkan string terakhir.


JavaScript (ES6), 61 byte

Fungsi rekursif.

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

Cobalah online!

Arnauld
sumber
⭐ - Anda mendapatkan bintang untuk jawaban yang bagus - Saya suka, pendek tapi masih bersih dan "dapat diubah manusia" :)
Kamil Kiełczewski
5

C # (Visual C # Interactive Compiler) , 54 byte

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

Disimpan 4 byte berkat @PeterCordes

Cobalah online!

Penjelasan

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
Perwujudan Ketidaktahuan
sumber
Bisakah Anda mengecilkan 4278255360konstanta topeng ke 16711935( 0xff00ff) jika Anda menggeser sebelum menutup? Atau apakah itu memerlukan tambahan paren? Juga, jika tidak maka 0xff00ff00panjangnya sama tetapi jauh lebih bermakna bagi manusia.
Peter Cordes
@PeterCordes Ini juga memiliki keuntungan tambahan untuk dapat menghapus tanda kurung karena >>memiliki prioritas lebih tinggi daripada &, yang disimpan total 4 byte. Terima kasih!
Perwujudan Ketidaktahuan
Keren. Di bagian "penjelasan" Anda, saya sarankan menulis konstanta dalam hex.
Peter Cordes
4

Japt -P , 10 byte

sG ùT8 ò w

Cobalah

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
sumber
Apa yang -Pharus dilakukan
SS Anne
🚀 jawaban Anda ada di atas (dapatkah Anda menambahkan penjelasan?)
Kamil Kiełczewski
@ JL2210 Dari dokumen : " -P: Jika output adalah sebuah array, output tanpa pemisah (yaitu bergabung dengan P). ". Jadi flag adalah untuk implisit, bukan gabungan eksplisit untuk menyimpan byte. :)
Kevin Cruijssen
2
@ KamilKiełczewski, penjelasan ditambahkan.
Shaggy
4

C (gcc) , 30 byte

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

Cobalah online!

SS Anne
sumber
ketika dijalankan pada mesin big-endian, ini tidak akan dikonversi menjadi little-endian?
peter ferrie
@peterferrie Lihat revisi 3.
SS Anne
4

Python 2 , 43 byte

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

Cobalah online!

-4 byte terima kasih kepada benrg

Mengeluarkan daftar karakter. Dihitung dengan mengambil, secara berurutan, digit hex input pada indeks 6, 7, 4, 5, 2, 3, 0, 1.

negatif tujuh
sumber
2
[i^6]for i in range(8)menghemat beberapa byte.
benrg
Apakah diizinkan untuk menampilkan daftar, bukan string?
Qwertiy
keluaran sebagai daftar tidak benar - benar cocok untuk semangat pertanyaan imo
qwr
3

C (gcc) endian agnostik, tidak ada lib standar, 92 91 byte

h(n)adalah fungsi integer- digit hex tunggal.
f(x,p)membutuhkan integer dan char[8]pointer. Hasilnya adalah 8 byte chardata. ( Tidak diakhiri 0 kecuali penelepon melakukan itu.)

Asumsi: Kumpulan karakter ASCII. 2 ini melengkapi intsehingga shift kanan akhirnya membawa ke bit tanda, dan mengkonversi uint32_tke inttidak munge bit-pola jika bit tinggi ditetapkan. intsetidaknya 32-bit. (Lebih luas mungkin membiarkannya bekerja pada komplemen 1 atau implementasi sign-magnitude C).

Non-asumsi: apa pun tentang implementasi byte-order atau penandatanganan 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);}

Cobalah online! termasuk pemanggil uji yang digunakan printf("%.8s\n", buf)untuk mencetak buffer keluaran tanpa menghentikannya 0.

Tidak Disatukan:

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);
}

Melakukan n&=15;di dalam h(x)adalah titik impas; 6 byte ada vs. 3 masing-masing untuk &15mengisolasi gigitan rendah di kedua situs panggilan.

,adalah titik urutan (atau setara dalam terminologi modern) sehingga aman untuk dilakukan *p++= stuffdua kali dalam satu pernyataan ketika dipisahkan oleh ,operator.

>>pada integer yang ditandatangani adalah implementasi-didefinisikan sebagai aritmatika atau logis. GNU C mendefinisikannya sebagai pelengkap aritmatika 2. Tetapi pada mesin komplemen 2 yang mana pun, itu tidak terlalu penting karena kita tidak pernah melihat bit 0 atau pergeseran dari bit tanda. MSB asli pada akhirnya akan masuk ke byte rendah tidak berubah. Ini tidak terjadi pada tanda / besarnya, dan saya tidak yakin tentang komplemen 1.

Jadi ini mungkin hanya portable untuk implementasi C 2 yang melengkapi. (Atau di mana intadalah lebih luas dari 32 bit sehingga sedikit 31 adalah hanya bagian dari besarnya.) Unsigned -> ditandatangani konversi juga munges sedikit-pola untuk bilangan bulat negatif, sehingga &15pada inthanya akan mengambil camilan dari nilai unsigned asli pada komplemen 2 ini. Sekali lagi, kecuali intitu lebih luas dari 32-bit sehingga semua input non-negatif.

Versi golf memiliki UB dari jatuh dari fungsi non-void. Bukan untuk mengembalikan nilai, hanya untuk menghindari mendeklarasikan voidbukan default int. Kompiler modern akan memutus ini dengan optimasi diaktifkan.


Motivasi: Saya sedang mempertimbangkan jawaban x86 atau ARM Thumb asm, berpikir mungkin menyenangkan untuk melakukannya secara manual dalam C, mungkin untuk asm yang dihasilkan oleh kompiler sebagai titik awal. Lihat /programming/53823756/how-to-convert-a-number-to-hex untuk asm x86 yang hemat kecepatan, termasuk versi AVX512VBMI yang hanya 2 instruksi (tetapi membutuhkan vektor kontrol untuk vpmultishiftqb dan vpshufb jadi tidak akan bagus untuk golf). Biasanya diperlukan kerja ekstra untuk SIMD untuk byte-reverse ke dalam urutan pencetakan pada little-endian x86 sehingga output hex byte-terbalik ini sebenarnya lebih mudah dari biasanya.


Ide lain

Saya mempertimbangkan mengambil integer dengan referensi dan mengulangi byte-nya dengan char*, pada implementasi C little-endian (seperti x86 atau ARM). Tetapi saya tidak berpikir itu akan menyelamatkan banyak.

Menggunakan sprintfuntuk melakukan 1 byte pada suatu waktu, 64 byte setelah bermain golf:

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

Tetapi jika kita menggunakan fungsi seperti printf, kita mungkin juga melakukan byte-swap dan melakukan %xprintf dari semuanya seperti jawaban @ JL2210 .

Peter Cordes
sumber
⭐ - Anda mendapatkan bintang untuk jawaban yang bagus
Kamil Kiełczewski
3

kode mesin SIMD x86 (AVX512-VBMI), 36 byte

(16 byte di antaranya adalah tabel hex lookup)

Ini adalah fungsi yang mengambil integer xmm0dan mengembalikan 8 byte data char ASCII xmm0, untuk penelepon untuk menyimpan di mana pun ia mau. (mis. ke memori video setelah interleaving dengan byte atribut, atau ke dalam string yang sedang dibangun, atau apa pun)

Dari C, sebut itu seperti __m128i retval = lehex(_mm_cvtsi32_si128(x))dengan konvensi pemanggilan System V x86-64, atau 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 byte.

Lihat Bagaimana mengonversi angka menjadi hex? pada SO untuk cara kerjanya. (SSE2 untuk shift / punpck, lalu vpermbsimpan pekerjaan yang kita perlukan pshufb. AVX1 alih-alih SSE2 / SSSE3 juga menghindari movapssalinan register.)

Perhatikan bahwa punpcklbwdengan operan sumber dalam urutan itu akan memberi kita nibble paling signifikan dari byte input rendah dalam elemen byte terendah, kemudian nibble paling signifikan dari byte sumber terendah. (Dalam jawaban SO itu, a bswapdigunakan pada input untuk mendapatkan hasil dalam urutan pencetakan standar dengan hanya SSE2. Tetapi di sini kami menginginkan urutan itu: menggigit tinggi dalam elemen yang lebih rendah dalam setiap byte, tetapi masih urutan byte sedikit-endian).

Jika kita memiliki lebih banyak konstanta data, kita dapat menghemat ruang mode pengalamatan dengan melakukan satu mov edx, imm32lalu menggunakan [rdx+16]atau mode pengalamatan apa pun. Atau vpbroadcastb xmm0, [rdx+1].

Tapi saya pikir hex 16-byte LUT + vpermbmasih lebih baik daripada menerapkan n>9 : n+'a'-10 : n+'0'kondisi: yang membutuhkan 3 konstanta dan setidaknya 3 instruksi dengan AVX512BW byte-masking (bandingkan dengan mask vpaddb, merge-masked vpaddb), atau lebih dengan AVX1 atau SSE2. (Lihat Bagaimana mengonversi angka menjadi hex? Pada SO untuk versi SSE2 itu). Dan setiap instruksi AVX512BW setidaknya sepanjang 6 byte (EVEX + opcode + modrm 4-byte), lebih lama dengan perpindahan dalam mode pengalamatan.

Sebenarnya dibutuhkan setidaknya 4 instruksi karena kita perlu membersihkan sampah yang tinggi dengan andps, (atau EVEX vpandddengan operan memori siaran 4-byte) sebelum membandingkan. Dan masing-masing membutuhkan konstanta vektor yang berbeda. AVX512 memiliki broadcast operan memori, tetapi hanya untuk elemen 32-bit dan lebih luas. mis . operan terakhir EVEXvpaddb hanya xmm3/m128, tidak xmm3/m128/m8bcst. (Port muat Intel hanya dapat melakukan siaran 32 dan 64-bit secara gratis sebagai bagian dari load uop sehingga Intel mendesain AVX512BW untuk mencerminkan itu dan tidak dapat menyandikan operan byte atau word broadcast memory sama sekali, alih-alih memberi mereka opsi untuk lakukan siaran dword sehingga Anda masih dapat memampatkan konstanta Anda menjadi 4 byte: /.)

Alasan saya menggunakan AVX512VBMIvpermb bukan SSSE3 / AVX1 pshufbada dua:

  • vpermbmengabaikan bit pemilih yang tinggi. (v)pshufbnol byte sesuai dengan bit tinggi dari vektor kontrol dan akan membutuhkan tambahan pandatau andpsuntuk benar-benar mengisolasi camilan. Dengan ukuran XMM / 16-byte, vpermbhanya terlihat pada 4 bit rendah elemen kontrol-acak, yaitu bit [3:0]dalam notasi Intel di bagian Operasi .
  • vpermbdapat mengambil data untuk dikocok (tabel pencarian) sebagai operan memori. (v)pshufbOperan xmm / mem adalah vektor kendali-acak.

Perhatikan bahwa AVX512VBMI hanya tersedia di CannonLake / Ice Lake sehingga Anda mungkin perlu simulator untuk menguji ini, seperti SDE Intel.

Peter Cordes
sumber
⭐ - Anda mendapatkan bintang untuk jawaban yang bagus
Kamil Kiełczewski
@ KamilKiełczewski: lol terima kasih. Mengubah angka menjadi hex secara efisien adalah salah satu hal favorit saya. Ini adalah case yang bagus untuk beberapa trik dan manipulasi bit.
Peter Cordes
3

Scala , 58 40 36 byte

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

Cobalah online!

Masih menggunakan builtin untuk membalik byte Int, tetapi menggunakan formatuntuk memformat Intsebagai Hex. Tidak perlu menelepon toHexString.

Dihidupkan parens format. Ini sekarang berarti bahwa argumen dapat diambil secara implisit _.

Bersabun
sumber
2

Keempat (gforth) , 52 51 40 byte

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

Cobalah online!

Penjelasan kode

: 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
sumber
2

Jelly , 13 byte

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

Cobalah online!

Program lengkap yang menggunakan integer sebagai argumennya dan mencetak string.

Nick Kennedy
sumber
🚀 jawaban Anda ada di atas
Kamil Kiełczewski
2

Excel, 91 byte

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

K4 , 12 11 byte

Larutan:

,/$|4_0x0\:

Contoh:

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

Penjelasan:

Persis pertanyaan yang diajukan:

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

Catatan:

  • -1 byte sebagai nomor K4 adalah panjang (64bit) secara default, jadi menjatuhkan 4 byte (32 bit)
streetster
sumber
🚀 jawaban Anda ada di atas
Kamil Kiełczewski
2

PHP , 31 byte

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

Cobalah online!

Mengambil keuntungan dari paket PHP dan membongkar , saya kemas input yang tidak ditandatangani dengan format "32 bit endian byte order" ( V) ke dalam string biner dan kemudian membongkar dengan format "hex string, high nibble first" ( H) dan mencetak hasilnya.

Ini tampaknya menjadi salah satu kasus langka di mana PHP bawaan sebenarnya lebih pendek daripada menerapkan algoritma sederhana!

Night2
sumber
PHP pack()/ unpack()fungsinya luar biasa untuk 0 kali Anda membutuhkannya di sebagian besar proyek PHP. Selamat, Anda menemukan penggunaannya!
640KB
1

Arang , 11 byte

⪫⮌⪪﹪%08xN²ω

Cobalah online! Tautan adalah untuk mengucapkan versi kode. Penjelasan:

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

19 byte tanpa menggunakan format Python:

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

Cobalah online! Tautan adalah untuk mengucapkan versi kode. Penjelasan:

           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
sumber
🚀 jawaban Anda ada di atas
Kamil Kiełczewski
1

J , 10 byte

8{._1{3!:3

Cobalah online!

bagaimana

3!:3adalah "konjungsi asing" untuk representasi hex, didokumentasikan di sini . Artinya, itu adalah builtin untuk mengkonversi ke hex. Namun, hasilnya tidak seperti yang kita inginkan. Misalnya, berlari:

3!:3 (304767)

menghasilkan:

e300000000000000
0400000000000000
0100000000000000
0000000000000000
7fa6040000000000

Arti dari baris lain dijelaskan pada halaman doc yang saya tautkan di atas. Bagaimanapun, jelas kami menginginkan 8 karakter pertama dari baris terakhir.

_1{ dapatkan baris terakhir.

8{. mendapat 8 karakter pertama dari itu.

Jonah
sumber
🚀 jawaban Anda ada di atas
Kamil Kiełczewski
1

Ruby , 31 27 byte

Akhirnya menjadi port dari jawaban PHP Night2 karena Ruby memiliki fungsionalitas paket / pembongkaran yang sama.

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

Cobalah online!

Jawaban 31 byte asli saya yang tidak memanfaatkan mode H8 unpack karena saya tidak mengetahuinya:

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

Cobalah online!

Nilai Tinta
sumber
1

Windows Batch, 90 byte

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

Jalankan command-line dengan / v untuk mengaktifkan ekspansi yang tertunda.

Peter Ferrie
sumber
1

x86 kode mesin 32-bit, 24 21 byte

changelog: -3 byte: ganti add standar / cmp / jbe / add dengan hack DAS oleh @peter ferrie

64-bit: masih 24 byte. Mode lama menghapus opcode DAS.
Mode 16-bit: ukuran operan standar adalah 16-bit tetapi spesifikasi masalahnya pada dasarnya 32-bit. Termasuk hard-kode 8 digit hex.


Bolak-balik dengan bswapint-> manual kemudian hex dalam urutan standar (paling signifikan menggigit pertama, menulis digit hex ke buffer output char dalam urutan menaik). Ini menghindari perlu membuka gulungan untuk beralih urutan antara menggigit dalam byte vs melintasi byte.

Dapat dipanggil void lehex(char buf[8] /*edi*/, uint32_t x /*esi*/);seperti x86-64 Sistem V, kecuali ini tidak berfungsi dalam mode 64-bit. (Perlu pointer output di EDI untuk stosb. Nomor input dapat dalam register selain ECX atau 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

size = 0x15 = 21 byte.

TIO FASM 32-bit kasus uji x86 dengan pemanggil asm yang menggunakan pemanggilan writesistem untuk menulis output setelah memanggilnya dua kali untuk menambahkan 2 string ke dalam buffer. Menguji semua digit hex 0..F, termasuk 9 dan A pada batas antara angka vs huruf.

The DAShack - x86 memiliki setengah-membawa bendera, untuk membawa keluar dari nibble rendah. Berguna untuk hal-hal BCD yang dikemas seperti instruksi DAS, dimaksudkan untuk digunakan setelah mengurangi dua bilangan bulat BCD 2 digit. Dengan nibble rendah AL yang berada di luar rentang 0-9, kami pasti menyalahgunakannya di sini.

Perhatikan if (old_AL > 99H) or (old_CF = 1)LALU AL ← AL − 60H;bagian dari bagian Operasi dalam manual; sbb selalu mengatur CF di sini sehingga bagian itu selalu terjadi. Itu dan kisaran ASCII untuk huruf besar adalah apa yang memotivasi pilihansub al, 0x69

  • cmp 0xD, 0xA tidak mengatur CF
  • sbb 0xD - 0x69wraps ke AL ​​= 0xA4sebagai input ke DAS. (Dan mengatur CF, menghapus AF)
  • tidak ada AL - = 6 pada bagian pertama DAS (karena 4> 9 salah dan AF = 0)
  • AL - = 0x60 di bagian kedua, meninggalkan 0x44, kode ASCII untuk'D'

vs. angka:

  • cmp 0x3, 0xA mengatur CF
  • sbb 3 - 0x69 - 1= AL = 0x99 dan mengatur CF dan AF
  • tidak ada AL - = 6 pada bagian pertama DAS (9> 9 salah tetapi AF diatur), meninggalkan 0x93
  • AL - = 0x60 di bagian kedua, meninggalkan 0x33, kode ASCII untuk '3'.

Mengurangkan 0x6adalam SBB akan menetapkan AF untuk setiap digit <= 9 sehingga semua angka mengikuti logika yang sama. Dan biarkan itu dibersihkan untuk setiap digit hex alfabet. yaitu dengan benar mengeksploitasi penanganan split 9 / A DAS.


Biasanya (untuk kinerja) Anda akan menggunakan tabel pencarian untuk loop skalar, atau mungkin 2x tanpa cabang leadan cmp/cmovtambahan bersyarat. Tetapi al, imm8instruksi 2-byte adalah kemenangan besar untuk ukuran kode.


versi versi x86-64 : hanya bagian yang berbeda, antara and al, 0xfdan 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:

Perhatikan bahwa add al, '0' selalu berjalan, dan penambahan bersyarat hanya menambahkan perbedaan antara 'a'-10dan '0', untuk membuatnya hanya sebagai ifpengganti if/ else.

Diuji dan berfungsi, menggunakan mainpenelepon yang sama dengan jawaban C saya , yang menggunakan char buf[8]dan printf("%.8s\n", buf).

Peter Cordes
sumber
dapatkah Anda membuat cuplikan yang berfungsi online misalnya di sini ?
Kamil Kiełczewski
@ KamilKiełczewski: TIO membuat tidak mungkin (AFAIK) untuk menulis pemanggil dalam C untuk menguji fungsi asm jadi saya sering tidak repot, tapi yakin karena Anda bertanya dan sys_writedapat menampilkan string dengan panjang tetap dengan mudah. Oh menarik, saya tidak menyadari FASM di TIO membiarkan Anda membuat executable 32-bit, tidak seperti dengan NASM di mana ia tidak menghormati -felf32. Saya lebih suka x86-64, dan jawaban ini tidak menyimpan byte dari kode 32-bit.
Peter Cordes
⭐ - Anda mendapatkan bintang untuk jawaban yang bagus
Kamil Kiełczewski
1
@ JL2210: Maksud Anda sprintf? Saya tidak berpikir libc memiliki fungsi string int-> berguna selain yang berbasis format-string, hanya string-> int seperti strtoul. Tapi ya, bswap / printf mungkin akan lebih pendek, jika Anda bisa mencari cara untuk menghitung byte untuk entri GOT untuk suatu fungsi di perpustakaan dinamis (selain call [rel printf wrt ..got]situs panggilan 6-byte ); executable yang terhubung secara statis minimal dapat secara signifikan lebih kecil dari dinamis, setidaknya ketika dibuat lddengan default normal. Tapi saya tidak berpikir itu akan masuk akal untuk menghubungkannya secara statis tetapi tidak menghitung ukuran kodenya.
Peter Cordes
1
@ JL2210: Ingat, ini adalah jawaban kode mesin x86 , bukan asm ukuran sumber teks. Saya belum pernah menggunakan fungsi libc dalam jawaban kode mesin sebelumnya, hanya panggilan sistem Linux (misalnya dalam Fibonacci), dan IDK bagaimana saya akan menghitung biaya atau apakah saya bahkan ingin menulis jawaban kode mesin dengan libc . Ada beberapa kasus penggunaan untuk kode mesin x86 di mana libc tidak tersedia, misalnya dalam bootloader.
Peter Cordes