x86-64マシンコード、24バイト
6A 0A 5E 31 C9 89 F8 99 F7 F6 01 D1 85 C0 75 F7 8D 04 09 99 F7 F7 92 C3
上記のコードは、入力値がその数字の合計の2倍で割り切れるかどうかを決定する64ビットx86マシンコードの関数を定義しています。この関数はSystem V AMD64呼び出し規約に準拠しているため、C関数であるかのように、事実上すべての言語から呼び出すことができます。
EDI
テストする整数である呼び出し規約に従って、レジスタを介した入力として単一のパラメータを取ります。(これはチャレンジルールと一致する正の整数であると想定され、CDQ
使用する命令が正しく機能するために必要です。)
EAX
呼び出し規約に従って、再び結果をレジスタに返します。入力値がその数字の合計で割り切れる場合、結果は0になり、そうでない場合はゼロ以外になります。(基本的に、チャレンジルールで与えられた例とまったく同じ逆ブール。)
そのCプロトタイプは次のようになります。
int DivisibleByDoubleSumOfDigits(int value);
以下に、各命令の目的の簡単な説明が注釈されていない、アセンブリされていないアセンブリ言語の命令を示します。
; EDI == input value
DivisibleByDoubleSumOfDigits:
push 10
pop rsi ; ESI <= 10
xor ecx, ecx ; ECX <= 0
mov eax, edi ; EAX <= EDI (make copy of input)
SumDigits:
cdq ; EDX <= 0
div esi ; EDX:EAX / 10
add ecx, edx ; ECX += remainder (EDX)
test eax, eax
jnz SumDigits ; loop while EAX != 0
lea eax, [rcx+rcx] ; EAX <= (ECX * 2)
cdq ; EDX <= 0
div edi ; EDX:EAX / input
xchg edx, eax ; put remainder (EDX) in EAX
ret ; return, with result in EAX
最初のブロックでは、レジスタの予備的な初期化を行います。
PUSH
+ POP
命令はESI
、10 に初期化する低速で短い方法として使用されます。これはDIV
、x86 の命令にレジスタオペランドが必要なため必要です。(たとえば、10の即値で除算する形式はありません。)
XOR
は、ECX
レジスタをクリアするための短くて速い方法として使用されます。このレジスタは、今後のループ内で「アキュムレーター」として機能します。
- 最後に、(
EDI
)からの入力値のコピーが作成されEAX
、に保存されます。これは、ループを通過するときに上書きされます。
次に、ループを開始し、入力値の数字を合計します。これは、x86 DIV
命令に基づいています。x86 命令は、EDX:EAX
オペランドで除算し、で商EAX
と剰余を返しますEDX
。ここでは、入力値を10で除算して、残りが最後の桁(アキュムレータレジスタに追加するECX
)で、商が残りの桁になるようにします。
- この
CDQ
命令は、EDX
0に設定する短い方法です。実際には、EAX
to の値を符号拡張します。EDX:EAX
これはDIV
、被除数として使用されます。入力値は符号なしであるため、ここでは実際に符号拡張は必要ありませんが、clear CDQ
を使用XOR
するのではなく、1バイトであるためEDX
、2バイトになります。
- その後、我々は
DIV
、IDE EDX:EAX
によってESI
(10)。
- 残り(
EDX
)はアキュムレータ(ECX
)に追加されます。
EAX
レジスタ(商)がもしそうなら、それは0に等しいかどうかを確認するためにテストされ、我々はすべての桁を通してそれを作っていると我々はフォールスルー。そうでない場合、合計する桁がまだあるので、ループの先頭に戻ります。
最後に、ループが終了した後、実装しますnumber % ((sum_of_digits)*2)
。
このLEA
命令はECX
、2 を掛ける(または同等ECX
にそれ自体に加算する)ための短い方法として使用され、結果を別のレジスターに格納します(この場合はEAX
)。
(add ecx, ecx
+ を実行することもできますがxchg ecx, eax
、両方とも3バイトですが、LEA
命令はより高速でより一般的です。)
- それから、
CDQ
分割の準備をするためにもう一度やります。のでEAX
(すなわち、符号なし)正となり、これがゼロの効果があるEDX
だけで以前のように、。
- 次は除算で、今度
EDX:EAX
は入力値で割ったものです(その中にあるのは、まだ残っているのですEDI
)。これはモジュロと同等で、残りはになりEDX
ます。(商もに入れられますがEAX
、必要ありません。)
- 最後に、我々
XCHG
(為替)の内容EAX
とEDX
。通常、MOV
ここで実行しますが、XCHG
1バイトしかありません(低速ですが)。EDX
除算後の余りが含まれるため、値が均等に割り切れる場合は0になり、そうでない場合はゼロ以外になります。したがって、RET
urnでEAX
、入力値がその桁の合計の2倍で割り切れる場合、(結果)は0になり、そうでない場合は0以外になります。
うまくいけば説明で十分です。
これは最短のエントリではありませんが、ゴルフ以外のほとんどすべての言語に勝るもののようです。:-)