Изменения

Перейти к: навигация, поиск

Представление вещественных чисел

743 байта добавлено, 23 февраль
Нет описания правки
Вещественные числа обычно представляются в виде чисел с плавающей запятой. Числа с плавающей запятой — один из возможных способов представления действительных чисел, который является компромиссом между точностью и диапазоном принимаемых значений, его можно считать аналогом экспоненциальной записи чисел, но только в памяти компьютера.
Число с плавающей запятой состоит из набора отдельных двоичных разрядов, условно разделенных на так называемые '''знак''' (англ.''sign''), '''порядок''' (англ. ''exponent'') и '''мантиссу''' (англ. ''mantis''). В наиболее распространённом формате (стандарт IEEE 754) число с плавающей запятой представляется в виде набора битов, часть из которых кодирует собой мантиссу числа, другая часть — показатель степени, и ещё один бит используется для указания знака числа (<tex>0</tex> {{---}} если число положительное, <tex>1</tex> {{---}} если число отрицательное). При этом порядок записывается как целое число в [[Представление целых чисел: прямой код, код со сдвигом, дополнительный код|коде со сдвигом]], а мантисса {{---}} в [[#Нормальная и нормализованная форма|нормализованном виде]], своей дробной частью в двоичной системе счисления. Вот пример такого числа из <tex>16</tex> двоичных разрядов:
{|class="wikitable" style="border-collapse: collapse; border: none"
|-
<tex>(-1)^S \times M \times B^E</tex>, где <tex>S</tex> {{---}} знак, <tex>B</tex> {{---}} основание, <tex>E</tex> {{---}} порядок, а <tex>M</tex> {{---}} мантисса.
Десятичное число, записываемое как <tex> ReE</tex>, где <tex>R</tex> {{---}} число в полуинтервале <tex>[1; 10)</tex>, <tex>E</tex> {{---}} степень, в которой стоит множитель <tex>10</tex>;В в нормализированной форме модуль <tex>R</tex> будет являться мантиссой, а <tex>E</tex> {{---}} порядком, а <tex>S</tex> будет равно <tex>1</tex> тогда и только тогда, когда <tex>R</tex> принимает отрицательное значение.
Например, в числе <tex>-2435e9</tex>
* <tex>S</tex> <tex> = </tex> <tex>1</tex>* <tex>B</tex> <tex> = </tex> <tex>10</tex>* <tex>M</tex> <tex> = </tex> <tex>2435</tex>* <tex>E</tex> <tex> = </tex> <tex>9</tex>
Порядок также иногда называют '''экспонентой''' или просто '''показателем степени'''.
При этом лишь некоторые из вещественных чисел могут быть представлены в памяти компьютера точным значением, в то время как остальные числа представляются приближёнными значениями.
Так же используется формат хранения Более простым вариантом представления вещественных чисел является вариант с фиксированной точкой, который используется для неточных расчётовкогда целая и вещественная части хранятся отдельно. ОчевидноНапример, что степень числа на целую часть отводится всегда <tex>X</tex> бит и на дробную отводится всегда <tex>Y</tex> бит. Такой способ в архитектурах процессоров не хранится в оперативной памятиприсутствует. Отдаётся предпочтение числам с плавающей запятой, а выделяется место для хранения целого числа. Порядок же задаётся программистомкак компромиссу между диапазоном допустимых значений и точностью
== Нормальная и нормализованная форма ==
'''Нормальной формой''' (англ. ''normal form'') числа с плавающей запятой называется такая форма, в которой мантисса (без учёта знака) в десятичной системе находится на полуинтервале <tex>[0; 1)</tex>. Такая форма записи имеет недостаток: некоторые числа записываются неоднозначно (например, <tex>0{,}0001</tex> можно записать в 4 формах — <tex>0{,}0001</tex>×<tex>\times 10</tex><sup><tex>0</tex></sup>, <tex>0{,}001</tex>×<tex>\times 10</tex><sup><tex>−1</tex></sup>, <tex>0{,}01</tex>×<tex>\times 10</tex><sup><tex>−2</tex></sup>, <tex>0{,}1</tex>×<tex>\times 10</tex><sup><tex>−3</tex></sup>), поэтому распространена также другая форма записи — '''нормализованная''' (англ. ''normalized''), в которой мантисса десятичного числа принимает значения от <tex>1</tex> (включительно) до <tex>10</tex> (не включительно), а мантисса двоичного числа принимает значения от <tex>1</tex> (включительно) до <tex>2</tex> (не включительно). То есть в мантиссе слева от запятой до применения порядка находится ровно один знак. В такой форме любое число (кроме <tex>0</tex>) записывается единственным образом. Ноль же представить таким образом невозможно, поэтому стандарт предусматривает специальную последовательность битов для задания числа <tex>0</tex> (а заодно и некоторых других [[#Особые значения чисел с плавающей точкой|полезных чисел]], таких как <tex>-\infty</tex> и <tex>+\infty</tex>).
Так как старший двоичный разряд (целая часть) мантиссы вещественного числа в нормализованном виде всегда равен «<tex>1</tex>», то его можно не записывать, сэкономив таким образом один бит, что и используется в стандарте IEEE 754. В позиционных системах счисления с основанием большим, чем <tex>2</tex> (в троичной, четверичной и др.), этого замечательного свойства нет (ведь целая часть там может быть не только единицей).
 
== Типы чисел с плавающей точкой (по IEEE 754) ==
Порядок записан со сдвигом '''<tex>-16383</tex>'''.
Обычно этот формат реализуется программно, случаи аппаратной реализации крайне редки (но присутствует в процессоре Intel). Также не гарантируется поддержка этого типа в языках программирования, хотя кое-где она и реализована (например, компилятор gcc для архитектуры x86 позволяет использовать тип __float128, являющийся программной реализацией числа с четверной точностью).
В совокупности эти факторы делают Quadruple весьма экзотичным и редко встречающимся форматом чисел с плавающей запятой.
|Half precision||-||6,10&times;10<sup>-5</sup>..65504||11||16
|-
|Single presicion||float||-3,4&times;10<sup>-38</sup>..3,4&times;10<sup>38</sup>||23||32
|-
|Double precision||double||-1,7&times;10<sup>-308</sup>..1,7&times;10<sup>308</sup>||53||64
|-
|Extended precision||На некоторых архитектурах (например в сопроцессоре Intel) long double||-3,4&times;10<sup>-4932</sup>..3,4&times;10<sup>4932||65||80
|}
* <tex>0\times\infty= NaN</tex>
* <tex>\frac{\pm0}{\pm0}=NaN</tex> * <tex>\frac{\pm\infty}{\pm\infty} = NaN</tex>
* <tex>\sqrt{x} = NaN</tex>, где <tex>x<0</tex>
=== Денормализованные числа ===
'''Денормализованные числа''' (англ. ''denormalized/subnormal numbers'') - это способ увеличить количество представимых числом с плавающей запятой значений около нуля, дабы повысить точность вычислений. Каждое значение денормализованного числа меньше самого маленького '''нормализованного''' ("обычного") значения числа с плавающей запятой.
Согласно стандарту, если порядок равен своему минимальному значению (все его биты {{---}} нули, а истинное значение порядка равно его сдвигу) и все биты мантиссы равны нулю, то это <tex>\pm0</tex>. Если же мантисса не равна нулю, то это число с порядком, на единицу большим минимального (все биты порядка, кроме младшего {{---}} нули) и данной мантиссой, '''целая часть которой считается равной нулю, а не единице'''.
То есть число с плавающей запятой, при учете вышесказанного, можно задать следующим образом:
<br/>
* <tex>(-1)^s\times1.,M\times2^E</tex>, если <tex>E_{min} \le E \le E_{max}</tex> (''нормализованное число'')
* <tex>(-1)^s\times0.,M\times2^{E_{min}}</tex>, если <tex>E=E_{min}-1</tex> (''денормализованное число'')
Где <tex>s</tex> {{---}} бит знака, <tex>M</tex> {{---}} последовательность битов мантиссы, <tex>E</tex> {{---}} значение порядка (с учетом сдвига), <tex>E_{min}</tex> {{---}} минимальное значение порядка, используемое для записи чисел (1 {{---}} ''сдвиг'') , <tex>E_{min}-1</tex> {{---}} минимальное значение порядка, которое он в принципе может принять (все биты нули, 0 {{---}} ''сдвиг'').
Хоть денормализованные числа и позволяют бороться с погрешностями и обрабатывать очень маленькие значения, за эти возможности приходится дорого платить. Ввиду сложности денормализованные числа крайне редко реализуют на аппаратном уровне - вместо этого используются программные реализации, работающие значительно медленнее. <br/>
В современных процессорах обработка денормализованных чисел происходит в десятки раз медленнее, чем обработка нормализованных чисел. Ниже приведена часть таблицы из статьи Isaac Dooley, Laxmikant Kale "Quantifying the Interference Caused by Subnormal Floating-Point Values"<ref>[http://charm.cs.uiuc.edu/papers/SubnormalOSIHPA06.pdf статьи Статья Isaac Dooley, Laxmikant Kale "Quantifying the Interference Caused by Subnormal Floating-Point Values" ''(англ.)'']</ref>
{| class="wikitable"
!Производитель||Процессор||Замедление (разы)
|-
|IBM||PowerPC 970||2.,4
|-
|AMD||Athlon||6.,0
|-
|Intel||Pentium 3||15.,8
|-
|AMD||Athlon 64||21.,4
|-
|AMD||Opteron64||23.,8
|-
|Intel||Core Duo||44.,2
|-
|Intel||P4 Xeon||97.,9
|-
|Intel||Pentium 4||131.,0
|-
|Intel||Itanium 2||183.,2
|-
|Sun||UltraSPARC IV||520.,0
|}
Идея метода сложения и вычитания чисел с плавающей точкой заключается в приведении их к одному порядку. Сначала выбирается оптимальный порядок, затем мантиссы обоих чисел представляются в соответствии с новым порядком, затем над ними производится сложение/вычитание, мантисса результата округляется и, если нужно, результат приводится к нормализированной форме. Пример:
Выполним сложение чисел с плавающей точкой и смещенным порядком в 32-х разрядном формате <tex>-269</tex> <tex>7</tex><tex>/</tex><tex>32</tex> и <tex>405,875</tex>. Переведем <tex>-269</tex> <tex>7</tex><tex>/</tex><tex>32</tex> в машинный вид. Для этого сначала переведем его в двоичную систему счисления. <tex>-269</tex> <tex>7</tex><tex>/</tex><tex>32</tex> <tex> = </tex> <tex>-269{,}21875</tex> <tex>-269{,}21875</tex><sub><tex>10</tex></sub> <tex> = </tex> <tex>-100001101{,}00111</tex><sub><tex>2</tex></sub>
Нормализуем полученное двоичное число по правилам машинной арифметики.
<tex>-100001101{,}00111</tex> <tex> = </tex> <tex>-1{,}0000110100111</tex> &<tex> \times; </tex> <tex>10</tex><sup><tex>1000</tex></sup>
Найдем смещенный порядок. Так как в условии говорится о 32-разрядном представлении, то смещение порядка равно <tex>127</tex><sub><tex>10</tex></sub>.
<tex>E </tex> <tex>= </tex> <tex>8</tex><sub><tex>10</tex></sub> <tex> + </tex> <tex>127</tex><sub><tex>10</tex></sub> <tex> = </tex> <tex>1000</tex><sub><tex>2</tex></sub> <tex> + </tex> <tex>1111111</tex><sub><tex>2</tex></sub> <tex> = </tex> <tex>10000111</tex><sub><tex>2</tex></sub>
Число отрицательное, следовательно, в бите знака будет стоять единица.
Переведем второе число в машинный вид, совершая те же действия.
<tex>405,87510</tex> = <tex>110010101</tex>,<tex>111000000000011010</tex>...<sub><tex>2</tex></sub> <tex> = </tex> <tex>1,10010101111000000000011010</tex>... &<tex>\times; </tex> <tex>10</tex><sup><tex>1000</tex></sup>
В качестве мантиссы будут сохранены первые <tex>23</tex> бита после запятой т.е. <tex>10010101111000000000011</tex>.
Очевидно, что порядок со смещением у второго числа будет таким же, как и у первого.
<tex>0</tex><strong>10000111</strong><tex>10010101111000000000011</tex>
Далее в арифметических операциях будет использоваться число <tex>110010101</tex>,<tex>111</tex><sub><tex>2</tex></sub>=<tex>405</tex>.<tex>{,}875</tex><sub><tex>10</tex></sub>, а не <tex>110010101</tex>{,<tex>}111000000000011</tex><sub><tex>2</tex></sub>=<tex>405</tex>{,<tex>}87510</tex><sub><tex>10</tex></sub> видимо для упрощения(хотя это не совсем корректно).
Порядки у слагаемых равны, поэтому пропускаем шаг выравнивания порядков и проводим вычитание мантисс по правилам двоичной арифметики. В
компьютере этим занимается арифметический сопроцессор, встроенный в центральный процессор машины.
<tex>1</tex>,<tex>1001010111100</tex><sub><tex>2</tex></sub> - <tex>1-</tex>,<tex>1{,}0000110100111</tex><sub><tex>2</tex></sub> = <tex>0=</tex>,<tex>0{,}1000100010101</tex><sub><tex>2</tex></sub>
Приводим полученный результат к машинному виду. Для этого мы должны внести поправку в порядок {{---}} уменьшить его на единицу.
Найдем реальный порядок результата, вычтя из него значение смещения <tex>127</tex><sub><tex>10</tex></sub>.
<tex>E </tex> <tex>= </tex> <tex>10000110</tex><sub><tex>2</tex></sub> <tex> - </tex> <tex>1111111</tex><sub><tex>2</tex></sub> <tex> = </tex> <tex>134</tex><sub><tex>10</tex></sub> <tex> - </tex> <tex>127</tex><sub><tex>10</tex></sub> <tex> = </tex> <tex>7</tex><sub><tex>10</tex></sub> <tex> = </tex> <tex>111</tex><sub><tex>2</tex></sub>
Следовательно, число результата будет иметь вид:
<tex>A </tex> <tex>= </tex> <tex>1{,}000100010101</tex>,<tex>000100010101\times</tex> &times; <tex>10</tex><sup><tex>111</tex></sup> <tex> = </tex> <tex>10001000</tex>,<tex>10101</tex><sub><tex>2</tex></sub> = <tex>136=</tex>,<tex>136{,}65625</tex><sub><tex>10</tex></sub>
Результат наших вычислений верен, так как <tex>405</tex>{,<tex>}875</tex> - <tex>269{,}21875</tex>,<tex>21875=</tex> = <tex>136</tex>{,<tex>}65625</tex>.
=== Алгоритм получения представления вещественного числа в памяти ЭВМ ===
соответствовал нуль. Например, для типа Double порядок занимает <tex>11</tex> бит и
имеет диапазон от <tex>2</tex><sup><tex>-1023</tex></sup> до <tex>2</tex><sup><tex>1023</tex></sup>, поэтому смещение равно <tex>1023</tex><sub>(<tex>10</tex>)</sub> <tex> = </tex>
<tex>1111111111</tex><sub>(<tex>2</tex>)</sub>. Наконец, бит с номером <tex>63</tex> указывает на знак числа.</P>
<LI>перевести модуль данного числа в двоичную систему счисления;</LI>
<LI>нормализовать двоичное число, т.е. записать в виде <I>M</I>&nbsp;&<tex> \times;&nbsp;</tex>2<I><sup>p</sup></I>, где <I>M</I>&nbsp;&#151;
мантисса (ее целая часть равна <tex>1</tex><sub>(<tex>2</tex>)</sub>) и <I>p</I>&nbsp;&#151; порядок, записанный в
<OL>
<LI>Двоичная запись модуля этого числа имеет вид <tex>100111000</tex>{,<tex>}0101</tex>.</LI>
<LI>Имеем <tex>100111000{,}0101</tex>,<tex>0101=</tex> =
<tex>1{,}001110000101</tex>,<tex>001110000101\times</tex>&nbsp;&times;&nbsp;<tex>2</tex><sup><tex>8</tex></sup>.</LI>
<LI>Получаем смещенный порядок <tex>8</tex> <tex> + </tex> <tex>1023</tex> <tex> = </tex> <tex>1031</tex>. Далее имеем
<tex>1031</tex><sub>(<tex>10</tex>)</sub> <tex> = </tex> <tex>10000000111</tex><sub>(<tex>2</tex>)</sub>.</LI>
<LI>Окончательно
разряде с номером <tex>63</tex> записан нуль. Получим порядок этого числа:
<tex>01111111110</tex><sub>(<tex>2</tex>)</sub> <tex> = </tex> <tex>1022</tex><sub>(<tex>10</tex>)</sub>; <tex>1022</tex> <tex> - </tex> <tex>1023</tex> <tex> = </tex> <tex>-1</tex>.</LI>
<LI>Число имеет вид <tex>1</tex>,<tex>1100011</tex>&nbsp;&<tex> \times;&nbsp;</tex><tex>2</tex><sup><tex>-1</tex></sup> или
<tex>0</tex>,<tex>11100011</tex>.</LI>
== Примечания ==
[http:<references//charm.cs.uiuc.edu/papers/SubnormalOSIHPA06.pdf Статья Isaac Dooley, Laxmikant Kale "Quantifying the Interference Caused by Subnormal Floating-Point Values"]>
== Ссылки ==
* [http://ru.wikipedia.org/wiki/%D0%9E%D1%82%D1%80%D0%B8%D1%86%D0%B0%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D1%8B%D0%B9_%D0%B8_%D0%BF%D0%BE%D0%BB%D0%BE%D0%B6%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D1%8B%D0%B9_%D0%BD%D0%BE%D0%BB%D1%8C Википедия {{---}} Отрицательный и положительный ноль]
*[http://habrahabr.ru/blogs/cpp/112953/ Хабрахабр {{---}} статья пользователя Yruslan "Что нужно знать про арифметику с плавающей запятой"]
*[http://www.sgu.ru/prcnit/teach/3.php Статья Лапшевой Е.Е. "Машинная арифметика с вещественными числами"]<span style="color: red">Статья удалена</span>
'''На английском'''
Анонимный участник

Навигация