Машинный код x86-64, 22 байта
48 B8 41 92 34 6D DB F7 FF FF 83 F9 40 7D 03 48 D3 E8 83 E0 01 C3
Вышеуказанные байты определяют функцию в 64-битном машинном коде x86, которая определяет, является ли входное значение числом Чикен МакНуггет. Один положительный целочисленный параметр передается в ECX
регистр в соответствии с 64-битным соглашением о вызовах Microsoft, используемым в Windows. Результатом является логическое значение, возвращаемое в EAX
регистре.
Неуправляемая сборка мнемоники:
; bool IsMcNuggetNumber(int n)
; n is passed in ECX
movabs rax, 0xFFFFF7DB6D349241 ; load a 64-bit constant (the bit field)
cmp ecx, 64
jge TheEnd ; if input value >= 64, branch to end
shr rax, cl
TheEnd:
and eax, 1 ; mask off all but LSB
ret
Очевидно, что это сильно противоречит решению Андерса Касерга в Python , так как оно основано на битовом поле, представляющем значения, представляющие собой числа Чикен МакНуггет. В частности, каждый бит в этом поле, который соответствует действительному номеру Chicken McNugget, устанавливается в 1; все остальные биты установлены в 0. (Это считает, что 0 является действительным числом Chicken McNugget, но если вам это не нравится, ваши предпочтения - это однобитная модификация.)
Мы начинаем с простой загрузки этого значения в регистр. Это 64-битное значение, для кодирования которого уже требуется 8 байтов, плюс нам нужен однобайтовый префикс REX.W, так что мы действительно потратили немного времени на байты, но это суть решения, поэтому Я думаю, это того стоит.
Затем мы сдвигаем поле вправо на входное значение. * Наконец, мы маскировать все , кроме бита низшего порядка, и становится нашим Логическое результат.
Однако, поскольку вы не можете сдвинуться больше, чем на количество битов, фактически находящихся в значении, это работает только для входов от 0 до 63 Чтобы поддерживать более высокие входные значения, мы вставляем тест в верхней части функции, которая ветвится в нижней части входного значения:> = 64. Единственное, что интересно в этом, это то, что мы предварительно загружаем константу битового поля в RAX
, а затем выполняем переход вплоть до инструкции, которая маскирует бит самого младшего порядка, гарантируя, что мы всегда возвращаем 1.
Попробуйте онлайн!
(Вызов функции C там помечен атрибутом, который заставляет GCC вызывать его, используя соглашение о вызовах Microsoft, которое использует мой ассемблерный код. Если бы TIO предоставил MSVC, в этом не было бы необходимости.)
__
* В качестве альтернативы сдвигу мы могли бы использовать BT
инструкцию x86 , но для кодирования это на 1 байт больше, так что никаких преимуществ. Если только мы не были вынуждены использовать другое соглашение о вызовах, в котором не было удобно передавать входное значение в ECX
регистр. Это было бы проблемой, потому что SHR
требует, чтобы его исходный операнд был CL
для счетчика динамического сдвига. Поэтому другое соглашение о вызовах потребовало бы, чтобы мы MOV
редактировали входное значение из любого регистра, в который оно было передано ECX
, что стоило бы нам 2 байта. BT
Команда может использовать любой регистр в качестве операнда - источника, при стоимости только 1 байт. Так что в такой ситуации это было бы предпочтительнее.BT
заносит значение соответствующего бита в флаг переноса (CF), так что вы бы использоватьSETC
инструкция для получения этого значения в целочисленном регистре, например, AL
чтобы оно могло быть возвращено вызывающей стороне.
Альтернативная реализация, 23 байта
Вот альтернативная реализация, которая использует операции по модулю и умножению, чтобы определить, является ли входное значение числом Чикен МакНуггет.
Он использует соглашение о вызовах System V AMD64 , которое передает входное значение в EDI
регистр. Результат все еще логический, возвращенный в EAX
.
Обратите внимание, что, в отличие от приведенного выше кода, это обратный логический тип (для удобства реализации). Он возвращает, false
если входное значение является числом Чикен МакНуггет, или true
если входное значение не является числом Чикен МакНуггет.
; bool IsNotMcNuggetNumber(int n)
; n is passed in EDI
8D 04 3F lea eax, [rdi+rdi*1] ; multiply input by 2, and put result in EAX
83 FF 2B cmp edi, 43
7D 0E jge TheEnd ; everything >= 43 is a McNugget number
99 cdq ; zero EDX in only 1 byte
6A 03 push 3
59 pop rcx ; short way to put 3 in ECX for DIV
F7 F1 div ecx ; divide input value by 3
6B D2 14 imul edx, edx, 20 ; multiply remainder of division by 20
39 D7 cmp edi, edx
0F 9C C0 setl al ; AL = (original input) < (input % 3 * 20)
TheEnd:
C3 ret
Уродливым в этом является необходимость явной обработки входных значений> = 43 с помощью функции сравнения и ветвления вверху. Очевидно, есть другие способы сделать это, которые не требуют ветвления, например алгоритм caird coinheringaahing , но для кодирования потребуется намного больше байтов, так что это не является разумным решением. Я полагаю, что мне, вероятно, не хватает некоторого хитрого трюка, который сделал бы эту работу более элегантной и занимал бы меньше байтов, чем решение на основе битового поля выше (поскольку кодирование самого битового поля занимает так много байтов), но я изучил это для некоторое время и до сих пор не вижу этого.
Ну ладно, попробуйте в любом случае онлайн !