разница целого числа без знака - стандартный поддерживаемый способ получения результата со знаком? - PullRequest
11 голосов
/ 02 июля 2019

при условии двух произвольных временных отметок:

uint32_t timestamp1;    
uint32_t timestamp2;

Существует ли стандартный способ соответствия для получения разницы между двумя знаками, кроме очевидных вариантов преобразования в больший тип со знаком и довольно многословного if-else.

Заранее неизвестно, какой из них больше, но известно, что разница не превышает максимум 20 бит, поэтому он уместится в 32-битную подпись.

int32_t difference = (int32_t)( (int64_t)timestamp1 - (int64_t)timestamp2 );

Этот вариант имеетнедостаток в том, что использование 64-битной арифметики может не поддерживаться аппаратным обеспечением и, конечно, возможно только при наличии более крупного типа (что, если временная метка уже 64-битная).

Другая версия

int32_t difference;
if (timestamp1 > timestamp2) {
  difference =    (int32_t)(timestamp1 - timestamp2);
} else {
  difference = - ((int32_t)(timestamp2 - timestamp1));
}

является довольно многословным и включает условные переходы.

То есть с

int32_t difference = (int32_t)(timestamp1 - timestamp2);

Гарантируется ли это с точки зрения стандартов?

Ответы [ 2 ]

7 голосов
/ 02 июля 2019

Вы можете использовать каламбур типа union на основе

typedef union
{
    int32_t _signed;
    uint32_t _unsigned;
} u;

Выполните вычисление в unsigned арифметике, присвойте результат элементу _unsigned, затем прочитайте _signed член union как результат:

u result {._unsigned = timestamp1 - timestamp2};
result._signed; // yields the result

Это переносимо на любую платформу, которая реализует типы фиксированной ширины, на которые мы полагаемся (им это не нужно). Дополнение 2 гарантировано для подписанного члена, и на уровне «машины» подписанная арифметика дополнения 2 неотличима от арифметики без знака. Здесь нет преобразования или издержек типа memcpy: хороший компилятор скомпилирует то, что по сути является стандартным синтаксическим сахаром.

(Обратите внимание, что это неопределенное поведение в C ++.)

0 голосов
/ 02 июля 2019

Преобразование целого числа без знака в целое число со знаком - , определенная реализация .Это прописано в разделе 6.3.1.3 стандарта C относительно целочисленных преобразований:

1 Когда значение с целочисленным типом преобразуется в другое целое числотип, отличный от _Bool, если значение может быть представлено новым типом, оно не изменяется.

2 В противном случае, если новый тип не подписан, значение преобразуется путем многократного добавления иливычитая на единицу больше максимального значения, которое может быть представлено в новом типе, пока значение не окажется в диапазоне нового типа.60)

3 В противном случае новый тип подписывается и значение не может быть представлено в нем;либо результат определяется реализацией, либо генерируется определяемый реализацией сигнал.

На реализациях, которые люди чаще всего используют, преобразование будет происходить так, как вы ожидаете, то есть представлениезначение без знака будет переинтерпретировано как значение со знаком.

В частности GCC выполняет следующие действия:

  • Результат или сигнал, выданныйпреобразование целого числа в целочисленный тип со знаком, когда значение не может быть представлено в объекте этого типа (C90 6.2.1.2, C99 и C11 6.3.1.3).

Для преобразования в типширина N, значение уменьшается по модулю 2 ^ N, чтобы быть в пределах диапазона типа;сигнал не подается.

MSVC :

Когда длинное целое число приводится к короткому, или короткое - к символу,младшие значащие байты сохраняются.

Например, этой строке

short x = (short)0x12345678L;

присваивается значение 0x5678 x, а этой строке

char y = (char)0x1234;

присваивается значениеОт 0x34 до y.

Когда знаковые переменные преобразуются в беззнаковые и наоборот, битовые комбинации остаются неизменными.Например, приведение -2 (0xFE) к значению без знака дает 254 (также 0xFE).

Так что для этих реализаций то, что вы предложили, будет работать.

...