C ++: разница двух 64-разрядных целых чисел без знака в 64-разрядных целых числах со знаком - PullRequest
4 голосов
/ 17 января 2012

Я пытаюсь написать функцию на C ++, которая принимает два 64-разрядных целых числа без знака и возвращает их разницу в 64-разрядном целом числе со знаком.Это кажется немного сложным из-за ситуации переполнения - поскольку входные данные представляют собой два беззнаковых положительных целых числа, если абсолютная разница между этими двумя больше максимального значения со знаком (INT64_MAX), то разница не может быть передана через целое число со знаком.Итак, я написал следующую реализацию, и мне было интересно, во-первых, если это правильно функционально, и во-вторых, есть ли более простая реализация.Любые предложения будут ценны.Спасибо!(Я собираюсь заменить assert исключением, это только сейчас!)

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

Ответы [ 4 ]

5 голосов
/ 17 января 2012

Мне кажется, это более простая и читаемая реализация.

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;
}
4 голосов
/ 17 января 2012

Это короче и, вероятно, быстрее.

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

Хороший оптимизирующий компилятор должен заметить, что diff < 0 - это отрицательный флаг, а first < second - это флаг переноса из предыдущего выражения.Сравнение этих двух флагов является классическим тестом на переполнение.

Даже если он этого не обнаруживает, требуется меньше операций.

Но самая большая причина, по которой я предпочитаю это, заключается в том, что нет магических чисел .

4 голосов
/ 17 января 2012

Три щупальца:

  • sizeof(abs_diff)*8 - 1 может быть заменено литералом 63 без потери переносимости (фактически, он будет более переносимым из-за платформ, где char не имеет ширины 8 бит)
  • & 1 не требуется, поскольку результат сдвига всегда равен одному биту
  • Вы можете получить diff из abs_diff без повторения вычитания.

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

2 голосов
/ 17 января 2012

Как насчет этого:

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