Подтвердить что ты не робот

Самый быстрый способ свести число

Я думал сегодня утром, что было бы самым быстрым способом обратить вспять число положительных в отрицательные и от отрицательного к положительному, конечно, самый простой способ мог бы быть:

int a = 10;
a = a*(-1);

или

int a = 10;
a = -a;

Но потом, подумал я, я беру это для этого, используя команды shift и указатели... Действительно ли можно было бы изменить знак значения, используя команды смены операторов и память?

4b9b3361

Ответ 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;

но вы не должны беспокоиться об этом, потому что компилятор делает это наилучшим образом.