C ++: differenza di due interi non firmati a 64 bit in un numero intero firmato a 64 bit

StackOverflow https://stackoverflow.com/questions/8886014

  •  29-10-2019
  •  | 
  •  

Domanda

Sto cercando di scrivere una funzione in C ++ che richiede due numeri interi senza segno a 64 bit e restituisce la loro differenza in un numero intero firmato a 64 bit. Sembra essere un po 'complicato a causa della situazione di overflow - poiché gli input sono due numeri interi positivi non firmati, se la differenza assoluta tra questi due è maggiore del valore firmato massimo (int64_max), la differenza non può essere trasmessa attraverso un numero intero firmato. Quindi ho scritto la seguente implementazione e mi chiedevo, in primo luogo, se questo è corretto funzionalmente e in secondo luogo, esiste un'implementazione più semplice. Ogni suggerimento sarà molto apprezzato. Grazie! (Sostituirò l'affermazione con un'eccezione, è proprio lì per ora!)

int64_t GetDifference(uint64_t first, uint64_t second) {
  uint64_t abs_diff = (first > second) ? (first - second): (second - first);    
  uint64_t msb_abs_diff = (abs_diff >> (sizeof(abs_diff)*8 - 1)) & 1;
  assert(msb_abs_diff == 0);
  int64_t diff = first - second;
  return diff;
}
È stato utile?

Soluzione

Per me questo sembra un'implementazione più semplice e leggibile.

int64_t GetDifference(uint64_t first, uint64_t second) {
    uint64_t abs_diff = (first > second) ? (first - second): (second - first);
    assert(abs_diff<=INT64_MAX);
    return (first > second) ? (int64_t)abs_diff : -(int64_t)abs_diff;
}

Altri suggerimenti

Tre Nit Picks:

  • sizeof(abs_diff)*8 - 1 può essere sostituito dal letterale 63 senza perdita di portabilità (in effetti, lo sarà Di più portatile a causa di piattaforme in cui a char non è largo 8 bit)
  • & 1 non è necessario, poiché il risultato del turno è sempre un bit
  • Puoi derivare diff da abs_diff senza ripetere la sottrazione.

Altrimenti, questo mi sembra perfettamente corretto.

Questo è più breve e probabilmente più veloce.

int64_t GetDifference(uint64_t first, uint64_t second)
{
  int64_t diff = first - second;
  bool overflowed = (diff < 0) ^ (first < second);
  assert(!overflowed);
  return diff;
}

Un buon compilatore ottimizzante dovrebbe notarlo diff < 0 è la bandiera negativa e first < second è la bandiera di trasporto dall'espressione precedente. Confrontare queste due bandiere è il test classico per Overflow.

Anche se non lo rileva, ci sono meno operazioni richieste.

Ma il motivo principale per cui preferisco questo è quello Non ci sono numeri magici.

Cosa ne pensi di questo:

int64_t GetDifference(uint64_t first, uint64_t second) {
    int64_t diff = (int64_t)(first - second);
    assert first >= second && diff >= 0 || first < second && diff < 0;
    return diff;
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top