2013-07-30 2 views
2

Этот код кода пытается найти абсолютное значение отрицательного числа, но выход также отрицательный. Может ли кто-нибудь сказать мне, как преодолеть это?Проблема с абсолютным значением 64-битного целого

#include <stdio.h> 
#include <stdint.h> 
#include <stdlib.h> 
#include <inttypes.h> 

int main() { 
    int64_t a = 0x8000000000000000; 
    a = llabs(a); 
    printf("%" PRId64 "\n", a); 
    return 0; 
} 

Выход

-9223372036854775808 

UPDATE:

Спасибо за все ваши ответы. Я понимаю, что это нестандартное значение, и поэтому я не могу выполнить абсолютную операцию над ним. Тем не менее, я столкнулся с этим в реальной кодовой базе, которая представляет собой моделирование генетического программирования. «Организмы» в этом не знают о стандарте C и настаивают на генерировании этой ценности :) Может ли кто-нибудь сказать мне эффективный способ обойти это? Еще раз спасибо.

ответ

4

Если результат llabs() не может быть представлен в виде long long, тогда поведение не определено. Мы можем заключить, что это то, что здесь происходит - значение вне диапазона 0x8000000000000000 преобразуется в значение -9223372036854775808 при преобразовании в int64_t, а ваше значение long long имеет ширину 64 бит, поэтому значение 9223372036854775808 невозможно представить.

Для того, чтобы ваша программа имела определенное поведение, вы должны убедиться, что значение, переданное в llabs(), составляет не менее -LLONG_MAX. То, как вы это делаете, зависит от вас - либо модифицируйте «организмы», чтобы они не могли генерировать это значение (например, отфильтровывайте те, которые создают значение вне диапазона, как немедленно непригодное) или зажимают значение, прежде чем передавать его llabs().

+0

спасибо. Я думаю, что я проверю недопустимое значение как частный случай перед вызовом llabs. –

1

Подписанный 64-битные целые диапазоны от −(2^63) до 2^63 − 1, абсолютное значение 0x8000000000000000 или −(2^63), является 2^63, больше, чем максимальное 64-разрядного целого числа.

4

В принципе, вы не можете.

Диапазон представимых значений для int64_t -2 до +2 -1. (И стандарт требует, чтобы int64_t имел чистое представление 2's-complement, если это не поддерживается, реализация просто не будет определять int64_t.)

Это дополнительное отрицательное значение не имеет соответствующего представляемого положительного значения.

Так что, если ваша система не имеет целочисленного типа размером более 64 бит, вы просто не сможете представить абсолютное значение 0x8000000000000000 как целое число.

Фактически, поведение вашей программы не определено в соответствии со стандартом ISO C. Цитирование раздел 7.22.6.1 из N1570 draft стандарта ISO C 2011:

абс, лаборатории и llabs вычисляют абсолютное значение целочисленного J. Если результат не может быть представлен, поведение не определено.

В этом отношении, в результате

int64_t a = 0x8000000000000000; 

определяется реализацией. Предполагая, что long long имеет 64 бита, эта константа имеет тип unsigned long long. Он неявно преобразован в int64_t. Очень вероятно, но не гарантировано, что сохраненное значение будет -2 , или -9223372036854775808. (Даже если конверсия позволяет повысить сигнал, определяемый реализацией, но это маловероятно.)

(Также теоретически возможно, чтобы поведение вашей программы было скорее определением реализации, а не неопределенным.Если long long шире, чем 64 бита, то оценка llabs(a) не определена, но преобразование результата обратно в int64_t определено реализацией. На практике я никогда не видел компилятора С с long long шире, чем 64 бита.)

Если вам действительно нужно представить целочисленные значения, большие, вы можете рассмотреть арифметический пакет с несколькими точками, такой как GNU GMP.

2

0x8000000000000000 - наименьшее число, которое может быть представлено подписанным 64-битным целым числом. Из-за особенностей в two's complement это единственное 64-разрядное целое число с абсолютным значением, которое не может быть представлено как 64-разрядное целое число со знаком.

Это связано с тем, что 0x8000000000000000 = -2^63, а максимальное представляемое 64-битовое целое число равно 0x7FFFFFFFFFFFFFFF = 2^63-1.

Из-за этого принятие абсолютного значения этого является неопределенным поведением, которое обычно приводит к тому же значению.

1

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

наблюдать 8-битовое целое число

int8_t х = 0x80; // двоичный 1000_0000, decimal -128

8-разрядное целое число со знаком может содержать значения от -128 до +127 включительно, поэтому значение +128 выходит за пределы допустимого диапазона. Для 16-разрядного целого это тоже

int16_t = 0x8000; // двоичный 1000_0000_0000_0000, decimal -32,768

16-разрядное целое число может содержать значения от -32,768 до +32,767 включительно.

Этот шаблон хранится для любого размера целого числа, если он представлен в дополнении 2, так же как и де-факто представление для целых чисел в компьютерах. Дополнение Two содержит 0, так как все разряды низки и -1, поскольку все бит высок.

Таким образом, целое число со знаком N-бит может содержать значения между 2^(N-1) и 2^(N-1) -1 включительно, целое число без знака может содержать значения от 0 до 2^N-1 включительно.

Смежные вопросы