私は今朝、ここで、多くのポジティブからネガティブへ、そしてネガティブからポジティブへと逆転する最も速い方法は何であるかを考えていました。もちろん、最も簡単な方法は次のとおりです。
int a = 10;
a = a*(-1);
または
int a = 10;
a = -a;
しかし、その後、私はそれをコマンドシフトとポインタを使用してこれを行うと考えました...コマンドシフト演算子とメモリを使用して、値の符号を変更することは本当に可能でしょうか?
最初の生成物:
.file "optimum.c"
.def ___main; .scl 2; .type 32; .endef
.text
.globl _main
.def _main; .scl 2; .type 32; .endef
_main:
pushl %ebp
movl %esp, %ebp
andl $-16, %esp
subl $16, %esp
call ___main
movl $10, 12(%esp) ;i = 10
negl 12(%esp) ;i = -i
movl $0, %eax
leave
ret
2番目のものは以下を生成します。
.file "optimum.c"
.def ___main; .scl 2; .type 32; .endef
.text
.globl _main
.def _main; .scl 2; .type 32; .endef
_main:
pushl %ebp
movl %esp, %ebp
andl $-16, %esp
subl $16, %esp
call ___main
movl $10, 12(%esp) ;i = 10
negl 12(%esp) ;i = -i
movl $0, %eax
leave
ret
同じ出力!生成されるアセンブリコードに違いはありません。
--------------------------編集、OPはVC++ 2012を使用していると答え、インテルアーチ----------- --------
cl optimum.c /Fa optimum.asm
を使用してコンパイル
; Listing generated by Microsoft (R) Optimizing Compiler Version 16.00.30319.01
TITLE C:\Users\Dell\Downloads\TTH\TTH\TTH\optimum.c
.686P
.XMM
include listing.inc
.model flat
INCLUDELIB LIBCMT
INCLUDELIB OLDNAMES
PUBLIC _main
; Function compile flags: /Odtp
_TEXT SEGMENT
_a$ = -4 ; size = 4
_argc$ = 8 ; size = 4
_argv$ = 12 ; size = 4
_main PROC
; File c:\users\Dell\downloads\tth\tth\tth\optimum.c
; Line 4
Push ebp
mov ebp, esp
Push ecx
; Line 5
mov DWORD PTR _a$[ebp], 10 ; 0000000aH
; Line 6
mov eax, DWORD PTR _a$[ebp]
neg eax ;1 machine cycle!
mov DWORD PTR _a$[ebp], eax
; Line 7
xor eax, eax
; Line 8
mov esp, ebp
pop ebp
ret 0
_main ENDP
_TEXT ENDS
END
そして、2番目のアプローチ(a = a * -1)
; Listing generated by Microsoft (R) Optimizing Compiler Version 16.00.30319.01
TITLE C:\Users\Dell\Downloads\TTH\TTH\TTH\optimum.c
.686P
.XMM
include listing.inc
.model flat
INCLUDELIB LIBCMT
INCLUDELIB OLDNAMES
PUBLIC _main
; Function compile flags: /Odtp
_TEXT SEGMENT
_a$ = -4 ; size = 4
_argc$ = 8 ; size = 4
_argv$ = 12 ; size = 4
_main PROC
; File c:\users\Dell\downloads\tth\tth\tth\optimum.c
; Line 4
Push ebp
mov ebp, esp
Push ecx
; Line 5
mov DWORD PTR _a$[ebp], 10 ; 0000000aH
; Line 6
mov eax, DWORD PTR _a$[ebp]
imul eax, -1 ;1 instruction, 3 machine/cycles :|
mov DWORD PTR _a$[ebp], eax
; Line 7
xor eax, eax
; Line 8
mov esp, ebp
pop ebp
ret 0
_main ENDP
_TEXT ENDS
END
次のような読みやすいものを使用してください
a *= -1;
または
a = -a;
残りはオプティマイザーに任せます。
他の答えは、読みやすさがより重要であることを正しく示しています。
a = -a
_と_a *= -1
_がまったく同じであり、どのように記述するかに関係なく、ターゲットCPUで最も効率的であると判断したasmを放出します。 (例 Godbolt compiler Explorer x86 gcc/MSVC/clangの場合、ARM gcc。)imul
命令を使用して、最近のIntel CPUでは_= -a
_に1サイクル、_*= -1
_に3サイクルかかります。
ただし、実用的な利点には_*= -1
_イディオムがあります。左側を1回書くだけで、1回だけ評価されます–そして読者は一度読むだけです!これは、LHSが長く、複雑で、高価である場合、または副作用がある可能性がある場合に関連します。
_(valid ? a : b)[prime_after(i++)] *= -1;
*look_up (input) *= -1; // Where look_up may have side-effects
parity[state][(unsigned int)getc(stdin)] *= -1;
variable_with_a_long_explanatory_name *= -1;
_
そして、イディオムを採用すると、他の状況でもイディオムを使い続ける傾向があります。
また、0-n
Gccは、4つの場合すべてに対して「neg」命令を発行します。-n、0-n、n * -1、および〜n + 1
プロセッサが少なくともある程度能力があり、sizeof(int) == sizeof(Cpu_register)
を持っていると仮定すると、「この番号を負にする」は単一の命令(通常neg
と呼ばれます)[まあ、値のロードと格納することもできますが、変数を他の目的に使用している場合は、ロード後も保持でき、後でのみ格納できます...]
-1
による乗算は、おそらくa = -a;
よりも遅くなりますが、ほとんどの有能なコンパイラーは、これらの両方を同等にできるはずです。
したがって、コードを明確に記述するだけで、残りは自分で処理する必要があります。数字を無効にすることは、ほとんどのプロセッサで難しい操作ではありません。異常なプロセッサを使用している場合は、コンパイラの出力を見て、それが何をするのかを確認してください。
高水準言語を使用したソリューション
このような質問は、インタビューや競合プログラミングの世界で人気があります。
ここに着いたのは、-または+演算子を使用せずに数値を否定するためのより多くの解決策を研究することです。
このため :
> int addNumbers(int x, int y) > { > if(y==0) return x; // carry is 0 > return addNumbers(x^y,(x&y)<<1); > }
ここで、x ^ yはビットの加算を実行し、x&yはキャリー演算を処理します