#heading[Представление чисел.]
#emph[Представление чисел. Способы хранения целых чисел: бит под знак, код со сдвигом, дополнение до 1, дополнение до 2). Способы хранения нецелых чисел: числа с фиксированной точкой, числа с плавающей точкой и стандарт IEEE 754 (как производить арифметические операции, специальные числа).]

== Представление чисел
=== Историческая справка
#emph[Аналитическая машина Бэббиджа] - первая в мире программируемая вычислительная машина. Конструкция разностной машины основывалась на использовании десятичной системы счисления.

#import "/commons.typ": imagebox
#imagebox("turring.png", height: 25%, width: 100%, label: [Аналитическая машина Бэббиджа])

=== Разновидности систем счисления
Числа представлены в двоичной системы счисления. Почему?
+ #emph[Система числения с основанием $e$]. Имеет максимальную плотность записи информации. Однако по историческим причинам она не получила сильного применения. Переход на другую систему счисления обошёлся бы очень дорого.
+ #emph[Троичная система счисления]. Имеет плотность записи информации выше, чем у двоичной системы счисления. Однако по историческим причинам она не получила сильного применения. Переход на другую систему счисления обошёлся бы очень дорого.
+ #emph[Десятичная система счисления]. Крайне неудобна из-за необходимости в огромном физическом сопровождении.

== Способы хранения целых чисел
=== Прямой код (бит под знак)
При записи числа в прямом коде старший разряд является знаковым разрядом. Если его значение равно нулю, то представлено положительное число или положительный ноль, если единице, то представлено отрицательное число или отрицательный ноль. В остальных разрядах, которые называются цифровыми, записывается двоичное представление модуля числа. Таким способом в n-битовом типе данных можно представить диапазон чисел $[−2^(n−1)+1;2^(n−1)−1]$.

#columns(2)[
  #align(center)[#strong[Плюсы]]
  + В таком представлении числа легко конвертировать в десятичную запись и обратно.
  + Из-за того, что 0 обозначает +, коды положительных чисел относительно беззнакового кодирования остаются неизменными.
  + Одинаковое количество положительных и отрицательных чисел.
  #colbreak()
  
  #align(center)[#strong[Минусы]]
  + Выполнение арифметических операций с отрицательными числами требует усложнения архитектуры центрального процессора (например, для вычитания невозможно использовать сумматор, необходима отдельная схема для этого).
  + Существуют два нуля: −0 (100…000) и +0 (000…000), из-за чего усложняется арифметическое сравнение.
]
_Из-за весьма существенных недостатков прямой код используется очень редко._

=== Код со сдвигом
При использовании кода со сдвигом целочисленный отрезок от нуля до $2^n$, где n — количество бит, сдвигается влево на $2^(n−1)$, а затем получившиеся на этом отрезке числа последовательно кодируются в порядке возрастания кодами от 000…0 до 111…1. По сути, при таком кодировании к кодируемому числу прибавляют $2^(n-1)$, после чего переводят получившееся число в двоичную систему исчисления.
Можно получить диапазон значений $[−2^(n−1);2^(n−1)-1]$.

#columns(2)[
  #align(center)[*Плюсы*]
  + Отсутствует проблема с двумя нулями.
  #colbreak()
  
  #align(center)[*Минусы*]
  + При арифметических операциях нужно учитывать смещение, то есть проделывать на одно действие больше (например, после «обычного» сложения двух чисел у результата будет двойное смещение, одно из которых необходимо вычесть).
  + Ряд положительных и отрицательных чисел несимметричен.
]

_Из-за необходимости усложнять арифметические операции код со сдвигом для представления целых чисел используется не часто, но зато применяется для хранения порядка вещественного числа._
#columns(2)[
  #imagebox("priamoi_kod.png", height: 150pt, label: [Прямой код.])
  #colbreak()
  #imagebox("sdvig_kod.png", height: 150pt, label: [Код со сдвигом.])
]
#pagebreak()

=== Дополнение до единицы

В качестве альтернативы представления целых чисел может использоваться код с дополнением до единицы. Алгоритм получения кода числа:
+ Если число положительное, то в старший разряд, который является знаковым, записывается ноль, а далее записывается само число.
+ Если число отрицательное, то код получается инвертированием представления модуля числа, таким образом, получается обратный код.
+ Если число является нулем, то его можно представить двумя способами: +0 (000…000) или −0 (111…111).

Таким способом можно получить диапазон значений $[−2^(n−1)+1;2^(n−1)−1]$.

#columns(2)[
  #align(center)[*Плюсы*]
  + Простое получение кода отрицательных чисел.
  + Из-за того, что 0 обозначает +, коды положительных чисел относительно беззнакового кодирования остаются неизменными.
  + Количество положительных чисел равно количеству отрицательных.
  #colbreak()
  
  #align(center)[*Минусы*]
  + Выполнение арифметических операций с отрицательными числами требует усложнения архитектуры центрального процессора.
  + Существуют два нуля: +0 и −0.
]

=== Дополнение до двух

*Чаще всего* для представления отрицательных чисел используется код с дополнением до двух. Алгоритм получения кода числа:
+ Если число неотрицательное, то в старший разряд записывается ноль, далее записывается само число.
+ Если число отрицательное, то все биты модуля числа инвертируются, то есть все единицы меняются на нули, а нули — на единицы, к инвертированному числу прибавляется единица, далее к результату дописывается знаковый разряд, равный единице.

Для получения из дополнительного кода самого числа нужно инвертировать все разряды кода и прибавить к нему единицу. Таким образом, можно получить диапазон значений $[−2^(n−1);2^(n−1)−1]$.

#columns(2)[
  #align(center)[*Плюсы*]
  + Возможность заменить арифметическую операцию вычитания операцией сложения и сделать операции сложения одинаковыми для знаковых и беззнаковых типов данных, что существенно упрощает архитектуру процессора и увеличивает его быстродействие.
  + Нет проблемы двух нулей.
  #colbreak()
  
  #align(center)[*Минусы*]
  + Ряд положительных и отрицательных чисел несимметричен, но это не так важно: с помощью дополнительного кода выполнены гораздо более важные вещи, желаемые от способа представления целых чисел.
  + В отличие от сложения, числа в дополнительном коде нельзя сравнивать как беззнаковые, или вычитать без расширения разрядности.
]

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

+ Дополнительный код также удобно использовать для вычислений в длинной арифметике, особенно для операций сложения и вычитания.
+ Для умножения и деления лучше всего использовать прямой код (бит под знак). Ибо выполнять умножения с числами в дополнительном коде не всегда оптимально.

#columns(2)[
  #imagebox("kodd_odin.png", height: 150pt, label: [
    #emph[Дополнение до единицы]
  ])
  #colbreak()
  #imagebox("kodd_dva.png", height: 150pt, label: [
    #emph[Дополнение до двух]
  ])
]
#pagebreak()

== Способы хранения нецелых чисел
=== Числа с фиксированной точкой
В представлении нецелых чисел с фиксированной точкой число разбивается на три части:
+ Бит под знак.
+ Представление целой части ($N$ бит).
+ Представление дробной части ($M$ бит).
Числа $M, N$ являются константами. Дапозон целой части чисел -- $[-2^N + 1, 2^N - 1]$.

#columns(2)[
  #set par(justify: false)
  #align(center)[*Плюсы*]
  + Имеем удобные арифмитические операции.
  #colbreak()
  
  #align(center)[*Минусы*]
  + Фиксированное количество бит под целую и дробную части.
  + Непонятно, где заканчивается целая и начинается дробная части.
  + Чтобы получить отрицательные числа, необходимо использовать дополнение до двух или прямой код (бит под знак).
]

=== Числа с плавающей точкой по стандарту IEEE754
Числа с плавающей точкой - один из возможных способов представления действительных чисел, который является компромиссом между точностью и диапазоном принимаемых значений, его можно считать аналогом экспоненциальной записи чисел, но только в памяти компьютера.

_Нормальной_ называется форма представления числа, при которой абсолютное значение мантиссы десятичного числа находится на полуинтервале $\[0,1)$

_Нормализованной_ называется форма представления числа, при которой абсолютное значение мантиссы десятичного числа лежит на полуинтервале $\[1,10)$, а двоичного на полуинтервале $\[1,2)$.

В формате IEEE754 число с плавающей запятой представляется в виде набора битов, часть из которых кодирует собой мантиссу числа, другая часть — показатель степени, и ещё один бит используется для указания знака числа (0 — если число положительное, 1 — если число отрицательное). При этом экспонента записывается как целое число в коде со сдвигом, а мантисса — в нормализованном виде, своей дробной частью в двоичной системе счисления.
_Например_, число -2435e9. 1 - знак, 10 - основание, 2345 - мантиса, 9 - экспонента.
#imagebox("ieee754.png", height: 60pt)

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

Существуют различные форматы точности:
#imagebox("precisionTable.png")
#pagebreak()

=== Особые значения чисел с плавающей точкой
*Ноль (со знаком)*.
В нормализованной форме невозможно представить ноль. Для его представления в стандарте зарезервированы специальные значения мантиссы и экспоненты.
#imagebox("edgeZero.png", height: 50pt)

*Бесконечность (со знаком)*.
Для приближения ответа к правильному при переполнении, в стандарте можно записать бесконечное значение. Так же, как и в случае с нолем, для этого используются специальные значение мантиссы и экспоненты.
#imagebox("edgeInf.png", height: 50pt)

Бесконечное значение можно получить при переполнении или при делении ненулевого числа на ноль.
  
*Неопределенность*.
В математике встречается понятие неопределенности, для этого предусмотрено псевдочисло, которое арифметическая операция может вернуть даже в случае ошибки.
#imagebox("edgeNan.png", height: 50pt)

=== Арифметические операции с числами с плавающей точкой
*Сложение и вычитание*.
Идея метода сложения и вычитания чисел с плавающей точкой заключается в приведении их к одному порядку. Сначала выбирается оптимальный порядок, затем мантиссы обоих чисел представляются в соответствии с новым порядком, затем над ними производится сложение/вычитание, мантисса результата округляется и, если нужно, результат приводится к нормализированной форме

*Умножение и деление*.
Самыми простыми для восприятия арифметическими операциями над числами с плавающей запятой являются умножение и деление. Для того, чтобы умножить два вещественных числа в нормализованной форме необходимо перемножить их мантиссы, сложить порядки, округлить и нормализовать полученное число. Соответственно, чтобы произвести деление нужно разделить мантиссу делимого на мантиссу делителя и вычесть из порядка делимого порядок делителя. Затем точно так же округлить мантиссу результата и привести его к нормализованной форме.
