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机器代码中定义了一个函数,该函数确定输入值是否为Chicken McNugget数字。ECX
遵循Windows上使用的Microsoft 64位调用约定,将单个正整数参数传递到寄存器中。结果是在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
显然,这与Anders Kaseorg在Python中的解决方案大不相同,因为它是基于一个位域的,该位域表示的是Chicken McNugget数字。具体而言,该字段中与有效的McNugget小鸡编号相对应的每个位都设置为1;所有其他位都设置为0。(这将0视为有效的Chicken McNugget数字,但如果您不喜欢它,则可以选择单个位进行修改。)
我们首先简单地将此值加载到寄存器中。它是一个64位的值,已经需要8个字节进行编码,而且我们需要一个1字节的REX.W前缀,因此在字节方面我们确实非常不省钱,但这是解决方案的核心,因此我想这是值得的。
然后,我们将字段右移输入值。*最后,我们屏蔽掉除了最低位以外的所有位,这成为我们的布尔结果。
但是,由于移位的位数不能超过该值的实际位数,因此仅适用于0-63之间的输入。为了支持更高的输入值,我们在该函数的顶部插入一个测试,该测试的顶部分支到输入值的底部是> =64。对此唯一有趣的是,我们在中预加载了位字段常量RAX
,然后进行分支直到掩盖最低位的指令,从而确保我们总是返回1。
在线尝试!
(那里的C函数调用带有一个属性,该属性使GCC使用我的汇编代码使用的Microsoft调用约定对其进行调用。如果TIO提供了MSVC,则不需要这样做。)
__
*作为移位的替代方法,我们可以使用x86 BT
指令,但是编码要长1个字节,因此没有优势。除非我们被迫使用不同的调用约定,该约定不能方便地在ECX
寄存器中传递输入值。这将是一个问题,因为SHR
要求其源操作数CL
用于动态移位计数。因此,不同的调用约定将要求我们MOV
从传递给它的任何寄存器中编辑输入值ECX
,这将花费2个字节。该BT
指令可以将任何寄存器用作源操作数,而仅花费1个字节。因此,在那种情况下,这将是可取的。BT
将相应位的值放入进位标志(CF)中,因此您将使用一条SETC
指令在整数寄存器中获取该值,就像AL
可以将其返回给调用方一样。
替代实现,23字节
这是使用模和乘法运算来确定输入值是否为Chicken McNugget数的替代实现。
它使用System V AMD64调用约定,该约定将输入值传递到EDI
寄存器中。结果仍然是布尔值,在中返回EAX
。
但是请注意,与上面的代码不同,这是一个反向布尔值(为实现方便)。它返回false
如果输入值是一个鸡McNugget号,或者true
如果输入值是不是一个鸡McNugget数。
; 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。显然有这样做不需要分支,就像其他的方式凯尔德coinheringaahing的算法,但是这将需要很多多个字节编码,所以不是一个合理的解决方案。我认为我可能会缺少一些纠结技巧,比上面的基于位域的解决方案(由于编码位域本身需要那么多的字节),它使该工作更优雅并且字节更少(但是对它进行了研究)一会儿,仍然看不到它。
哦,还是可以在线尝试!