Почему знак отличается после вычитания неподписанного и подписанного? - PullRequest
0 голосов
/ 11 октября 2018
unsigned int t = 10;
int d = 16;
float c = t - d;
int e = t - d;

Почему значение c положительное, а e отрицательное?

Ответы [ 2 ]

0 голосов
/ 11 октября 2018

Во-первых, вы должны понимать "обычные арифметические преобразования" (эта ссылка для C, но правила в C ++ те же).В C ++, если вы выполняете арифметику со смешанными типами (вам следует избегать этого, когда это возможно, между прочим), существует набор правил, который решает, в каком типе выполняется вычисление.

В вашем случае вывычитая подписанный int из неподписанного int.Правила продвижения гласят, что фактический расчет выполняется с использованием unsigned int.

Таким образом, ваш расчет составляет 10 - 16 в беззнаковой арифметике.Арифметика без знака является арифметикой по модулю, что означает, что она оборачивается.Итак, при условии вашего типичного 32-битного целого, результат этого вычисления будет 2 ^ 32 - 6.

Это одинаково для обеих строк.Обратите внимание, что вычитание полностью не зависит от назначения;тип слева не имеет абсолютно никакого влияния на то, как происходит вычисление.Распространенной ошибкой новичка считается, что тип слева как-то влияет на вычисление;но float f = 5 / 6 равно нулю, поскольку при делении все еще используется целочисленная арифметика.

Разница в том, что происходит во время назначения.Результат вычитания неявно преобразуется в float в одном случае и int в другом.

Преобразование в число с плавающей точкой пытается найти самое близкое значение к фактическому, которое может представлять тип.Это будет очень большая ценность;не совсем то, что было получено в исходном вычитании.

Преобразование в int говорит о том, что если значение попадает в диапазон int, значение не изменится.Но 2 ^ 32 - 6 намного больше, чем 2 ^ 31 - 1, который может содержать 32-битное целое, поэтому вы получаете другую часть правила преобразования, которая говорит, что полученное значение определяется реализацией.Это термин в стандарте, который означает, что «разные компиляторы могут делать разные вещи, но они должны документировать, что они делают».

Во всех практических целях все компиляторы, с которыми вы, вероятно, столкнетесь, скажут, что битшаблон остается прежним и просто интерпретируется как подписанный.Из-за способа работы арифметики дополнения 2 (так, что почти все компьютеры представляют отрицательные числа), в результате вы получите -6, который вы ожидаете из расчета.

Но все это очень длинный способ повторенияПервая точка, которая "не делайте арифметику смешанного типа".Сначала приведите типы явно к типам, которые, как вы знаете, будут делать правильно.

0 голосов
/ 11 октября 2018

Давайте начнем с анализа результата t - d.

t - это unsigned int, а d - int, поэтому для их арифметики необходимо указать значение d преобразуется в unsigned int (правила C ++ гласят, что здесь неподписанный получает предпочтение).Таким образом, мы получаем 10u - 16u, который (при условии 32-битного int) переносится в 4294967290u.

Это значение затем преобразуется в float в первом объявлении и в int ввторой.

Предполагая типичную реализацию float (32-разрядный IEEE с одинарной точностью), его наибольшее представимое значение примерно равно 1e38, поэтому 4294967290u находится в пределах этого диапазона.Будут ошибки округления, но преобразование во float не будет переполнено.

Для int ситуация другая.4294967290u слишком велик, чтобы поместиться в int, поэтому происходит циклическое изменение, и мы возвращаемся к значению -6.Обратите внимание, что такой обход не гарантируется стандартом: результирующее значение в этом случае определяется реализацией (1) , что означает, что это зависит от компилятора, каково значение результата, но оно должно бытьзадокументировано.


(1) C ++ 17 (N4659), [conv.integral] 7,8 / 3:

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

...