Преобразование числа с прямым порядком байтов в строку

13

Вступление

При работе с генератором BMP (битмап) я сталкиваюсь с проблемой преобразования числа в шестнадцатеричную строку с прямым порядком байтов. Вот функция, которую я создаю в JavaScript - но интересно, как маленький код может работать аналогично

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

Вызов

Напишите функцию, которая будет принимать 32-разрядное целое число без знака на входе и генерировать 8-значную шестнадцатеричную строку с прямым порядком байтов. Пример алгоритма, который выполняет работу:

  • преобразовать numb в шестнадцатеричную строку, например: 304767 -> '4a67f'
  • добавьте нули для заполнения, чтобы получить 8-символьную строку: '0004a67f'
  • разбить строку на четыре части по 2 символа: '00','04','a6','7f'
  • обратный порядок штук '7f','a6','04','00'
  • соединить фигуры и вернуть в результате: '7fa60400'

Пример ввода и вывода

Входной номер (или строка с номером dec) слева ->, выходная шестнадцатеричная строка справа

2141586432 -> 0004a67f
304767     -> 7fa60400
Камил Келчевски
источник

Ответы:

7

05AB1E , 10 9 байтов

žJ+h¦2ôRJ

Попробуйте онлайн!

-1 байт по вдохновению ответа желе.

ž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 байт

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

Попробуйте онлайн!

Арифметическое рекурсивное решение ( 50 49 байтов, работает также для Python 2 ) :

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

Попробуйте онлайн!

-1 байт благодаря @JonathanAllan

Joel
источник
Я бы сказал, представить рекурсивную как запись Python 2 :)
Джонатан Аллан
f=lambda n,i=4:i*'1'and'%02x'%(n%256)+f(n>>8,i-1)сохраняет байт :)
Джонатан Аллан
@JonathanAllan Спасибо. Я не знаком со всеми трюками Python 2 и не понимаю, как его можно сделать короче.
Джоэл
это не так, но 37 не будет работать в py 2
Джонатан Аллан
Да уж. Некоторые из этих встроенных программ предназначены только для Python-3.
Джоэл
6

R , 54 53 байта

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

Попробуйте онлайн!

Каждая группа из 2 символов на самом деле является шестнадцатеричным представлением цифры в базе 256. scan()%/%256^(0:3)%%256преобразует в базовое число 256 с обратными 4 цифрами, ...%*%256^(3:0)объединяет их в одно целое число и format.hexmode(...,8)преобразует это число в свое шестнадцатеричное представление с 8 цифрами.

Робин Райдер
источник
5

JavaScript (ES7),  59  57 байт

Струнные манипуляции.

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

Попробуйте онлайн!

Как?

Сначала мы конвертируем N+232 в шестнадцатеричное, чтобы убедиться, что все ведущие 0 включены:

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

Попробуйте онлайн!

Мы используем регулярное выражение /\B../gдля сопоставления всех групп из 2 цифр, игнорируя начальную 1 благодаря \B(без слов границы ).

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

Попробуйте онлайн!

Мы reverse()и join()получили финальную строку.


JavaScript (ES6), 61 байт

Рекурсивная функция.

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

Попробуйте онлайн!

Arnauld
источник
⭐ - ты получаешь звезду за хороший ответ - мне это нравится, коротко, но все еще чисто и "читабельно" :)
Kamil Kiełczewski
5

C # (интерактивный компилятор Visual C #) , 54 байта

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

Сохранено 4 байта благодаря @PeterCordes

Попробуйте онлайн!

объяснение

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
Воплощение невежества
источник
Можете ли вы уменьшить 4278255360константу маски до 16711935( 0xff00ff), если вы смещаетесь перед маскированием? Или это стоит дополнительных паренов? Кроме того, если нет, то 0xff00ff00такой же длины, но гораздо более значимым для людей.
Питер Кордес
@PeterCordes Он также имеет дополнительное преимущество, заключающееся в возможности удалять скобки, так как >>имеет более высокий приоритет, чем &при сохранении всего 4 байта. Благодаря!
Воплощение невежества
Здорово. В вашем разделе «объяснение» я бы предложил записать константы в шестнадцатеричном виде.
Питер Кордес
4

Japt -P , 10 байт

sG ùT8 ò w

Попробуй

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
мохнатый
источник
Что делает -P?
SS Anne
🚀 ваш ответ в топе (вы можете добавить объяснение?)
Камиль Келчевски
@ JL2210 Из документов : « -P: Если выход является массивом, то выходы не имеют разделителя (то есть объединены с P). ». Таким образом, флаг предназначен для неявного, а не явного соединения для сохранения байтов. :)
Кевин Круйссен
2
@ KamilKiełczewski, объяснение добавлено.
Shaggy
4

C (gcc) , 30 байтов

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

Попробуйте онлайн!

СС Энн
источник
при запуске на машине с прямым порядком байтов это не преобразуется в последовательность с прямым порядком байтов?
Питер Ферри
@peterferrie См. редакцию 3.
SS Anne
4

Python 2 , 43 байта

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

Попробуйте онлайн!

-4 байта благодаря benrg

Выводит список символов. Вычисляется путем получения по порядку шестнадцатеричных цифр ввода по индексам 6, 7, 4, 5, 2, 3, 0, 1.

минус семь
источник
2
[i^6]for i in range(8)сохраняет несколько байтов.
Бенг
Разрешено ли выводить список вместо строки?
Qwertiy
вывод в виде списка не совсем соответствует духу вопроса imo
qwr
3

C (gcc) endian-агностик, без стандартных библиотек, 92 91 байт

h(n)является однозначным целым числом -> шестнадцатеричная вспомогательная функция.
f(x,p)берет целое число и char[8]указатель. Результат - 8 байтов charданных. ( Не завершается 0, если вызывающий не делает этого.)

Допущения: набор символов ASCII. 2 дополняют, intпоэтому сдвиг вправо в конечном итоге приводит к уменьшению знакового бита, и преобразование uint32_tв intне изменяет битовую комбинацию, если установлен старший бит. intпо крайней мере 32-битный. (Более широкий мог бы позволить этому работать на реализациях дополнения 1 или величины C знака).

Не предположения: что-нибудь о реализации байтового порядка или подписанности 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);}

Попробуйте онлайн! включая использование вызывающего теста printf("%.8s\n", buf)для печати выходного буфера без 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);
}

Делать n&=15;внутри h(x)безубыточно; 6 байтов против 3 для каждого, &15чтобы изолировать низкий клев на обоих участках вызова.

,является точкой последовательности (или эквивалентной в современной терминологии), поэтому ее можно сделать *p++= stuffдважды в одном операторе, когда они разделены ,оператором.

>>целое число со знаком определяется реализацией как арифметического или логического. GNU C определяет его как дополнение арифметики 2. Но на любом дополнительном компьютере 2 это не имеет большого значения, потому что мы никогда не смотрим на сдвинутые 0 или копии знакового бита. Первоначальный MSB в конечном итоге перейдет в младший байт без изменений. Это не относится к знаку / величине, и я не уверен насчет дополнения 1.

Так что это может быть переносимо только на 2 дополнения Си реализации. (Или где intон шире, чем 32 бита, поэтому бит 31 является лишь частью величины). Unsigned -> знаковое преобразование также обрабатывает битовую комбинацию для отрицательных целых чисел, поэтому &15при intизвлечении только отрывки исходного значения без знака на дополнении 2. Опять же, если только он не intбыл шире 32-битного, поэтому все входы неотрицательны.

У версии для гольфа есть UB от падения незаполненной функции. Не возвращать значение, просто чтобы не объявить его voidвместо значения по умолчанию int. Современные компиляторы сломают это с включенной оптимизацией.


Мотивация: я рассматривал асм-ответ на x86 или ARM Thumb, подумал, что было бы забавно сделать это вручную в C, возможно, для сгенерированного компилятором asm в качестве отправной точки. См. Https://stackoverflow.com/questions/53823756/how-to-convert-a-number-to-hex для получения информации о быстродействующем x86 asm, включая версию AVX512VBMI, в которой всего 2 инструкции (но нужны векторы управления для vpmultishiftqb и vpshufb так что не было бы здорово для гольфа). Обычно SIMD требуется дополнительная работа для преобразования байтов в порядок печати на младшем байтовом коде x86, так что этот вывод в шестнадцатеричном виде с обращенными байтами на самом деле проще, чем обычно.


Другие идеи

Я подумал о том, чтобы взять целое число по ссылке и зациклить его байты с char*реализацией C с прямым порядком байтов (например, x86 или ARM). Но я не думаю, что это спасло бы многое.

Используется sprintfдля выполнения 1 байта за раз, 64 байта после игры в гольф:

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

Но если мы используем функции, похожие на printf, мы могли бы также поменять байты и сделать %xprintf всего этого, как ответ @ JL2210 .

Питер Кордес
источник
⭐ - вы получите звезду за хороший ответ
Камиль Келчевски
3

Машинный код x86 SIMD (AVX512-VBMI), 36 байт

(16 байтов из которых являются шестнадцатеричной таблицей поиска)

Это функция, которая принимает целое число xmm0и возвращает 8 байтов данных ASCII-символов xmm0, чтобы вызывающая сторона сохраняла их в любом месте. (например, в видеопамять после чередования с байтами атрибута, или в строящейся строке, или как угодно)

Начиная с C, называйте его так же, как __m128i retval = lehex(_mm_cvtsi32_si128(x))в соглашении о вызовах System V x86-64 или в 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"

Всего = 0x24 = 36 байт.

См. Как преобразовать число в гекс? на ТАК, как это работает. (SSE2 для shift / punpck, затем vpermbсохраняет работу, в которой мы нуждались pshufb. AVX1 вместо SSE2 / SSSE3 также избегает movapsкопирования регистра.)

Обратите внимание, что punpcklbwс исходными операндами в этом порядке мы получим самый значительный кусок маленького входного байта в младшем элементе байта, а затем младший значащий младший байтовый источник. (В этом ответе SO, a bswapиспользуется на входе для получения результата в стандартном порядке печати только с SSE2. Но здесь мы хотим этот порядок: высокий клев в нижнем элементе в каждом байте, но все еще порядок байтов с прямым порядком байтов).

Если бы у нас было больше констант данных, мы могли бы сэкономить место в режиме адресации, выполнив одну из них, mov edx, imm32используя [rdx+16]любой из режимов адресации. Или vpbroadcastb xmm0, [rdx+1].

Но я думаю, что 16-байтовое шестнадцатеричное LUT + vpermbвсе же лучше, чем выполнение n>9 : n+'a'-10 : n+'0'условия: для этого требуется 3 константы и как минимум 3 инструкции с байтовой маскировкой AVX512BW (сравнение с маской, маскировка vpaddbслиянием vpaddb) или более с AVX1 или SSE2. (См. Как преобразовать число в шестнадцатеричное? На SO для версии SSE2 этого). И каждая инструкция AVX512BW имеет длину не менее 6 байтов (4-байтовый EVEX + код операции + modrm), длиннее со смещением в режиме адресации.

На самом деле это займет не менее 4-х инструкций, потому что нам нужно очистить большое количество мусора с andps(или EVEX vpanddс 4-байтовым операндом широковещательной памяти) перед сравнением. И каждому из них нужна своя векторная константа. AVX512 имеет операнды широковещательной памяти, но только для 32-битных и более широких элементов. Например , последний операнд EVEXvpaddb - только xmm3/m128, нет xmm3/m128/m8bcst. (Порты загрузки Intel могут бесплатно выполнять только 32- и 64-разрядные широковещательные рассылки как часть загрузки, поэтому Intel разработала AVX512BW, чтобы отразить это и вообще не иметь возможности кодировать операнды памяти байтового или словосочетания, вместо того, чтобы дать им возможность делайте двойные трансляции, чтобы вы могли сжать свои константы до 4 байтов: /.)

Причина, по которой я использовал AVX512VBMIvpermb вместо SSSE3 / AVX1, двояка pshufb:

  • vpermbигнорирует старшие биты селекторов. (v)pshufbнули байты в соответствии с старшим битом управляющего вектора и потребовались бы дополнительные pandили andpsдля фактического выделения полубайтов. При размере XMM / 16 байт vpermbрассматриваются только младшие 4 бита элементов управления перемешиванием, т. Е. Биты [3:0]в нотации Intel в разделе « Эксплуатация» .
  • vpermbможет принимать данные, которые будут перетасовываться (таблица поиска), как операнд памяти. (v)pshufbОперанд xmm / mem является вектором управления перемешиванием.

Обратите внимание, что AVX512VBMI доступен только на CannonLake / Ice Lake, поэтому вам, вероятно, понадобится имитатор для тестирования, например, Intel SDE.

Питер Кордес
источник
⭐ - вы получите звезду за хороший ответ
Камиль Келчевски
@ KamilKiełczewski: LOL спасибо. Эффективное преобразование чисел в шестнадцатеричные - одна из моих любимых вещей. Это хороший пример использования нескольких хитрых трюков и манипуляций с битами.
Питер Кордес,
3

Scala , 58 40 36 байт

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

Попробуйте онлайн!

Все еще использует встроенную функцию для обращения байтов Int, но использует formatдля форматирования Intшестнадцатеричный формат . Не нужно звонить toHexString.

Убрал паренсов на format. Теперь это означает, что аргумент может быть принят неявно с помощью _.

мыльный
источник
2

Forth (gforth) , 52 51 40 байтов

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

Попробуйте онлайн!

Объяснение кода

: 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
источник
2

Желе , 13 байт

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

Попробуйте онлайн!

Полная программа, которая принимает целое число в качестве аргумента и печатает строку.

Ник Кеннеди
источник
🚀 ваш ответ в топе
Kamil Kiełczewski
2

Excel, 91 байт

=RIGHT(DEC2HEX(A1,8),2)&MID(DEC2HEX(A1,8),5,2)&MID(DEC2HEX(A1,8),3,2)&LEFT(DEC2HEX(A1,8),2)
Wernisch
источник
2

К4 , 12 11 байт

Решение:

,/$|4_0x0\:

Примеры:

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

Объяснение:

В значительной степени именно то, что задает вопрос:

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

Заметки:

  • -1 байт, поскольку числа K4 по умолчанию являются длинными (64 бита), поэтому отбрасывание 4 байтов (32 бита)
streetster
источник
🚀 ваш ответ в топе
Камиль Келчевски
2

PHP , 31 байт

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

Попробуйте онлайн!

Воспользовавшись пакетом PHP и распаковав его , я упаковываю неподписанные входные данные с форматом «32-битный порядок байтов с байтовым порядком байтов» ( V) в двоичную строку, а затем распаковываю их с форматом «шестнадцатеричная строка, сначала большой клев» ( H) и печатаю результат.

Похоже, это один из редких случаев, когда встроенные PHP-программы на самом деле короче, чем реализация простого алгоритма!

night2
источник
Функции pack()/ unpack()функции в PHP великолепны тем, что в большинстве проектов PHP они вам когда-либо нужны. Поздравляю, вы нашли свое применение!
640 КБ
1

Древесный уголь , 11 байт

⪫⮌⪪﹪%08xN²ω

Попробуйте онлайн! Ссылка на подробную версию кода. Объяснение:

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

19 байтов без обращения к форматированию Python:

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

Попробуйте онлайн! Ссылка на подробную версию кода. Объяснение:

           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
Нил
источник
🚀 ваш ответ в топе
Камиль Келчевски
1

J , 10 байт

8{._1{3!:3

Попробуйте онлайн!

как

3!:3является J "внешним соединением" для шестнадцатеричного представления, документированного здесь . То есть это встроенная функция для преобразования в гекс. Тем не менее, это вывод, это не совсем то, что мы хотим. Например, работает:

3!:3 (304767)

производит:

e300000000000000
0400000000000000
0100000000000000
0000000000000000
7fa6040000000000

Значение других строк объясняется на странице документа, на которую я ссылался выше. В любом случае ясно, что мы хотим первые 8 символов последней строки.

_1{ получить последнюю строку.

8{. получает первые 8 символов этого.

Ион
источник
🚀 ваш ответ в топе
Камиль Келчевски
1

Рубин , 31 27 байт

В конечном итоге это был порт ответа PHP на Night2, потому что Ruby имеет те же функции упаковки / распаковки.

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

Попробуйте онлайн!

Мой оригинальный 31-байтовый ответ, в котором не использовался режим распаковки H8, потому что я не знал об этом:

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

Попробуйте онлайн!

Значение чернил
источник
1

Пакет Windows, 90 байт

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

Запустите командную строку с / v, чтобы включить отложенное расширение.

Питер Ферри
источник
1

32-битный машинный код x86, 24 21 байт

changelog: -3 байта: заменить стандартное add / cmp / jbe / add взломом DAS на @peter ferrie

64-разрядный: все еще 24 байта. В длинном режиме удален код операции DAS.
16-битный режим: размер операнда по умолчанию - 16-битный, но спецификация проблемы - 32-битная. Включая 8 шестнадцатеричных цифр.


Обратный байт с bswapручным int-> hex в стандартном порядке (сначала идет самый значительный полубайт, запись шестнадцатеричных цифр в буфер вывода символов в порядке возрастания.) Это позволяет избежать необходимости развернуть цикл для переключения порядка между полубайтами в байте и через байты.

Вызывается как void lehex(char buf[8] /*edi*/, uint32_t x /*esi*/);x86-64 System V, за исключением того, что он не работает в 64-битном режиме. (Для этого требуется указатель вывода в EDI stosb. Входной номер может быть в любом регистре, кроме ECX или 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

размер = 0x15 = 21 байт.

32-разрядный тестовый пример x86 для TIO FASM с вызывающей стороной asm, которая использует writeсистемный вызов для записи вывода после двойного вызова, чтобы добавить 2 строки в буфер. Проверяет все шестнадцатеричные цифры 0..F, включая 9 и A на границе между цифрой и буквой.

DASхак - x86 имеет флаг половинной переноса, для переноса из - за низкой клев. Полезно для вещей с упакованными BCD, таких как инструкция DAS, предназначенных для использования после вычитания двух двузначных целых чисел BCD. При низком значении AL вне диапазона 0-9, мы определенно злоупотребляем этим здесь.

Обратите внимание на if (old_AL > 99H) or (old_CF = 1)ТОГДА AL ← AL − 60H;часть раздела «Эксплуатация» в руководстве; sbb всегда устанавливает CF здесь, чтобы эта часть всегда происходила. Это и диапазон ASCII для заглавных букв - это то, что мотивирует выборsub al, 0x69

  • cmp 0xD, 0xA не устанавливает CF
  • sbb 0xD - 0x69переносится в AL = 0xA4как ввод в DAS. (И устанавливает CF, очищает AF)
  • нет AL - = 6 в первой части DAS (потому что 4> 9 ложно и AF = 0)
  • AL - = 0x60 во второй части, оставляя 0x44код ASCII для'D'

против цифры:

  • cmp 0x3, 0xA устанавливает CF
  • sbb 3 - 0x69 - 1= AL = 0x99 и устанавливает CF и AF
  • нет AL - = 6 в первой части DAS (9> 9 ложно, но AF установлен), оставляя 0x93
  • AL - = 0x60 во второй части, оставляя 0x33, код ASCII для '3'.

Вычитание 0x6aв SBB установит AF для каждой цифры <= 9, поэтому все цифры следуют той же логике. И оставьте это очищенным для каждой буквенной шестнадцатеричной цифры. то есть правильно использовать 9 / A разделенную обработку DAS.


Обычно (для производительности) вы используете таблицу поиска для скалярного цикла или, возможно, 2x без ветвления leaи cmp/cmovусловного добавления. Но 2-байтовые al, imm8инструкции - большая победа для размера кода.


версия для x86-64 : просто другая часть, между and al, 0xfи 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:

Обратите внимание, что add al, '0' всегда выполняется, и условное добавление только добавляет разницу между 'a'-10и '0', чтобы сделать его просто ifвместо if/else .

Протестировано и работает, используя тот же mainвызывающий, что и мой ответ C , который использует char buf[8]и printf("%.8s\n", buf).

Питер Кордес
источник
Вы можете создать работающий онлайн-фрагмент, например, здесь ?
Камиль Келчевски
@ KamilKiełczewski: TIO делает невозможным (AFAIK) написать вызывающую программу на C, чтобы протестировать функцию asm, поэтому я часто не беспокоюсь, но уверен, так как вы спросили и sys_writeможете легко выводить строки фиксированной длины. О, интересно, я не понял, что FASM на TIO позволяет вам создавать 32-битные исполняемые файлы, в отличие от NASM, где это не уважает -felf32. В любом случае я предпочитаю x86-64, и этот ответ не сохраняет байты из 32-битного кода.
Питер Кордес,
⭐ - Вы получите звезду за хороший ответ
Камиль Келчевски
1
@ JL2210: Вы имеете в виду sprintf? Я не думаю, что в libc есть какие-либо удобные функции int-> string, кроме функций на основе форматных строк, только string-> int, например strtoul. Но да, bswap / printf, вероятно, будет короче, если вы сможете найти какой-нибудь способ подсчета байтов для записи GOT для функции в динамической библиотеке (помимо 6-байтового call [rel printf wrt ..got]сайта вызова); минимальные статически связанные исполняемые файлы могут быть значительно меньше динамических, по крайней мере, если они сделаны ldс обычными значениями по умолчанию. Но я не думаю, что было бы разумно статически связывать его, но не считать его размер кода.
Питер Кордес
1
@ JL2210: Помните, это ответ машинного кода x86 , а не размер исходного текста asm. Я не использовал функции libc в предыдущих ответах машинного кода, только системные вызовы Linux (например, в Фибоначчи) и IDK, как бы я посчитал стоимость или даже хочу написать ответы машинный код с libc , Существуют варианты использования для машинного кода x86, когда libc недоступен, например, в загрузчике.
Питер Кордес