Вступление
При работе с генератором 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
f=lambda n,i=4:i*'1'and'%02x'%(n%256)+f(n>>8,i-1)
сохраняет байт :)R ,
5453 байтаПопробуйте онлайн!
Каждая группа из 2 символов на самом деле является шестнадцатеричным представлением цифры в базе 256.
scan()%/%256^(0:3)%%256
преобразует в базовое число 256 с обратными 4 цифрами,...%*%256^(3:0)
объединяет их в одно целое число иformat.hexmode(...,8)
преобразует это число в свое шестнадцатеричное представление с 8 цифрами.источник
JavaScript (ES7),
5957 байтСтрунные манипуляции.
Попробуйте онлайн!
Как?
Сначала мы конвертируемn + 232 в шестнадцатеричное, чтобы убедиться, что все ведущие 0 включены:
Попробуйте онлайн!
Мы используем регулярное выражение1 благодаря
/\B../g
для сопоставления всех групп из 2 цифр, игнорируя начальную\B
(без слов границы ).Попробуйте онлайн!
Мы
reverse()
иjoin()
получили финальную строку.JavaScript (ES6), 61 байт
Рекурсивная функция.
Попробуйте онлайн!
источник
Zsh , 46 байтов
Попробуйте онлайн!
источник
C # (интерактивный компилятор Visual C #) , 54 байта
Сохранено 4 байта благодаря @PeterCordes
Попробуйте онлайн!
объяснение
источник
4278255360
константу маски до16711935
(0xff00ff
), если вы смещаетесь перед маскированием? Или это стоит дополнительных паренов? Кроме того, если нет, то0xff00ff00
такой же длины, но гораздо более значимым для людей.>>
имеет более высокий приоритет, чем&
при сохранении всего 4 байта. Благодаря!Japt
-P
, 10 байтПопробуй
источник
-P
?-P
: Если выход является массивом, то выходы не имеют разделителя (то есть объединены сP
). ». Таким образом, флаг предназначен для неявного, а не явного соединения для сохранения байтов. :)C (gcc) , 30 байтов
Попробуйте онлайн!
источник
Python 2 , 43 байта
Попробуйте онлайн!
-4 байта благодаря benrg
Выводит список символов. Вычисляется путем получения по порядку шестнадцатеричных цифр ввода по индексам
6, 7, 4, 5, 2, 3, 0, 1
.источник
[i^6]for i in range(8)
сохраняет несколько байтов.C (gcc) endian-агностик, без стандартных библиотек,
9291 байтh(n)
является однозначным целым числом -> шестнадцатеричная вспомогательная функция.f(x,p)
берет целое число иchar[8]
указатель. Результат - 8 байтовchar
данных. ( Не завершается 0, если вызывающий не делает этого.)Допущения: набор символов ASCII. 2 дополняют,
int
поэтому сдвиг вправо в конечном итоге приводит к уменьшению знакового бита, и преобразованиеuint32_t
вint
не изменяет битовую комбинацию, если установлен старший бит.int
по крайней мере 32-битный. (Более широкий мог бы позволить этому работать на реализациях дополнения 1 или величины C знака).Не предположения: что-нибудь о реализации байтового порядка или подписанности
char
.Попробуйте онлайн! включая использование вызывающего теста
printf("%.8s\n", buf)
для печати выходного буфера без 0-его завершения.Ungolfed:
Делать
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 байта после игры в гольф:Но если мы используем функции, похожие на printf, мы могли бы также поменять байты и сделать
%x
printf всего этого, как ответ @ JL2210 .источник
Машинный код x86 SIMD (AVX512-VBMI), 36 байт
(16 байтов из которых являются шестнадцатеричной таблицей поиска)
Это функция, которая принимает целое число
xmm0
и возвращает 8 байтов данных ASCII-символовxmm0
, чтобы вызывающая сторона сохраняла их в любом месте. (например, в видеопамять после чередования с байтами атрибута, или в строящейся строке, или как угодно)Начиная с C, называйте его так же, как
__m128i retval = lehex(_mm_cvtsi32_si128(x))
в соглашении о вызовах System V x86-64 или в MS Windowsvectorcall
.Всего = 0x24 = 36 байт.
См. Как преобразовать число в гекс? на ТАК, как это работает. (SSE2 для shift / punpck, затем
vpermb
сохраняет работу, в которой мы нуждалисьpshufb
. AVX1 вместо SSE2 / SSSE3 также избегаетmovaps
копирования регистра.)Обратите внимание, что
punpcklbw
с исходными операндами в этом порядке мы получим самый значительный кусок маленького входного байта в младшем элементе байта, а затем младший значащий младший байтовый источник. (В этом ответе SO, abswap
используется на входе для получения результата в стандартном порядке печати только с 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
(или EVEXvpandd
с 4-байтовым операндом широковещательной памяти) перед сравнением. И каждому из них нужна своя векторная константа. AVX512 имеет операнды широковещательной памяти, но только для 32-битных и более широких элементов. Например , последний операнд EVEXvpaddb
- толькоxmm3/m128
, нетxmm3/m128/m8bcst
. (Порты загрузки Intel могут бесплатно выполнять только 32- и 64-разрядные широковещательные рассылки как часть загрузки, поэтому Intel разработала AVX512BW, чтобы отразить это и вообще не иметь возможности кодировать операнды памяти байтового или словосочетания, вместо того, чтобы дать им возможность делайте двойные трансляции, чтобы вы могли сжать свои константы до 4 байтов: /.)Причина, по которой я использовал AVX512VBMI
vpermb
вместо 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.
источник
Scala ,
584036 байтПопробуйте онлайн!
Все еще использует встроенную функцию для обращения байтов
Int
, но используетformat
для форматированияInt
шестнадцатеричный формат . Не нужно звонитьtoHexString
.Убрал паренсов на
format
. Теперь это означает, что аргумент может быть принят неявно с помощью_
.источник
Forth (gforth) ,
52 5140 байтовПопробуйте онлайн!
Объяснение кода
источник
Желе , 13 байт
Попробуйте онлайн!
Полная программа, которая принимает целое число в качестве аргумента и печатает строку.
источник
APL + WIN,
3634 байта2 байта сохранены путем преобразования в нулевой индекс
Запрашивает целое число:
Попробуйте онлайн! Предоставлено Dyalog Classic
источник
Excel, 91 байт
источник
К4 ,
1211 байтРешение:
Примеры:
Объяснение:
В значительной степени именно то, что задает вопрос:
Заметки:
источник
PHP , 31 байт
Попробуйте онлайн!
Воспользовавшись пакетом PHP и распаковав его , я упаковываю неподписанные входные данные с форматом «32-битный порядок байтов с байтовым порядком байтов» (
V
) в двоичную строку, а затем распаковываю их с форматом «шестнадцатеричная строка, сначала большой клев» (H
) и печатаю результат.Похоже, это один из редких случаев, когда встроенные PHP-программы на самом деле короче, чем реализация простого алгоритма!
источник
pack()
/unpack()
функции в PHP великолепны тем, что в большинстве проектов PHP они вам когда-либо нужны. Поздравляю, вы нашли свое применение!Древесный уголь , 11 байт
Попробуйте онлайн! Ссылка на подробную версию кода. Объяснение:
19 байтов без обращения к форматированию Python:
Попробуйте онлайн! Ссылка на подробную версию кода. Объяснение:
источник
Perl 5 (-p), 22 байта
Попробуйте онлайн!
источник
J , 10 байт
Попробуйте онлайн!
как
3!:3
является J "внешним соединением" для шестнадцатеричного представления, документированного здесь . То есть это встроенная функция для преобразования в гекс. Тем не менее, это вывод, это не совсем то, что мы хотим. Например, работает:производит:
Значение других строк объясняется на странице документа, на которую я ссылался выше. В любом случае ясно, что мы хотим первые 8 символов последней строки.
_1{
получить последнюю строку.8{.
получает первые 8 символов этого.источник
Рубин ,
3127 байтВ конечном итоге это был порт ответа PHP на Night2, потому что Ruby имеет те же функции упаковки / распаковки.
Попробуйте онлайн!
Мой оригинальный 31-байтовый ответ, в котором не использовался режим распаковки H8, потому что я не знал об этом:
Попробуйте онлайн!
источник
Пакет Windows, 90 байт
Запустите командную строку с / v, чтобы включить отложенное расширение.
источник
32-битный машинный код x86,
2421 байт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-битном режиме. (Для этого требуется указатель вывода в EDIstosb
. Входной номер может быть в любом регистре, кроме ECX или EAX.)размер = 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
не устанавливает CF0xD - 0x69
переносится в AL =0xA4
как ввод в DAS. (И устанавливает CF, очищает AF)0x44
код ASCII для'D'
против цифры:
cmp 0x3, 0xA
устанавливает CF3 - 0x69 - 1
= AL = 0x99 и устанавливает CF и AF'3'
.Вычитание
0x6a
в SBB установит AF для каждой цифры <= 9, поэтому все цифры следуют той же логике. И оставьте это очищенным для каждой буквенной шестнадцатеричной цифры. то есть правильно использовать 9 / A разделенную обработку DAS.Обычно (для производительности) вы используете таблицу поиска для скалярного цикла или, возможно, 2x без ветвления
lea
иcmp/cmov
условного добавления. Но 2-байтовыеal, imm8
инструкции - большая победа для размера кода.версия для x86-64 : просто другая часть, между
and al, 0xf
иstosb
.Обратите внимание, что
add al, '0'
всегда выполняется, и условное добавление только добавляет разницу между'a'-10
и'0'
, чтобы сделать его простоif
вместоif
/else
.Протестировано и работает, используя тот же
main
вызывающий, что и мой ответ C , который используетchar buf[8]
иprintf("%.8s\n", buf)
.источник
sys_write
можете легко выводить строки фиксированной длины. О, интересно, я не понял, что FASM на TIO позволяет вам создавать 32-битные исполняемые файлы, в отличие от NASM, где это не уважает-felf32
. В любом случае я предпочитаю x86-64, и этот ответ не сохраняет байты из 32-битного кода.sprintf
? Я не думаю, что в libc есть какие-либо удобные функции int-> string, кроме функций на основе форматных строк, только string-> int, например strtoul. Но да, bswap / printf, вероятно, будет короче, если вы сможете найти какой-нибудь способ подсчета байтов для записи GOT для функции в динамической библиотеке (помимо 6-байтовогоcall [rel printf wrt ..got]
сайта вызова); минимальные статически связанные исполняемые файлы могут быть значительно меньше динамических, по крайней мере, если они сделаныld
с обычными значениями по умолчанию. Но я не думаю, что было бы разумно статически связывать его, но не считать его размер кода.