Самый быстрый способ свести число
Я думал сегодня утром, что было бы самым быстрым способом обратить вспять число положительных в отрицательные и от отрицательного к положительному, конечно, самый простой способ мог бы быть:
int a = 10;
a = a*(-1);
или
int a = 10;
a = -a;
Но потом, подумал я, я беру это для этого, используя команды shift и указатели...
Действительно ли можно было бы изменить знак значения, используя команды смены операторов и память?
Ответы
Ответ 1
Первое производит:
.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
Тот же выход! Никакой разницы в произведенном коде сборки.
-------------------------- EDIT, OP ОТВЕТЫ НА ИСПОЛЬЗОВАНИЕ VС++ 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
и со вторым подходом (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
Ответ 2
Используйте то, что читается, например
a *= -1;
или
a = -a;
Оставьте остальные для оптимизатора.
Ответ 3
В других ответах правильно указано, что читаемость имеет большее значение:
- Вы должны забыть о скорости и выбрать идиому, которую вы найдете наиболее читаемой.
- Почти все компиляторы (с включенными оптимизациями) понимают, что
a = -a
и a *= -1
являются точно такими же и будут излучать все, что они решат, будут наиболее эффективными на целевом ЦП, независимо от того, как вы его пишете. (например, Godbolt explorer для x86 gcc/MSVC/clang и ARM gcc.)
- Но хотя MSVS 2012 (только в режиме отладки) использует одну команду для каждого, они берут 1 цикл для
= -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;
И как только кто-то принял идиому, каждый имеет тенденцию придерживаться ее в других ситуациях.
Ответ 4
Предполагая, что процессор хотя бы несколько компетентен и имеет sizeof(int) == sizeof(Cpu_register)
, тогда "сделать это число отрицательным" будет одной инструкцией (обычно называемой neg
) [ну, может понадобиться загрузка и хранение значений, но если вы используете переменную для чего-либо еще, она может оставаться после загрузки и храниться только позже...]
Умножение на -1
наиболее вероятно медленнее, чем a = -a;
, но большинство компетентных компиляторов должны иметь возможность сделать оба этих эквивалента.
Итак, просто напишите код четко, а остальное должно позаботиться о себе. Отрицание числа не является сложной операцией в большинстве процессоров. Если вы используете какой-то необычный processsor, посмотрите на вывод компилятора и посмотрите, что он делает.
Ответ 5
Также 0 - n
Gcc испускает инструкцию "neg" для всех четырех случаев: -n, 0 - n, n * -1 и ~ n + 1
Ответ 6
Я приземлился здесь, исследуя большее решение для отрицания числа без использования - или + оператора.
Для этого:
- дополнять число с помощью оператора ~
- Затем добавьте 1 к числу, полученному на шаге 1, используя логику Half adder:
> 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 обрабатывает операцию переноса
Ответ 7
Вы можете попробовать
int a = 10;
a= ~a+1;
но вы не должны беспокоиться об этом, потому что компилятор делает это наилучшим образом.