Chuyển đổi chuỗi số nhỏ thành chuỗi


13

Giới thiệu

Trong quá trình làm việc với trình tạo BMP (bitmap), tôi gặp phải vấn đề chuyển đổi số thành chuỗi hex cuối nhỏ. Đây là chức năng mà tôi tạo trong JavaScript - nhưng tự hỏi làm thế nào mã nhỏ có thể hoạt động tương tự

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

Thử thách

Hàm ghi sẽ lấy số nguyên không dấu 32 bit trên đầu vào và tạo ra chuỗi thập lục phân 8 chữ số với thứ tự cuối nhỏ. Thuật toán ví dụ thực hiện công việc:

  • chuyển đổi tê thành chuỗi hex, ví dụ: 304767 -> '4a67f'
  • thêm số không đệm để có chuỗi 8 ký tự: '0004a67f'
  • chia chuỗi thành bốn mảnh 2-char: '00','04','a6','7f'
  • thứ tự đảo ngược của miếng '7f','a6','04','00'
  • tham gia các mảnh và trả về như kết quả: '7fa60400'

Ví dụ đầu vào và đầu ra

Số đầu vào (hoặc chuỗi có số dec) ở bên trái ->, chuỗi hex đầu ra ở bên phải

2141586432 -> 0004a67f
304767     -> 7fa60400

Câu trả lời:


7

05AB1E , 10 9 byte

žJ+h¦2ôRJ

Hãy thử trực tuyến!

-1 byte theo cảm hứng của câu trả lời 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

6

Python 3 , 37 byte

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

Hãy thử trực tuyến!

Giải pháp đệ quy dựa trên số học ( 50 49 byte, cũng hoạt động với Python 2 ) :

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

Hãy thử trực tuyến!

-1 byte nhờ @Jonathan ALLan


Tôi muốn gửi đệ quy dưới dạng mục nhập Python 2 :)
Jonathan Allan

f=lambda n,i=4:i*'1'and'%02x'%(n%256)+f(n>>8,i-1)tiết kiệm một byte :)
Jonathan Allan

@Jonathan ALLan Cảm ơn. Tôi không quen với tất cả các thủ thuật Python 2 và không thấy làm thế nào nó có thể được rút ngắn hơn.
Joel

không, nhưng 37 sẽ không hoạt động trong py 2
Jonathan Allan

Vâng. Một số trong những phần mềm tích hợp này chỉ có Python-3.
Joel

6

R , 54 53 byte

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

Hãy thử trực tuyến!

Mỗi nhóm gồm 2 ký tự thực sự là biểu diễn hex của một chữ số trong cơ sở 256. scan()%/%256^(0:3)%%256chuyển đổi thành một số cơ sở 256 với 4 chữ số được đảo ngược, ...%*%256^(3:0)nối chúng thành một số nguyên duy nhất và format.hexmode(...,8)chuyển đổi số đó thành biểu diễn hex của nó với 8 chữ số.


5

JavaScript (ES7),  59  57 byte

Thao tác chuỗi.

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

Hãy thử trực tuyến!

Làm sao?

n+2320

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

Hãy thử trực tuyến!

/\B../g1\B

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

Hãy thử trực tuyến!

Chúng tôi reverse()join()để có được chuỗi cuối cùng.


JavaScript (ES6), 61 byte

Hàm đệ quy.

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

Hãy thử trực tuyến!


- bạn nhận được ngôi sao cho câu trả lời hay - Tôi thích nó, ngắn nhưng vẫn sạch sẽ và "có thể sửa được con người" :)
Kamil Kiełczewski


5

C # (Trình biên dịch tương tác Visual C #) , 54 byte

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

Đã lưu 4 byte nhờ @PeterCordes

Hãy thử trực tuyến!

Giải trình

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

Bạn có thể thu nhỏ 4278255360mặt nạ không đổi thành 16711935( 0xff00ff) nếu bạn thay đổi trước khi đắp mặt nạ? Hoặc điều đó có chi phí thêm parens? Ngoài ra, nếu không thì 0xff00ff00có cùng chiều dài nhưng có ý nghĩa hơn nhiều đối với con người.
Peter Cordes

@PeterCordes Nó cũng có thêm lợi thế là có thể loại bỏ các dấu ngoặc >>có độ ưu tiên cao hơn &, khi lưu tổng cộng 4 byte. Cảm ơn!
Hiện thân của sự thiếu hiểu biết

Mát mẻ. Trong phần "giải thích" của bạn, tôi khuyên bạn nên viết các hằng số bằng hex.
Peter Cordes

4

Japt -P , 10 byte

sG ùT8 ò w

Thử nó

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

Không gì -Plàm gì?
SS Anne

Answer câu trả lời của bạn nằm ở đầu (bạn có thể thêm lời giải thích không?)
Kamil Kiełczewski

@ JL2210 Từ các tài liệu : " -P: Nếu đầu ra là một mảng, đầu ra không có dấu phân cách (nghĩa là được nối với P). ". Vì vậy, cờ là cho một ẩn thay vì nối rõ ràng để lưu byte. :)
Kevin Cruijssen

2
@ KamilKiełczewski, giải thích thêm.
Shaggy


4

Python 2 , 43 byte

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

Hãy thử trực tuyến!

-4 byte nhờ benrg

Xuất ra một danh sách các ký tự. Được tính bằng cách truy xuất, theo thứ tự, các chữ số hex của đầu vào tại các chỉ số 6, 7, 4, 5, 2, 3, 0, 1.


2
[i^6]for i in range(8)tiết kiệm một vài byte.
benrg

Được phép đưa ra danh sách đầu ra thay vì chuỗi?
Qwertiy

đầu ra dưới dạng danh sách không thực sự phù hợp với tinh thần của câu hỏi imo
qwr

3

Thuyết bất khả tri C (gcc) , không có libs chuẩn, 92 91 byte

h(n)là một số nguyên một chữ số-> hàm trợ giúp hex.
f(x,p)lấy một số nguyên và một char[8]con trỏ. Kết quả là 8 byte chardữ liệu. ( Không kết thúc 0 trừ khi người gọi thực hiện việc đó.)

Giả định: bộ ký tự ASCII. Bổ sung của 2 intvì vậy sự thay đổi bên phải cuối cùng sẽ làm giảm bit dấu và chuyển đổi uint32_tthành intkhông thay đổi mẫu bit nếu bit cao được đặt. intít nhất là 32 bit. (Rộng hơn có thể cho phép nó hoạt động trên các triển khai C bổ sung hoặc cường độ ký hiệu 1).

Không giả định: bất cứ điều gì về thứ tự byte thực hiện hoặc chữ ký của 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);}

Hãy thử trực tuyến! bao gồm cả người gọi kiểm tra sử dụng printf("%.8s\n", buf)để in bộ đệm đầu ra mà không kết thúc 0.

Ung dung:

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

Làm n&=15;bên trong h(x)là hòa vốn; 6 byte ở đó so với 3 mỗi cái &15để cô lập mức độ thấp ở cả hai trang web cuộc gọi.

,là một điểm thứ tự (hoặc tương đương theo thuật ngữ hiện đại), do đó, an toàn để thực hiện *p++= stuffhai lần trong một câu lệnh khi được phân tách bởi ,toán tử.

>>trên số nguyên đã ký được triển khai - được định nghĩa là số học hoặc logic. GNU C định nghĩa nó là phần bù 2 của số học. Nhưng trên máy bổ sung của bất kỳ 2 nào, điều đó không thực sự quan trọng bởi vì chúng tôi không bao giờ nhìn vào 0 hoặc các bản sao của bit dấu được thay đổi. MSB ban đầu cuối cùng sẽ chuyển xuống byte thấp không thay đổi. Đây không phải là trường hợp về dấu hiệu / cường độ và tôi không chắc chắn về phần bù của 1.

Vì vậy, điều này chỉ có thể được thực hiện cho 2 triển khai C bổ sung. (Hoặc nơi intrộng hơn so với 32 bit để cắn 31 chỉ là một phần của độ lớn.) Unsigned -> chuyển đổi ký cũng munges bit-khuôn mẫu cho số nguyên âm, vì vậy &15trên intsẽ chỉ trích Nibbles giá trị unsigned gốc trên bổ sung 2 của. Một lần nữa, trừ khi intrộng hơn so với 32-bit vì vậy tất cả đầu vào là không âm.

Phiên bản chơi gôn có UB từ phần cuối của hàm không trống. Không trả về giá trị, chỉ để tránh khai báo voidthay vì mặc định int. Trình biên dịch hiện đại sẽ phá vỡ điều này với tối ưu hóa được kích hoạt.


Động lực: Tôi đã xem xét câu trả lời asm x86 hoặc ARM Thumb, nghĩ rằng có thể rất vui khi thực hiện thủ công trong C, có thể lấy asm do trình biên dịch tạo làm điểm bắt đầu. Xem /programming/53823756/how-to-convert-a-number-to-hex để biết x86 asm hiệu quả tốc độ, bao gồm phiên bản AVX512VBMI chỉ có 2 hướng dẫn (nhưng cần vectơ điều khiển cho vpmultishiftqb và vpshufb vì vậy sẽ không tốt cho golf). Thông thường, cần thêm công việc để SIMD đảo ngược byte thành thứ tự in trên x86 endian nhỏ, do đó, đầu ra hex đảo ngược byte này thực sự dễ dàng hơn bình thường.


Những ý tưởng khác

Tôi đã xem xét việc lấy số nguyên bằng cách tham chiếu và lặp qua các byte của nó char*, trên một triển khai C nhỏ về cuối (như x86 hoặc ARM). Nhưng tôi không nghĩ rằng sẽ tiết kiệm được nhiều.

Sử dụng sprintfđể thực hiện 1 byte mỗi lần, 64 byte sau khi chơi gôn:

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

Nhưng nếu chúng ta đang sử dụng các hàm giống như printf, chúng ta cũng có thể trao đổi byte và thực hiện một %xprintf của toàn bộ điều như câu trả lời của @ JL2210 .


- bạn nhận được ngôi sao cho câu trả lời hay
Kamil Kiełczewski

3

Mã máy x86 SIMD (AVX512-VBMI), 36 byte

(16 byte trong đó là bảng tra cứu hex)

Đây là một hàm lấy một số nguyên trong xmm0và trả về 8 byte dữ liệu char ASCII xmm0, để người gọi lưu trữ bất cứ nơi nào nó muốn. (ví dụ: bộ nhớ video sau khi xen kẽ với các byte thuộc tính hoặc vào một chuỗi đang được xây dựng hoặc bất cứ điều gì)

Từ C, gọi nó như __m128i retval = lehex(_mm_cvtsi32_si128(x))với quy ước gọi System V x86-64 hoặc 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"

Tổng = 0x24 = 36 byte.

Xem Làm thế nào để chuyển đổi một số thành hex? trên SO cho cách thức này hoạt động. (SSE2 cho ca / ​​Punpck, sau đó vpermblưu công việc mà chúng tôi cần pshufb. AVX1 thay vì SSE2 / SSSE3 cũng tránh movapsbản sao đăng ký.)

Lưu ý rằng punpcklbwvới các toán hạng nguồn theo thứ tự đó sẽ cung cấp cho chúng ta mức độ đáng kể nhất của byte đầu vào thấp trong phần tử byte thấp nhất, sau đó là mức thấp nhất có ý nghĩa của byte nguồn thấp nhất. (Trong câu trả lời SO đó, a bswapđược sử dụng trên đầu vào để có kết quả theo thứ tự in tiêu chuẩn chỉ với SSE2. Nhưng ở đây chúng tôi muốn thứ tự đó: nibble cao trong phần tử thấp hơn trong mỗi byte, nhưng vẫn là thứ tự byte cuối nhỏ).

Nếu chúng ta có nhiều hằng số dữ liệu, chúng ta có thể tiết kiệm không gian chế độ địa chỉ bằng cách thực hiện mov edx, imm32sau đó sử dụng [rdx+16]hoặc bất kỳ chế độ địa chỉ nào. Hoặc vpbroadcastb xmm0, [rdx+1].

Nhưng tôi nghĩ rằng LUT + hex 16 byte vpermbvẫn tốt hơn so với thực hiện n>9 : n+'a'-10 : n+'0'điều kiện: yêu cầu 3 hằng số và ít nhất 3 hướng dẫn với mặt nạ byte AVX512BW (so sánh với mặt nạ, mặt nạ vpaddbhợp nhất vpaddb) hoặc nhiều hơn với AVX1 hoặc SSE2. (Xem Cách chuyển đổi một số thành hex? Trên SO cho phiên bản SSE2 của số đó). Và mỗi lệnh AVX512BW dài ít nhất 6 byte (EVEX + opcode + modrm 4 byte), dài hơn với sự dịch chuyển trong chế độ địa chỉ.

Trên thực tế, sẽ mất ít nhất 4 hướng dẫn bởi vì chúng tôi cần xóa rác cao với andps(hoặc EVEX vpanddvới toán hạng bộ nhớ phát 4 byte) trước khi so sánh. Và mỗi cái cần một hằng số vectơ khác nhau. AVX512 có các toán hạng bộ nhớ phát sóng, nhưng chỉ dành cho các phần tử 32 bit và rộng hơn. ví dụ: toán hạng cuối cùng của EVEXvpaddb là duy nhất xmm3/m128, không phải xmm3/m128/m8bcst. (Các cổng tải của Intel chỉ có thể phát miễn phí 32 và 64 bit như một phần của tải trọng, do đó, Intel đã thiết kế AVX512BW để phản ánh điều đó và không thể mã hóa các toán hạng bộ nhớ phát byte hoặc từ, thay vì cho chúng tùy chọn thực hiện truyền phát từ khóa để bạn vẫn có thể nén các hằng số của mình thành 4 byte: /.)

Lý do tôi sử dụng AVX512VBMIvpermb thay vì SSSE3 / AVX1 pshufbcó hai mặt:

  • vpermbbỏ qua các bit cao của bộ chọn. (v)pshufbsố 0 byte theo bit cao của vectơ điều khiển và sẽ cần thêm pandhoặc andpsđể thực sự cô lập nibble. Với kích thước XMM / 16 byte, vpermbchỉ nhìn vào 4 bit thấp của các phần tử điều khiển xáo trộn, tức là các bit [3:0]trong ký hiệu của Intel trong phần Hoạt động .
  • vpermbcó thể lấy dữ liệu được xáo trộn (bảng tra cứu) làm toán hạng bộ nhớ. (v)pshufbToán hạng xmm / mem là vectơ điều khiển xáo trộn.

Lưu ý rằng AVX512VBMI chỉ khả dụng trên CannonLake / Ice Lake, do đó bạn có thể cần một trình giả lập để kiểm tra điều này, như SDE của Intel.


- bạn nhận được ngôi sao cho câu trả lời hay
Kamil Kiełczewski

@ KamilKiełczewski: lol cảm ơn. Chuyển đổi số thành hex hiệu quả là một trong những điều yêu thích của tôi. Đây là một trường hợp sử dụng tốt cho một số thủ thuật gọn gàng và thao tác bit.
Peter Cordes

3

Scala , 58 40 36 byte

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

Hãy thử trực tuyến!

Vẫn sử dụng nội dung để đảo ngược các byte của một Int, nhưng sử dụng formatđể định dạng dưới dạng IntHex. Không cần gọi toHexString.

Loại bỏ các parens trên format. Điều này bây giờ có nghĩa là đối số có thể được sử dụng hoàn toàn bằng cách sử dụng_ .


2

Forth (gforth) , 52 51 40 byte

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

Hãy thử trực tuyến!

Giải thích mã

: 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



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)

2

K4 , 12 11 byte

Giải pháp:

,/$|4_0x0\:

Ví dụ:

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

Giải trình:

Chính xác những gì câu hỏi yêu cầu:

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

Ghi chú:

  • Theo mặc định, số byte 1 K4 là dài (64 bit), do đó giảm 4 byte (32 bit)

Answer câu trả lời của bạn nằm trong top
Kamil Kiełczewski

2

PHP , 31 byte

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

Hãy thử trực tuyến!

Tận dụng góigiải nén của PHP , tôi đóng gói đầu vào không dấu với định dạng "thứ tự byte cuối nhỏ 32 bit" ( V) thành một chuỗi nhị phân và sau đó giải nén nó với định dạng "chuỗi hex, đầu tiên nibble cao" (H ) và in kết quả.

Đây dường như là một trong những trường hợp hiếm hoi mà các phần dựng sẵn của PHP thực sự ngắn hơn so với việc thực hiện một thuật toán đơn giản!


Các hàm pack()/ unpack()hàm của PHP là tuyệt vời cho 0 lần bạn cần chúng trong hầu hết các dự án PHP. Xin chúc mừng, bạn đã tìm thấy công dụng của chúng!
640KB

1

Than , 11 byte

⪫⮌⪪﹪%08xN²ω

Hãy thử trực tuyến! Liên kết là phiên bản dài dòng của mã. Giải trình:

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

19 byte mà không cần dùng đến định dạng Python:

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

Hãy thử trực tuyến! Liên kết là phiên bản dài dòng của mã. Giải trình:

           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

Answer câu trả lời của bạn nằm trong top
Kamil Kiełczewski


1

J , 10 byte

8{._1{3!:3

Hãy thử trực tuyến!

làm sao

3!:3là một "kết hợp nước ngoài" J cho đại diện hex, được ghi lại ở đây . Đó là, nó là một nội dung để chuyển đổi thành hex. Tuy nhiên, nó xuất ra nó không hoàn toàn như những gì chúng ta muốn. Ví dụ: đang chạy:

3!:3 (304767)

sản xuất:

e300000000000000
0400000000000000
0100000000000000
0000000000000000
7fa6040000000000

Ý nghĩa của các dòng khác được giải thích trên trang tài liệu tôi liên kết đến ở trên. Trong mọi trường hợp, rõ ràng chúng tôi muốn 8 ký tự đầu tiên của dòng cuối cùng.

_1{ lấy dòng cuối cùng.

8{. được 8 ký tự đầu tiên của nó.


Answer câu trả lời của bạn nằm trong top
Kamil Kiełczewski


1

Batch Windows, 90 byte

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

Chạy dòng lệnh với / v để cho phép mở rộng bị trì hoãn.


1

mã máy x86 32 bit, 24 21 byte

changelog: -3 byte: thay thế add / cmp / jbe / add tiêu chuẩn bằng hack DAS bởi @peter ferrie

64-bit: vẫn là 24 byte. Chế độ dài đã loại bỏ opcode DAS.
Chế độ 16 bit: kích thước toán hạng mặc định là 16 bit nhưng thông số kỹ thuật vốn dĩ là 32 bit. Bao gồm 8 chữ số hex cứng.


Byte-Reverse với bswapthủ công int-> hex theo thứ tự tiêu chuẩn (đầu tiên là đáng kể nhất, viết các chữ số hex vào bộ đệm đầu ra char theo thứ tự tăng dần.) Điều này tránh việc phải hủy bỏ vòng lặp để chuyển đổi thứ tự giữa các nib trong một byte so với trên các byte.

Có thể gọi void lehex(char buf[8] /*edi*/, uint32_t x /*esi*/);như x86-64 System V, ngoại trừ việc này không hoạt động ở chế độ 64 bit. (Nó cần con trỏ đầu ra trong EDI cho stosb. Số đầu vào có thể ở bất kỳ thanh ghi nào ngoài ECX hoặc 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

kích thước = 0x15 = 21 byte.

Trường hợp thử nghiệm TIO FASM 32-bit x86 với một người gọi asm sử dụng writelệnh gọi hệ thống để ghi đầu ra sau khi gọi hai lần để nối 2 chuỗi vào bộ đệm. Kiểm tra tất cả các chữ số hex 0..F, bao gồm 9 và A tại ranh giới giữa chữ số và chữ cái.

Vụ DAShack - x86 có cờ nửa mang, để mang ra khỏi tầm thấp. Hữu ích cho các công cụ BCD đóng gói như hướng dẫn DAS, dự định sử dụng sau khi trừ hai số nguyên BCD 2 chữ số. Với khả năng thấp của AL nằm ngoài phạm vi 0-9, chúng tôi chắc chắn lạm dụng nó ở đây.

Lưu ý phần if (old_AL > 99H) or (old_CF = 1)THEN AL ← AL − 60H;của phần Hoạt động trong hướng dẫn; sbb luôn đặt CF ở đây để phần đó luôn xảy ra. Điều đó và phạm vi ASCII cho chữ in hoa là những gì thúc đẩy sự lựa chọn củasub al, 0x69

  • cmp 0xD, 0xA không đặt CF
  • sbb 0xD - 0x69kết thúc tốt đẹp với AL = 0xA4làm đầu vào cho DAS. (Và đặt CF, xóa AF)
  • không AL - = 6 trong phần đầu tiên của DAS (vì 4> 9 là sai và AF = 0)
  • AL - = 0x60 trong phần thứ hai, để lại 0x44, mã ASCII cho'D'

so với một con số:

  • cmp 0x3, 0xA đặt CF
  • sbb 3 - 0x69 - 1 = AL = 0x99 và đặt CF và AF
  • không AL - = 6 trong phần đầu tiên của DAS (9> 9 là sai nhưng AF được đặt), để lại 0x93
  • AL - = 0x60 trong phần thứ hai, để lại 0x33, mã ASCII cho '3'.

Phép trừ 0x6atrong SBB sẽ đặt AF cho mọi chữ số <= 9 để tất cả các chữ số tuân theo cùng một logic. Và để lại nó xóa cho mỗi chữ số hex chữ cái. tức là khai thác chính xác việc xử lý phân chia 9 / A của DAS.


Thông thường (để thực hiện) bạn sẽ sử dụng bảng tra cứu cho một vòng vô hướng hoặc có thể là một nhánh không có điều kiện 2x leacmp/cmovbổ sung có điều kiện. Nhưng các al, imm8hướng dẫn 2 byte là một chiến thắng lớn cho kích thước mã.


phiên bản x86-64 : chỉ là phần khác nhau, giữa and al, 0xfstosb.

;; 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:

Lưu ý rằng add al, '0' luôn luôn chạy và bổ sung có điều kiện chỉ thêm sự khác biệt giữa 'a'-10'0', để làm cho nó chỉ là một ifthay vì if/else .

Đã kiểm tra và hoạt động, sử dụng cùng một mainngười gọi như câu trả lời C của tôi , sử dụng char buf[8]printf("%.8s\n", buf).


bạn có thể tạo đoạn trích làm việc trực tuyến trong ví dụ ở đây không?
Kamil Kiełczewski

@ KamilKiełczewski: TIO không thể (AFAIK) viết người gọi bằng C để kiểm tra chức năng asm nên tôi thường không bận tâm, nhưng chắc chắn vì bạn đã hỏi và sys_writecó thể dễ dàng xuất ra các chuỗi có độ dài cố định. Thật thú vị, tôi đã không nhận ra FASM trên TIO cho phép bạn tạo các tệp thực thi 32 bit, không giống như NASM nơi nó không tôn trọng -felf32. Dù sao tôi cũng thích x86-64 hơn và câu trả lời này không lưu bất kỳ byte nào từ mã 32 bit.
Peter Cordes

- bạn nhận được ngôi sao cho câu trả lời hay
Kamil Kiełczewski

1
@ JL2210: Ý bạn là sprintfsao? Tôi không nghĩ libc có bất kỳ hàm int-> chuỗi tiện dụng nào ngoài các hàm dựa trên chuỗi định dạng, chỉ có chuỗi-> int như strtoul. Nhưng vâng, bswap / printf có thể sẽ ngắn hơn, nếu bạn có thể tìm ra cách nào đó để đếm byte cho mục nhập GOT cho một chức năng trong thư viện động (bên cạnh call [rel printf wrt ..got]trang web cuộc gọi 6 byte ); một thực thi được liên kết tĩnh tối thiểu có thể nhỏ hơn đáng kể so với động, ít nhất là khi được thực hiện bằng ldcác mặc định thông thường. Nhưng tôi không nghĩ sẽ hợp lý khi liên kết tĩnh nhưng không tính kích thước mã của nó.
Peter Cordes

1
@ JL2210: Hãy nhớ rằng, đây là câu trả lời mã máy x86 , không phải kích thước nguồn văn bản asm. Tôi đã không sử dụng các hàm libc trong các câu trả lời mã máy trước đây, chỉ các cuộc gọi hệ thống Linux (ví dụ như trong Fibonacci) và IDK về cách tôi sẽ tính chi phí hoặc thậm chí tôi có muốn viết câu trả lời bằng mã máy với libc không . Có các trường hợp sử dụng cho mã máy x86 trong đó libc không khả dụng, ví dụ như trong bộ tải khởi động.
Peter Cordes
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.