Опубликован: 04.12.2009 | Доступ: свободный | Студентов: 8424 / 660 | Оценка: 4.30 / 3.87 | Длительность: 27:27:00
Лекция 4:

Работа с числами в языке Java

< Лекция 3 || Лекция 4: 12345 || Лекция 5 >

Двоичное представление отрицательных целых чисел. Дополнительный код

Старший бит в целых без знака имеет обычный смысл, в целых со знаком – для положительных чисел всегда равен 0. В отрицательных числах старший бит всегда равен 1. В примерах для простоты мы будем рассматривать четырехбитную арифметику. Тогда в качестве примера целого положительного числа можно привести 0110_2.

Для хранения отрицательных чисел используется дополнительный код. Число (– n), где n положительно, переводится в число n2=-n по следующему алгоритму:

  • этап 1: сначала число n преобразуется в число n1 путем преобразования n\to n1, во время которого все единицы числа n заменяются нулями, а нули единицами, то есть 1\to0,\;0\to1 ;
  • этап 2: перевод n1\to n2=n1+1, то есть к получившемуся числу n1 добавляется единица младшего разряда.

Надо отметить, что дополнительный код отрицательных чисел зависит от разрядности.

Например, код числа (–1) в четырехразрядной арифметике будет 1111_2, а в 8-разрядной арифметике будет 11111111_2. Коды числа (–2) будут 1110_2 и 11111110_2, и так далее.

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

Сложение положительного и отрицательного чисел

Рассмотрим, чему равна сумма числа 1 и числа –1, представленного в дополнительном коде. Сначала переведем в дополнительный код число –1. При этом n=1_{10}, n_2= –1_{10}.

  • Этап1: n=1_{10}=0001_2\to n1=1110_2 ;
  • Этап2: n2=1110_2+1=1111_2 ;

Таким образом, в четырехбитном представлении –1_{10}=1111_2.

Проверка:

n2+n=10000_2. Получившийся пятый разряд, выходящий за пределы четырехбитной ячейки, отбрасывается, поэтому в рамках четырехбитной арифметики получается n2+n=0000_2=0.

Аналогично

  • n=2_{10}=0010_2\to n1=1101_2; n2=1110_2;
  • n=3_{10}=0011_2\to n1=1100_2; n2=1101_2;
  • n=4_{10}=0100_2\to n1=1011_2; n2=1100_2;

Очевидно, во всех этих случаях n2+n=0.

Что будет, если мы сложим 3_{10} и –2_{10} (равное 1110_2, как мы уже знаем)?

\frac{+\substack{0011_2\\ 1110_2}}{\;\;10001_2}

После отбрасывания старшего бита, выходящего за пределы нашей четырехбитовой ячейки, получаем 0011_2 + 1110_2=0001_2, то есть 3_{10} + (–2_{10})=1_{10}, как и должно быть.

Сложение отрицательных чисел

(-1)+(-1)=1111_2+1111_2=11110_2\to 1110_2 из-за отбрасывания лишнего старшего бита, выходящего за пределы ячейки. Поэтому (-1)+(-1)=1110_2=-2.

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

Приведенные примеры иллюстрируют тот факт, что сложение положительного числа с отрицательным, хранящимся в дополнительном коде, или двух отрицательных, может происходить аппаратно (что значит очень эффективно) на основе крайне простых электронных устройств.

Проблемы целочисленной машинной арифметики

Несмотря на достоинства в двоичной машинной (аппаратной) арифметике имеются очень неприятные особенности, возникающие из-за конечной разрядности машинной ячейки.

Проблемы сложения положительных чисел

Пусть a=3_{10}=0011_2; b=2_{10}=0010_2; a+b=0101_2=5_{10}, то есть все в порядке.

Пусть теперь a=6_{10}=0110_2, b=5_{10}=0101_2. Тогда a+b =1011_2= -3_2.

То есть сложение двух положительных чисел может дать отрицательное, если результат сложения превышает максимальное положительное число, выделяемое под целое со знаком для данной разрядности ячеек! В любом случае при выходе за разрешенный диапазон значений результат оказывается неверным.

Если у нас беззнаковые целые, проблема остается в несколько измененном виде. Сложим 8_{10}+8_{10} в двоичном представлении. Поскольку 8_{10}=1000_2, тогда 8_{10}+8_{10}= 1000_2+ 1000_2=10000_2. Но лишний бит отбрасывается, и получаем 0. Аналогично в четырехбитной арифметике, 8_{10}+9_{10}=1_{10}, и т.д.

При целочисленном умножении выход за пределы разрядности ячейки происходит гораздо чаще, чем при сложении или вычитании. Например, 110_2\times 101_2=110_2\times 100_2+110_2\times 1_2=11000_2+110_2=11100_2. Если наша ячейка четырехразрядная, произойдет выход за ее пределы, и мы получим после отбрасывания лишнего бита 1110_2=-2_{10}<0. Таким образом, умножение целых чисел легко может дать неправильный результат. В том числе – даже отрицательное число. Поэтому при работе с целочисленными типами данных следует обращать особое внимание на то, чтобы в программе не возникало ситуаций арифметического переполнения. Повышение разрядности целочисленных переменных позволяет смягчить проблему, хотя полностью ее не устраняет. Например, зададим переменные

byte m=10,n=10,k=10;

Тогда значения m*n, m*k и n*k будут лежать в разрешенном диапазоне -128..127. А вот m*n + m*k из него выйдет. Не говоря уж об m*n*k.

Если мы зададим

short m=10,n=10,k=10;

переполнения не возникнет даже для m*n*k. Однако, при m=n=k=100 значение m*n*k будет равно 10^6, что заметно выходит за пределы разрешенного диапазона –32768..32767. Хотя m*n, m*k и n*k не будут за него выходить (но уже 4*m*n за него выйдет). Использование типа long поможет и в этом случае. Однако уже значения m=n=k=2000 (не такие уж большие!) опять приведут к выходу m*n*k за пределы диапазона. Хотя для m*n выход произойдет только при значениях около 50000.

Вычисление факториала с помощью целочисленной арифметики даст удивительные результаты! В таких случаях лучше использовать числа с плавающей точкой. Пример:

byte i=127, j=1, k;
k=(byte)(i+j);
System.out.println(k);

В результате получим число (-128). Если бы мы попробовали написать

byte i=127,j=1,k;
System.out.println(i+j);

то получили бы +128. Напомним, что значения величин типа byte перед проведением сложения преобразуются в значения типа int.

< Лекция 3 || Лекция 4: 12345 || Лекция 5 >
Полетаев Дмитрий
Полетаев Дмитрий
Не очень понятно про оболочечные Данные,ячейки памяти могут наверно размер менять,какое это значение те же операции только ячейки больше,по скорости тоже самое
Максим Старостин
Максим Старостин

Код с перемещением фигур не стирает старую фигуру, а просто рисует новую в новом месте. Точку, круг.