При преобразовании между различными базами / основами всегда работайте с целыми типами без знака.
Допустим, у вас есть long num
, который вы хотите преобразовать.Используйте unsigned long u
.Чтобы представить отрицательные значения в формате дополнения до двух, вы можете использовать
if (num < 0)
u = 1 + (~(unsigned long)(-num));
else
u = num;
или даже короче,
unsigned long u = (num < 0) ? 1 + (~(unsigned long)(-num)) : num;
Это работает на всех архитектурах (кроме num == LONG_MIN
, в этом случаевыше это технически неопределенное поведение), даже те, которые не используют внутреннее дополнение до двух , потому что мы по существу преобразуем абсолютное значение в num
.Если num
изначально был отрицательным, мы затем дополняем их до значения без знака.
В комментарии chux предложил альтернативную форму, которая не полагается на UB для num == LONG_MIN
(если только LONG_MAX == ULONG_MAX
, что было бы ужасно странно видеть):
unsigned long u = (num < 0) ? 1 + (~((unsigned long)(-1 - num) + 1)) : num;
Это может показаться "уродливым", но здравый компилятор C должен иметь возможность полностью оптимизировать любой из них на архитектурах с двумя целыми числами дополнения.Версия chux позволяет избежать неопределенного поведения, вычитая отрицательное значение num
из -1
, отображая таким образом -1
в 0
, -2
в 1
и т. д., гарантируя, что все отрицательные значения представимы как неотрицательные long
.Это значение затем преобразуется в unsigned long
.Это увеличивается на единицу, чтобы учесть более ранние значения -1
.Эта процедура дает правильное отрицание num
.
Другими словами, чтобы получить абсолютное значение long
, вы можете использовать
unsigned long abs_long(const long num)
{
return (num < 0) ? (unsigned long)(-1 - num) + 1u : (unsigned long)num;
}