C ++: diferencia de dos enteros de 64 bits sin firmar en un entero de 64 bits firmado

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

  •  29-10-2019
  •  | 
  •  

Pregunta

Estoy tratando de escribir una función en C ++ que toma dos enteros sin firmar de 64 bits y devuelve su diferencia en un entero de 64 bits firmado. Parece ser un poco complicado debido a la situación del desbordamiento, dado que las entradas son dos enteros positivos sin firmar, si la diferencia absoluta entre estos dos es mayor que el valor firmado máximo (int64_max), entonces la diferencia no se puede transmitir a través de un entero firmado. Así que he escrito la siguiente implementación, y me preguntaba, primero, si esto es correcto funcionalmente, y segundo, hay una implementación más simple. Cualquier sugerencia sería muy apreciada. ¡Gracias! (Voy a reemplazar el afirmación con una excepción, ¡está ahí por ahora!)

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;
}
¿Fue útil?

Solución

Para mí, esto parece una implementación más simple y legible.

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;
}

Otros consejos

Tres nitpicks:

  • sizeof(abs_diff)*8 - 1 puede ser reemplazado por el literal 63 sin pérdida de portabilidad (de hecho, será más portátil debido a plataformas donde un char no tiene 8 bits de ancho)
  • & 1 no es necesario, ya que el resultado del cambio siempre es un bit
  • Puedes derivar diff de abs_diff sin repetir la resta.

De lo contrario, esto me parece perfectamente correcto.

Esto es más corto y probablemente más rápido.

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 buen compilador de optimización debe notar que diff < 0 es la bandera negativa y first < second es la bandera de transporte de la expresión anterior. Comparar estas dos banderas es la prueba clásica de desbordamiento.

Incluso si no detecta eso, se requieren menos operaciones.

Pero la razón más importante por la que prefiero esto es que No hay números mágicos.

Qué tal esto:

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;
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top