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命令は、EDX0に設定する短い方法です。実際には、EAXto の値を符号拡張します。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ここで実行しますが、XCHG1バイトしかありません(低速ですが)。EDX除算後の余りが含まれるため、値が均等に割り切れる場合は0になり、そうでない場合はゼロ以外になります。したがって、RETurnでEAX、入力値がその桁の合計の2倍で割り切れる場合、(結果)は0になり、そうでない場合は0以外になります。
うまくいけば説明で十分です。
これは最短のエントリではありませんが、ゴルフ以外のほとんどすべての言語に勝るもののようです。:-)