Сегодня утром я думал о том, что будет самым быстрым способом изменить число положительного на отрицательное и с отрицательного на положительное, конечно, самый простой способ может быть:
int a = 10;
a = a*(-1);
или же
int a = 10;
a = -a;
Но потом, я подумал, я так и сделаю, используя команды shift и указатели …
Что, действительно, можно было бы изменить знак значения, используя операторы сдвига и память?
Первый производит:
.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
Второй производит:
.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
Тот же вывод! Никакой разницы в коде сборки не производится.
—————————РЕДАКТИРОВАТЬ, ОП ОТВЕТЫ, ИСПОЛЬЗУЕТ VC ++ 2012, INTEL ARCH ——————-
Скомпилировано с использованием 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
и со вторым подходом (а = а * -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
являются точно такими же и будут испускать любой ассм, который они решат, будет наиболее эффективным на целевом процессоре, независимо от того, как вы его напишите. (например. Godbolt проводник компилятора для x86 gcc / MSVC / clang и ARM gcc.)
= -a
и 3 для *= -1
на последних процессорах Intel, используя фактический imul
инструкция.
Однако есть практическое преимущество к *= -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;
И как только человек принял идиому, он склонен придерживаться ее в других ситуациях.
Предполагая, что процессор, по крайней мере, несколько компетентен и имеет sizeof(int) == sizeof(Cpu_register)
тогда «сделать это число отрицательным» будет одна инструкция (обычно называется neg
) [ну, может понадобиться и загрузка и сохранение значения, но если вы используете переменную для чего-то еще, она может остаться после загрузки и сохраниться только после одной …]
Умножение на -1
скорее всего медленнее чем a = -a;
, но большинство компетентных компиляторов должны быть в состоянии сделать оба этих эквивалента.
Итак, просто напишите код четко, а остальное должно позаботиться о себе. Отрицание числа не является сложной операцией в большинстве процессоров. Если вы используете какой-то необычный обработчик, посмотрите на вывод компилятора и посмотрите, что он делает.
Также 0 — n
Gcc выдает команду «neg» для всех четырех случаев: -n, 0 — n, n * -1 и ~ n + 1
Решение с использованием языка высокого уровня
Подобные вопросы популярны в интервью и в мире конкурентного программирования.
Я приземлился здесь, исследуя больше решений для отрицания числа без использования оператора — или +.
За это :
> int addNumbers(int x, int y) > { > if(y==0) return x; // carry is 0 > return addNumbers(x^y,(x&y)<<1); > }
Здесь x ^ y выполняет сложение битов и x&у ручки несут операцию
Ты можешь попробовать
int a = 10;
a= ~a+1;
но вы не должны беспокоиться об этом, потому что компилятор делает это наилучшим образом.