Trocar dois valores de variáveis ​​sem usar a terceira variável

104

Uma das perguntas mais complicadas feitas em uma entrevista.

Troque os valores de duas variáveis ​​como a=10e b=15.

Geralmente, para trocar dois valores de variáveis, precisamos da terceira variável como:

temp=a;
a=b;
b=temp;

Agora, o requisito é trocar os valores de duas variáveis ​​sem usar a terceira variável.

Muhammad Akhtar
fonte
76
Uau. Uma pergunta da entrevista mal escolhida IMHO. Esta é uma técnica raramente, ou nunca, útil na prática. Há uma boa chance de que isso apenas confunda o otimizador do compilador, resultando em um código menos eficiente do que uma "troca temporária". A menos que este lugar em que você estava entrevistando se envolva em coisas muito pesadas em matemática (pense: desenvolvimento de algoritmo de criptografia ou algo parecido), não consigo imaginar nenhum bom motivo para fazer essa pergunta.
Dan Molding
10
Na verdade, essa pergunta não diz nada além de se o candidato conhece esse truque específico que é praticamente inútil no código de produção. Suponho que você possa encontrar um bruxo ocasional que descobre isso na hora, mas pessoas assim, que ainda não conhecem o truque, são muito raras.
CEO
2
Será que eles querem eliminar as pessoas que pensam que conhecer esses truques é o que torna um bom programador? Além disso, ao ler sobre o truque xor, preste atenção para quando ele falhará (o que o IMO o torna completamente inútil para troca de inteiros de propósito geral).
UncleBens

Respostas:

155

Usando o algoritmo de troca xor

void xorSwap (int* x, int* y) {
    if (x != y) { //ensure that memory locations are different
       *x ^= *y;
       *y ^= *x;
       *x ^= *y;
    }
}


Por que o teste?

O teste é para garantir que xey tenham localizações de memória diferentes (em vez de valores diferentes). Isso ocorre porque, (p xor p) = 0e se xey compartilham o mesmo local de memória, quando um é definido como 0, ambos são definidos como 0. Quando * x e * y são 0, todas as outras operações xor em * x e * y serão iguais 0 (porque são iguais), o que significa que a função definirá * x e * y como 0.

Se eles tiverem os mesmos valores, mas não o mesmo local de memória, tudo funcionará conforme o esperado

*x = 0011
*y = 0011
//Note, x and y do not share an address. x != y

*x = *x xor *y  //*x = 0011 xor 0011
//So *x is 0000

*y = *x xor *y  //*y = 0000 xor 0011
//So *y is 0011

*x = *x xor *y  //*x = 0000 xor 0011
//So *x is 0011


Isso deve ser usado?

Em casos gerais, não. O compilador otimizará a variável temporária e, dado que a troca é um procedimento comum, ele deve produzir o código de máquina ideal para sua plataforma.

Veja, por exemplo, este programa de teste rápido escrito em C.

#include <stdlib.h>
#include <math.h>

#define USE_XOR 

void xorSwap(int* x, int *y){
    if ( x != y ){
        *x ^= *y;
        *y ^= *x;
        *x ^= *y;
    }
}

void tempSwap(int* x, int* y){
    int t;
    t = *y;
    *y = *x;
    *x = t;
}


int main(int argc, char* argv[]){
    int x = 4;
    int y = 5;
    int z = pow(2,28); 
    while ( z-- ){
#       ifdef USE_XOR
            xorSwap(&x,&y);
#       else
            tempSwap(&x, &y);
#       endif
    }
    return x + y;    
}

Compilado usando:

gcc -Os main.c -o swap

A versão xor leva

real    0m2.068s
user    0m2.048s
sys  0m0.000s

Onde a versão com a variável temporária leva:

real    0m0.543s
user    0m0.540s
sys  0m0.000s
Yacoby
fonte
1
Pode valer a pena explicar o porquê do teste (ou seja, que a abordagem triplo xor falha terrivelmente se xey referirem o mesmo objeto).
dmckee --- ex-moderador gatinho
1
Não sei como isso conseguiu tantos votos positivos quando o código está tão quebrado. Ambas as trocas no segmento de código testado estão completamente incorretas. Olhe de perto.
SoapBox
@SoapBox: Muito bom ponto! A troca XOR zeros x & deixa y intocado, e a troca temporária deixa ambos com o valor de x. Opa!
Drew Hall,
4
@SoapBox Good catch. Corrigido e testado novamente e não consigo nenhuma grande diferença nos tempos.
Yacoby,
4
O questionador disse duas variáveis, não int. : P
Plumenator
93

a forma geral é:

A = A operation B
B = A inverse-operation B
A = A inverse-operation B 

no entanto, você deve potencialmente estar atento a estouros e também nem todas as operações têm um inverso bem definido para todos os valores em que a operação está definida. por exemplo, * e / trabalhar até que A ou B seja 0

xor é particularmente agradável, pois é definido para todos os ints e é seu próprio inverso

jk.
fonte
XOR (X, X) == 0, então xor funciona para ints EXCETO quando os dois valores sendo trocados são iguais. Não sou o primeiro a apontar isso e muitos já o disseram (aqui e em outros lugares) para destacar alguém para receber o crédito.
AlanK
85
a = a + b
b = a - b // b = a
a = a - b
Dor
fonte
16
E se a+btransbordar?
Alok Singhal
2
@Alok: Isso não é levado em consideração, portanto, é impraticável :)
Dor
4
Se a e b são os mesmos, tipos inteiros porte básicos (como int, unsigned short...), ele ainda funciona no final, mesmo com excesso, porque se a + b transborda, em seguida, a - b será estouro negativo. Com esses tipos de inteiros básicos, os valores simplesmente rolam.
Patrick Johnmeyer,
10
Em C, usar isso em unsignedtipos inteiros está OK e sempre funciona. Em tipos assinados, ele invocaria um comportamento indefinido no estouro.
jpalecek,
1
@Shahbaz: Mesmo que inteiros com sinal sejam armazenados em complemento de 2, o comportamento no estouro ainda é indefinido.
Keith Thompson
79

Ninguém sugeriu usar std::swap, ainda.

std::swap(a, b);

Eu não uso nenhuma variável temporária e dependendo do tipo de ae bda implementação pode haver uma especalização que também não. A implementação deve ser escrita sabendo se um 'truque' é apropriado ou não. Não adianta tentar adivinhar.

De forma mais geral, provavelmente gostaria de fazer algo assim, pois funcionaria para tipos de classe, permitindo que o ADL encontrasse uma sobrecarga melhor, se possível.

using std::swap;
swap(a, b);

Claro, a reação do entrevistador a essa resposta pode dizer muito sobre a vaga.

CB Bailey
fonte
é claro que no C ++ 0x swap usará referências rvalue, então será ainda melhor!
jk.
16
Todo mundo quer mostrar o xor brilhante ou outro truque que aprenderam, com várias advertências sobre como usá-lo, mas essa é, sem dúvida, a melhor resposta.
2
por que não std :: swap (a, b); ? Quero dizer, por que usar?
Dinaiz,
2
@Dinaiz sem o std::, definido pelo usuárioswap implementações pelo usuário para tipos definidos pelo usuário também estão disponíveis para chamada (isso é o que significa "funcionaria para tipos de classe, permitindo que o ADL encontre uma sobrecarga melhor, se possível").
Kyle Strand
std::swap usa memória temporária adicional em sua implementação não? cplusplus.com/reference/utility/swap
Ninja
19

Como já notado por manu, o algoritmo XOR é um algoritmo popular que funciona para todos os valores inteiros (isso inclui ponteiros então, com alguma sorte e fundição). Para ser mais completo, gostaria de mencionar outro algoritmo menos poderoso com adição / subtração:

A = A + B
B = A - B
A = A - B

Aqui, você deve ter cuidado com overflows / underflows, mas, caso contrário, funciona perfeitamente. Você pode até tentar isso em floats / doubles no caso de XOR não ser permitido neles.

Vilx-
fonte
" todos os valores inteiros (isso inclui ponteiros então) " - O quê? Não, os ponteiros não são inteiros e você não pode xor valores de ponteiro. Você também não pode controlar valores de ponto flutuante. Mas o método de adição / subtração tem comportamento indefinido (para tipos com sinal ou ponto flutuante) em estouro ou underflow, pode perder a precisão para ponto flutuante e não pode ser aplicado a ponteiros ou outros tipos não numéricos.
Keith Thompson
@KeithThompson - OK, a perda de precisão para pontos flutuantes é verdadeira, mas dependendo das circunstâncias, pode ser aceitável. Quanto aos ponteiros - bem, nunca ouvi falar de um compilador / plataforma onde os ponteiros não pudessem ser convertidos livremente em inteiros e vice-versa (tomando cuidado para escolher um inteiro do tamanho certo, é claro). Mas eu não sou um especialista em C / C ++. Sei que isso vai contra o padrão, mas tive a impressão de que esse comportamento é bastante consistente em ... tudo.
Vilx-
1
@ Vilx-: Por que a perda de precisão seria aceitável quando é tão fácil evitá-la usando uma variável temporária - ou std::swap()? Claro, você pode converter ponteiros em inteiros e vice-versa (assumindo que haja um tipo inteiro grande o suficiente, o que não é garantido), mas não foi isso que você sugeriu; você sugeriu que os ponteiros são inteiros, não que eles podem ser convertidos em inteiros. Sim, a resposta aceita não funcionará para indicadores. A resposta de Charles Bailey usando std::swapé realmente a única correta.
Keith Thompson
1
Em primeiro lugar, a questão era "como trocar sem usar uma terceira variável" e o motivo apresentado para isso foi "uma questão de entrevista". Acabei de dar uma outra resposta possível. Em segundo lugar, OK, minhas desculpas por sugerir que os ponteiros são inteiros. Atualizei a resposta para ser mais explícita e correta. Corrija-me se ainda estiver errado (ou atualize a resposta você mesmo). Terceiro, apaguei a parte sobre a resposta aceita. Ele não está usando conversões de ponteiro para inteiro em qualquer lugar e funciona corretamente (tanto quanto eu entendo).
Vilx-
@KeithThompson: A questão poderia ser emendada para ser sobre como std::swappode ser implementado sem usar uma terceira variável.
jxh
10

Perguntas estúpidas merecem respostas adequadas:

void sw2ap(int& a, int& b) {
  register int temp = a; // !
  a = b;
  b = temp;
}

O único bom uso da registerpalavra - chave.

MSalters
fonte
1
Um objeto declarado com registro de classe de armazenamento não é uma "variável"? Além disso, não estou convencido de que este seja um bom uso de registro, pois se seu compilador já não pode otimizar isso, então qual é o ponto de tentar, você deve aceitar qualquer lixo que obtiver ou escrever o assembly você mesmo ;-) Mas como você diz, uma pergunta duvidosa merece uma resposta duvidosa.
Steve Jessop
1
Quase todos os compiladores modernos ignoram a classe de armazenamento de registradores, pois eles têm uma ideia muito melhor do que você acessa com frequência.
CEO
6
Observe que esta resposta se destina a entrevistadores, não a compiladores. Ele tira vantagem principalmente do fato de que o tipo de entrevistador que faz esse tipo de pergunta não entende realmente C ++. Portanto, eles não podem rejeitar esta resposta. (e não há uma resposta padrão; ISO C ++ fala sobre objetos, não variáveis).
MSalters em
Ah, eu entendo você. Eu estava procurando no padrão C a menção de variável como um substantivo, e não apenas o significado de não const. Eu encontrei um no n1124, na seção sobre loops for que definem o escopo das "variáveis" declaradas no inicializador. Então percebi que a pergunta era C ++ e não me preocupei em pesquisar para ver se C ++ tinha cometido o mesmo erro de digitação em algum lugar.
Steve Jessop
3

Trocar dois números usando a terceira variável seja assim,

int temp;
int a=10;
int b=20;
temp = a;
a = b;
b = temp;
printf ("Value of a", %a);
printf ("Value of b", %b);

Trocar dois números sem usar a terceira variável

int a = 10;
int b = 20;
a = a+b;
b = a-b;
a = a-b;
printf ("value of a=", %a);
printf ("value of b=", %b);
Ashwini
fonte
2
#include<iostream.h>
#include<conio.h>
void main()
{
int a,b;
clrscr();
cout<<"\n==========Vikas==========";
cout<<"\n\nEnter the two no=:";
cin>>a>>b;
cout<<"\na"<<a<<"\nb"<<b;
a=a+b;
b=a-b;
a=a-b;

cout<<"\n\na="<<a<<"\nb="<<b;
getch();
}
vikas
fonte
1
Depois de cout << "Enter the two no=:"esperar para lercout << "Now enter the two no in reverse order:"
user253751
Tal como acontece com várias outras respostas, este tem um comportamento indefinido se a+bou a-bestoura Além disso, void main()é inválido e <conio.h>não é padrão.
Keith Thompson
2

Já que a solução original é:

temp = x; y = x; x = temp;

Você pode torná-lo um revestimento duplo usando:

temp = x; y = y + temp -(x=y);

Em seguida, transforme-o em um forro usando:

x = x + y -(y=x);
user3258202
fonte
1
Comportamento indefinido. yé lido e escrito na mesma expressão sem ponto de sequência intermediário.
Keith Thompson
1

Se você mudar um pouco a pergunta para perguntar sobre 2 registros de montagem em vez de variáveis, você pode usar também a xchgoperação como uma opção e a operação de pilha como outra.

rkellerm
fonte
1
A que xchgoperação você está se referindo? A questão não especifica uma arquitetura de CPU.
Keith Thompson
1

Considere a=10, b=15:

Usando adição e subtração

a = a + b //a=25
b = a - b //b=10
a = a - b //a=15

Usando divisão e multiplicação

a = a * b //a=150
b = a / b //b=10
a = a / b //a=15
Venkat
fonte
1
Possui comportamento indefinido em overflow.
Keith Thompson
1
Além disso, em C ++, pode haver um comportamento indesejável quando os valores não são do mesmo tipo. Por exemplo, a=10e b=1.5.
Matthew Cole
1
#include <iostream>
using namespace std;
int main(void)
{   
 int a,b;
 cout<<"Enter a integer" <<endl;
 cin>>a;
 cout<<"\n Enter b integer"<<endl;
 cin>>b;

  a = a^b;
  b = a^b;
  a = a^b;

  cout<<" a= "<<a <<"   b="<<b<<endl;
  return 0;
}

Atualização: neste, estamos pegando a entrada de dois inteiros do usuário. Então, estamos usando o XOR bit a bit para trocá-los.

Digamos que temos dois inteiros a=4e b=9em seguida:

a=a^b --> 13=4^9 
b=a^b --> 4=13^9 
a=a^b --> 9=13^9
Naeem Ul Hassan
fonte
Adicione uma breve explicação à sua resposta para futuros visitantes.
Nikolay Mihaylov
1
Neste, estamos pegando a entrada de dois inteiros do usuário. Então estamos usando a operação bit a bit xor dois trocá-los. Digamos que temos dois intergers a = 4 eb = 9, então agora a = a ^ b -> 13 = 4 ^ 9 b = a ^ b -> 4 = 13 ^ 9 a = a ^ b -> 9 = 13 ^ 9
Naeem Ul Hassan
1

Aqui está mais uma solução, mas um único risco.

código:

#include <iostream>
#include <conio.h>
void main()
{

int a =10 , b =45;
*(&a+1 ) = a;
a =b;
b =*(&a +1);
}

qualquer valor no local a + 1 será substituído.

Temerário
fonte
2
Foi uma resposta útil, talvez o valor tenha desaparecido.
Bibi Tahira
1
Vários tipos de comportamento indefinido. *(&a+1)pode muito bem ser b. void main()é inválido. <conio.h>não é padrão (e você nem mesmo o usa).
Keith Thompson
O código foi testado com outros parâmetros de saída, que foram excluídos, pois mencionei um risco. O risco da memória ser substituída .. Isso está aí .. Mas foi uma troca útil de duas variáveis ​​sem envolvimento da terceira.
DareDevil
A pior parte é que pode realmente funcionar algumas vezes, então você pode não perceber imediatamente que está completamente quebrado e irá falhar na otimização, em outros compiladores, etc.
avl_sweden
1

Claro, a resposta C ++ deve ser std::swap .

No entanto, também não há uma terceira variável na seguinte implementação de swap:

template <typename T>
void swap (T &a, T &b) {
    std::pair<T &, T &>(a, b) = std::make_pair(b, a);
}

Ou, como uma linha:

std::make_pair(std::ref(a), std::ref(b)) = std::make_pair(b, a);
jxh
fonte
0
#include <stdio.h>

int main()
{
    int a, b;
    printf("Enter A :");
    scanf("%d",&a);
    printf("Enter B :");
    scanf("%d",&b);
    a ^= b;
    b ^= a;
    a ^= b;
    printf("\nValue of A=%d B=%d ",a,b);
    return 1;
}
Siddiqui
fonte
0

esse é o algoritmo de troca XOR correto

void xorSwap (int* x, int* y) {
   if (x != y) { //ensure that memory locations are different
      if (*x != *y) { //ensure that values are different
         *x ^= *y;
         *y ^= *x;
         *x ^= *y;
      }
   }
}

você deve garantir que os locais de memória sejam diferentes e também que os valores reais sejam diferentes porque A XOR A = 0

Gianluca Ghettini
fonte
Você não precisa garantir que os valores sejam diferentes.
user253751
0

Você pode fazer .... de maneira fácil ... dentro de uma linha Lógica

#include <stdio.h>

int main()
{
    int a, b;
    printf("Enter A :");
    scanf("%d",&a);
    printf("Enter B :");
    scanf("%d",&b);
    int a = 1,b = 2;
    a=a^b^(b=a);
    printf("\nValue of A=%d B=%d ",a,b);

    return 1;
}

ou

#include <stdio.h>

int main()
{
    int a, b;
    printf("Enter A :");
    scanf("%d",&a);
    printf("Enter B :");
    scanf("%d",&b);
    int a = 1,b = 2;
    a=a+b-(b=a);
    printf("\nValue of A=%d B=%d ",a,b);

    return 1;
}
MOHAMMAD S HUSSAIN
fonte
1
Comportamento indefinido. Em ambas as versões, bé lido e modificado na mesma expressão, sem ponto de sequência intermediário.
Keith Thompson
0
public void swapnumber(int a,int b){
    a = a+b-(b=a);
    System.out.println("a = "+a +" b= "+b);
}
Ashish Bhavsar
fonte
0

A melhor resposta seria usar o XOR e usá-lo em uma linha seria legal.

    (x ^= y), (y ^= x), (x ^= y);

x, y são variáveis ​​e a vírgula entre elas introduz os pontos de sequência para que não se tornem dependentes do compilador. Felicidades!

Ankit Sharma
fonte
0

Vamos ver um exemplo simples de c para trocar dois números sem usar a terceira variável.

programa 1:

#include<stdio.h>
#include<conio.h>
main()
{
int a=10, b=20;
clrscr();
printf("Before swap a=%d b=%d",a,b);
a=a+b;//a=30 (10+20)
b=a-b;//b=10 (30-20)
a=a-b;//a=20 (30-10)
printf("\nAfter swap a=%d b=%d",a,b);
getch();
}

Resultado:

Antes da troca a = 10 b = 20 Após a troca a = 20 b = 10

Programa 2: Usando * e /

Vamos ver outro exemplo para trocar dois números usando * e /.

#include<stdio.h>
#include<conio.h>
main()
{
int a=10, b=20;
clrscr();
printf("Before swap a=%d b=%d",a,b);
a=a*b;//a=200 (10*20)
b=a/b;//b=10 (200/20)
a=a/b;//a=20 (200/10)
printf("\nAfter swap a=%d b=%d",a,b);
getch();
}

Resultado:

Antes da troca a = 10 b = 20 Após a troca a = 20 b = 10

Programa 3: Fazendo uso do operador XOR bit a bit:

O operador XOR bit a bit pode ser usado para trocar duas variáveis. O XOR de dois números x e y retorna um número que tem todos os bits como 1 sempre que os bits de x e y diferem. Por exemplo, XOR de 10 (In Binary 1010) e 5 (In Binary 0101) é 1111 e XOR de 7 (0111) e 5 (0101) é (0010).

#include <stdio.h>
int main()
{
 int x = 10, y = 5;
 // Code to swap 'x' (1010) and 'y' (0101)
 x = x ^ y;  // x now becomes 15 (1111)
 y = x ^ y;  // y becomes 10 (1010)
 x = x ^ y;  // x becomes 5 (0101)
 printf("After Swapping: x = %d, y = %d", x, y);
 return 0;

Resultado:

Após a troca: x = 5, y = 10

Programa 4:

Ninguém sugeriu usar std :: swap, ainda.

std::swap(a, b);

Eu não uso nenhuma variável temporária e dependendo do tipo de aeb, a implementação pode ter uma especialização que também não. A implementação deve ser escrita sabendo se um 'truque' é apropriado ou não.

Problemas com os métodos acima:

1) A abordagem baseada na multiplicação e divisão não funciona se um dos números for 0, já que o produto se torna 0 independentemente do outro número.

2) Ambas as soluções aritméticas podem causar estouro aritmético. Se x e y forem muito grandes, a adição e a multiplicação podem sair do intervalo inteiro.

3) Quando usamos ponteiros para uma variável e fazemos uma troca de função, todos os métodos acima falham quando ambos os ponteiros apontam para a mesma variável. Vamos dar uma olhada no que acontecerá neste caso se ambos estiverem apontando para a mesma variável.

// Método baseado em XOR bit a bit

x = x ^ x; // x becomes 0
x = x ^ x; // x remains 0
x = x ^ x; // x remains 0

// Método baseado em aritmética

x = x + x; // x becomes 2x
x = x  x; // x becomes 0
x = x  x; // x remains 0

Vamos ver o seguinte programa.

#include <stdio.h>
void swap(int *xp, int *yp)
{
    *xp = *xp ^ *yp;
    *yp = *xp ^ *yp;
    *xp = *xp ^ *yp;
}

int main()
{
  int x = 10;
  swap(&x, &x);
  printf("After swap(&x, &x): x = %d", x);
  return 0;
}

Resultado :

Após a troca (& x, & x): x = 0

A troca de uma variável com ela mesma pode ser necessária em muitos algoritmos padrão. Por exemplo, veja esta implementação do QuickSort onde podemos trocar uma variável com ele mesmo. O problema acima pode ser evitado colocando uma condição antes da troca.

#include <stdio.h>
void swap(int *xp, int *yp)
{
    if (xp == yp) // Check if the two addresses are same
      return;
    *xp = *xp + *yp;
    *yp = *xp - *yp;
    *xp = *xp - *yp;
}
int main()
{
  int x = 10;
  swap(&x, &x);
  printf("After swap(&x, &x): x = %d", x);
  return 0;
}

Produto :

Após a troca (& x, & x): x = 10

shobhit2905
fonte
0

Talvez fora do assunto, mas se você sabe o que está trocando uma única variável entre dois valores diferentes, pode ser capaz de fazer lógica de array. Cada vez que essa linha de código é executada, ela troca o valor entre 1 e 2.

n = [2, 1][n - 1]
quemeful
fonte
0

Você poderia fazer:

std::tie(x, y) = std::make_pair(y, x);

Ou use make_tuple ao trocar mais de duas variáveis:

std::tie(x, y, z) = std::make_tuple(y, z, x);

Mas não tenho certeza se internamente std :: tie usa uma variável temporária ou não!

Pooya13
fonte
0

Em javascript:

function swapInPlace(obj) {
    obj.x ^= obj.y
    obj.y ^= obj.x
    obj.x ^= obj.y
}

function swap(obj) {
    let temp = obj.x
    obj.x = obj.y
    obj.y = temp
}

Esteja ciente do tempo de execução de ambas as opções.

Ao executar este código, eu o medi.

console.time('swapInPlace')
swapInPlace({x:1, y:2})
console.timeEnd('swapInPlace') // swapInPlace: 0.056884765625ms

console.time('swap')
swap({x:3, y:6})
console.timeEnd('swap')        // swap: 0.01416015625ms

Como você pode ver (e como muitos disseram), a troca no lugar (xor) leva muito tempo do que a outra opção que usa a variável temp.

ofir_aghai
fonte
0

R está faltando uma atribuição concorrente, conforme proposto por Edsger W. Dijkstra em A Discipline of Programming , 1976, ch.4, p.29. Isso permitiria uma solução elegante:

a, b    <- b, a         # swap
a, b, c <- c, a, b      # rotate right
user3604103
fonte
-1
a = a + b - (b=a);

É muito simples, mas pode gerar um alerta.

Alsimoneau
fonte
5
O aviso é que não funciona? Não sabemos se b=aé realizado antes ou depois a + b.
Bo Persson de
-1

solução de linha única para trocar dois valores na linguagem c.

a=(b=(a=a+b,a-b),a-b);
HARITUSH
fonte
Comportamento indefinido no estouro.
Keith Thompson
-1
second_value -= first_value;
first_value +=  second_value;
second_value -= first_value;
second_value *= -1;
Zikria Qureshi
fonte
Isso tem um comportamento indefinido se qualquer uma das operações estourar ou estourar. Ele também pode perder a precisão se os objetos forem de ponto flutuante.
Keith Thompson