C ++: двойной против беззнакового int. Почему так работает?

Я пытаюсь умножить три числа, но получаю странный результат. Почему я получаю такие разные результаты?

unsigned int a = 7;
unsigned int b = 8;
double d1 = -2 * a * b;
double d2 = -2 * (double) a * (double) b;
double d3 = -2 * ( a * b );

// outputs:
// d1 = 4294967184.000000
// d2 = -112.000000
// d3 = 4294967184.000000

2

Решение

В вашем первом примере число -2 преобразуется в без знака Int. Результатом умножения является -112, которое при представлении в виде без знака равно 2 ^ 32 — 112 = 4294967184. Затем этот результат окончательно преобразуется в double для назначения.

Во втором примере вся математика делается на двойниках, что приводит к правильному результату. Вы получите тот же результат, если вы сделали:

double d3 = -2.0 * a * b

как -2.0 это double буквальный.

9

Другие решения

двойной подписан. Это означает, что первый бит (старший бит, известный также как знаковый бит) определяет, является ли это число положительным или отрицательным.

unsigned int не может обрабатывать отрицательные значения, потому что он использует первый бит (самый значимый бит), чтобы расширить диапазон «положительных» чисел, которые он может выразить. так в

double d1 = -2 * a * b;

после выполнения ваша машина помещает целое (-2 * a * b) в беззнаковую структуру int (например, a и b) и производит следующий двоичный файл 1111 1111 1111 1111 1111 1111 1001 0000 (потому что это дополнение двух к 112, которое это 0000 0000 0000 0000 0000 0000 0111 0000). Но проблема здесь в том, что это unsigned int, поэтому он рассматривается как очень большое положительное целое число (то есть 4294967184), потому что он не обрабатывает первую 1 как знаковый бит.

Затем вы помещаете его в двойное число, поэтому у вас напечатано .00000.

Другой пример работает, потому что вы вводите a в удвоение, а b в удвоение, поэтому при умножении -2 на удвоение ваш компьютер поместит его в двойную структуру, поэтому будет учитываться бит знака.

double d3 = -2 * (double) (a * b)

будет работать так же.

Чтобы получить представление о подписанных и неподписанных, проверьте этот

3

double d1 = -2 * a * b;

Все в правой части является целочисленным типом, поэтому правая часть будет вычислена как целочисленный тип. a а также b без знака, так что диктует конкретный тип результата. Что об этом -2? Это преобразовано в unsigned int, Отрицательные целые числа преобразуются в целые числа без знака с использованием арифметики с дополнением 2s. Тот -2 становится очень большим положительным целым числом без знака.

double d2 = -2 * (double) a * (double) b;

Теперь правая часть представляет собой смешанные целые числа и числа с плавающей запятой, поэтому правая часть будет вычисляться как тип с плавающей запятой. Что об этом -2? Это преобразовано в двойной. Теперь преобразование просто: -2 преобразован в double становится -2.0,

1

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

В этом конкретном случае, -2 по умолчанию типа signed int (синоним к int) в то время как a а также b имеют тип unsigned int, В этом случае правила гласят, что -2 должен быть повышен до unsigned intи потому что в вашей системе у вас, вероятно, есть 32 бита int и представление 2-дополнения, это в конечном итоге 2**32 - 2 (4 294 967 294). Затем это число умножается на a и результат взят по модулю 2**32 (4 294 967 282), затем bпо модулю 2**32 еще раз (4 294 967 184).

Это действительно странная система, которая привела к бесчисленным ошибкам. Например, сам переполнение привело к ошибке Linux 30 июня этого года, которая привела к зависанию стольких компьютеров по всему миру. Я слышал, это также разбило пару систем Java.

1
По вопросам рекламы [email protected]