Что такое положительное число с плавающей точкой
Урок №33. Типы данных с плавающей точкой: float, double и long double
Обновл. 11 Сен 2021 |
Типы данных с плавающей точкой
Есть три типа данных с плавающей точкой: float, double и long double. Язык C++ определяет только их минимальный размер (как и с целочисленными типами). Типы данных с плавающей точкой всегда являются signed (т.е. могут хранить как положительные, так и отрицательные числа).
Тип | Минимальный размер | Типичный размер | |
Тип данных с плавающей точкой | float | 4 байта | 4 байта |
double | 8 байт | 8 байт | |
long double | 8 байт | 8, 12 или 16 байт |
Объявление переменных разных типов данных с плавающей точкой:
Если нужно использовать целое число с переменной типа с плавающей точкой, то тогда после этого числа нужно поставить разделительную точку и нуль. Это позволяет различать переменные целочисленных типов от переменных типов с плавающей запятой:
Обратите внимание, литералы типа с плавающей точкой по умолчанию относятся к типу double. f в конце числа означает тип float.
Экспоненциальная запись
Обычно, в экспоненциальной записи, в целой части находится только одна цифра, все остальные пишутся после разделительной точки (в дробной части).
На практике экспоненциальная запись может использоваться в операциях присваивания следующим образом:
Разбираемся в числах с плавающей точкой (часть 0)
Здравствуйте, хабровчане. Я давно увлекаюсь темой регистров с плавающей точкой. Меня всегда волновало то, как происходит вывод на экран и т.д. Помню, давным-давно в универе реализовывал свой класс чисел с плавающей точкой, состоящих из 512 бит. Единственное, что я не мог никак реализовать — это вывод на экран.
Как только у меня появилось свободное время, я взялся за старое. Завел себе тетрадку и пошло-поехало. Хотелось додуматься до всего самому, лишь иногда заглядывая в стандарт IEEE 754.
И вот что из всего этого вышло. Интересующихся прошу под кат.
Чтобы освоить эту статью, надо знать следующее: что такое бит, двоичная система, арифметика на уровне знания отрицательных степеней. В статье не будут затронуты инженерные подробности реализации на уровне процессора а также нормализованные и денормализованные числа. Больший упор сделан на перевод числа в двоичную форму и наоборот, а также объяснение того, как вообще хранятся числа с плавающей точкой в виде битов.
Числа с плавающей точкой — очень мощный инструмент, которым надо уметь правильно пользоваться. Они не столь банальны, как целочисленные регистры, но и не столь сложны, если в них грамотно и потихоньку вникнуть.
В сегодняшней статье для примера я буду использовать 32-битные регистры. Числа с двойной точностью (64-битные) работают абсолютно по той же логике.
Сначала поговорим о том, как хранятся числа с плавающей точкой. Старший 31 бит у нас знаковый. Единичка значит, что число отрицательное, а ноль, соответственно наоборот. Далее идут 8 бит экспоненты. Эти 8 битов представляют из себя обычное беззнаковое число. И в самом конце идут 23 бита мантиссы. Для удобства будем обозначать знак как S, экспоненту как E, а мантиссу, как ни странно, M.
Получаем общую формулу
У мантиссы принято считать один неявный единичный бит. То есть мантисса будет представлять из себя 24 бита, но так как старший 23-й бит всегда единица, то его можно не записывать. Таким образом это «ограничение» будет давать нам единственность представления любого числа.
Мантисса из себя представляет обычное двоичное число, но в отличие от целых чисел, старший бит это 2^0 степени и далее по убыванию степеней. Вот тут и пригождается экспонента. В зависимости от ее значения, степень двойки старшего бита увеличивается либо уменьшается. Вот и вся гениальность этой задумки.
Давайте попробуем показать это на наглядном примере:
Представим число 3.625 в двоичном виде. Поначалу разобьем это число на степени двойки.
Степень старшей двойки равна единице. E – 127 = 1. E = 128.
0 1000000 11010000000000000000000
Вот и все наше число.
Попробуем также и в обратную сторону. Пусть у нас есть 32 бита, произвольные 32 бита.
0 10000100 (1)11011100101000000000000
В скобочках указан тот самый неявный старший бит.
Сначала вычислим экспоненту. E = 132. Соответственно степень старшей двойки будет равна 5. Итого имеем следующее число:
Нетрудно догадаться, что мы можем хранить всего лишь диапазон из 24-х степеней двойки. Соответственно, если два числа отличаются в экспоненте на больше чем 24, то при сложении число остается равным большему среди них.
Для удобной конвертации я накидал небольшую программу на языке C.
Шагом сетки является минимальная разницу между двумя соседними числами с плавающей точкой. Если представить последовательность битов такого числа как обычное целое число, то соседнее число с плавающей точкой будет отличаться в битах как целое число на единицу.
Можно выразиться иначе. Два соседних числа с плавающей точкой будут отличаться на 2 ^ (E — 127 — 23). То есть на разницу, равную значению младшего бита.
В качестве доказательства можете поменять main в коде и скомпилировать еще раз.
Думаю на сегодня можно закругляться, а то получается слишком длинно. В следующий раз буду писать о сложении чисел с плавающей точкой и потере точности при округлении.
Всё, точка, приплыли! Учимся работать с числами с плавающей точкой и разрабатываем альтернативу с фиксированной точностью десятичной дроби
Сегодня мы поговорим о вещественных числах. Точнее, о представлении их процессором при вычислении дробных величин. Каждый из нас сталкивался с выводом в строку чисел вида 3,4999990123 вместо 3,5 или, того хуже, огромной разницей после вычислений между результатом теоретическим и тем, что получилось в результате выполнения программного кода. Страшной тайны в этом никакой нет, и мы обсудим плюсы и минусы подхода представления чисел с плавающей точкой, рассмотрим альтернативный путь с фиксированной точкой и напишем класс числа десятичной дроби с фиксированной точностью.
Куда уплывает точка
Не секрет, что вещественные числа процессор понимал не всегда. На заре эпохи программирования, до появления первых сопроцессоров вещественные числа не поддерживались на аппаратном уровне и эмулировались алгоритмически с помощью целых чисел, с которыми процессор прекрасно ладил. Так, тип real в старом добром Pascal был прародителем нынешних вещественных чисел, но представлял собой надстройку над целым числом, в котором биты логически интерпретировались как мантисса и экспонента вещественного числа.
Мантисса — это, по сути, число, записанное без точки. Экспонента — это степень, в которую нужно возвести некое число N (как правило, N = 2), чтобы при перемножении на мантиссу получить искомое число (с точностью до разрядности мантиссы). Выглядит это примерно так:
Чтобы избежать неоднозначности, считается, что 1 = 4 503 599 627 370 496 и спокойно вмещает в себя все 32-разрядные целые, давая сбой только на действительно больших 64-разрядных целых (19 десятичных знаков), где погрешность в сотнях единиц уже, как правило, несущественна. Если же нужна большая точность, то мы в данной статье обязательно в этом поможем.
Теперь что касается экспоненты. Это обычное бинарное представление целого числа, в которое нужно возвести 10, чтобы при перемножении на мантиссу в нормализованном виде получить исходное число. Вот только в стандарте вдобавок ввели смещение, которое нужно вычитать из бинарного представления, чтобы получить искомую степень десятки (так называемая biased exponent — смещенная экспонента). Экспонента смещается для упрощения операции сравнения, то есть для одинарной точности берется значение 127, а для двойной 1023. Все это звучит крайне сложно, поэтому многие пропускают главу о типе с плавающей точкой. А зря!
Примерное плаванье
Чтобы стало чуточку понятнее, рассмотрим пример. Закодируем число 640 (= 512 + 128) в бинарном виде как вещественное число одинарной точности:
Задание на дом: разобраться в двоичной записи следующих констант: плюс и минус бесконечность (INF — бесконечность), ноль, минус ноль и число-не-число (NaN — not-a-number).
За буйки не заплывай!
Если для целых чисел нужно учитывать только максимальное и минимальное значение, то для вещественных чисел в представлении с плавающей точкой следует больше внимания обращать не столько на максимальные значения, сколько на разрядность числа.
Другое дело проблема точности. Жалкие 23 бита под мантиссу дают погрешность уже на 8-м знаке после запятой. Для чисел с двойной точностью ситуация не столь плачевная, но и 15 десятичных знаков очень быстро превращаются в проблему, если, например, при обработке данных требуется 6 фиксированных знаков после точки, а числа до точки достаточно большие, под них остается всего лишь 9 знаков. Соответственно, любые многомиллиардные суммы будут давать значительную погрешность в дробной части. При большой интенсивности обработки таких чисел могут пропадать миллиарды евро, просто потому, что они «не поместились», а погрешность дробной части суммировалась и накопила огромный остаток неучтенных данных.
Если бы это была только теория! На практике не должно пропадать даже тысячной доли цента, погрешность всех операций должна быть строго равна нулю. Поэтому для бизнес-логики, как правило, не используют C/C++, а берут C# или Python, где в стандартной библиотеке уже встроен тип Decimal, обрабатывающий десятичные дроби с нулевой погрешностью при указанной точности в десятичных знаках после запятой. Что же делать нам, программистам на C++, если перед нами стоит задача обработать числа очень большой разрядности, при этом не используя высокоуровневые языки программирования? Да то же, что и обычно: заполнить пробел, создав один небольшой тип данных для работы с десятичными дробями высокой точности, аналогичный типам Decimal высокоуровневых библиотек.
Добавим плавающей точке цемента
Пора зафиксировать плавающую точку. Поскольку мы решили избавиться от типа с плавающей точкой из-за проблем с точностью вычислений, нам остаются целочисленные типы, а поскольку нам нужна максимальная разрядность, то и целые нам нужны максимальной разрядности в 64 бита.
Сегодня в учебных целях мы рассмотрим, как создать представление вещественных чисел с гарантированной точностью до 18 знаков после точки. Это достигается простым комбинированием двух 64-разрядных целых для целой и дробной части соответственно. В принципе, никто не мешает вместо одного числа для каждой из компонент взять массив значений и получить полноценную «длинную» арифметику. Но будет более чем достаточно сейчас решить проблему точности, дав возможность работать с точностью по 18 знаков до и после запятой, зафиксировав точку между двумя этими значениями и залив ее цементом.
Отсыпь и мне децимала!
Сначала немного теории. Обозначим наше две компоненты, целую и дробную часть числа, как n и f, а само число будет представимо в виде
Для целой части лучше всего подойдет знаковый тип 64-битного целого, а для дробной — беззнаковый, это упростит многие операции в дальнейшем.
Операции с типом десятичной дроби
Разумеется, тип числа с повышенной точностью будет бесполезен без арифметических операций. Сложение реализуется сравнительно просто:
NB: здесь и далее все записи в форме 1e — целые числа.
Здесь [n] — это получение целой части числа, а
Всегда нужно учитывать две вещи при реализации операций с числами, поскольку они подразумевают интенсивное использование: во-первых, нужно всегда оптимизировать алгоритм, сводя к минимуму операций умножения и деления, поэтому стоит заранее упростить выражение математически, так, чтобы легко выполнялся первый пункт. В нашем случае все нужно свести к минимуму целочисленных делений с остатком. Во-вторых, нужно обязательно проверять все возможные ситуации переполнения числа с выходом за границы вычисляемого типа, иначе получишь весьма неочевидные ошибки при использовании своего типа.
Введем матрицу для упрощения вычисления умножения:
Здесь мы опускаем слагаемое A44 div 10 18 просто потому, что оно равно нулю. Разумеется, перед каждым сложением стоит проверить, не выйдем ли мы за пределы MAX_INT64. К счастью, мы можем оперировать беззнаковым типом uint64_t для всех компонент матрицы и для промежуточного результата. Все, что нужно будет сделать в конце, — это определить знак результата se = sa xor sc и для отрицательного числа поправить целую и дробную часть: целую уменьшить на единицу, дробную вычесть из единицы. Вот, в общем, и все умножение, главное — быть очень аккуратным. С ассемблером все на порядок проще, но этот материал выходит за рамки Академии C++.
Алгоритм деления без регистрации и СМС
Для упрощения рассмотрим нахождение обратного числа для положительного x. Если хотя бы одна из компонент x равна нулю (но не обе сразу), вычисления сильно упрощаются. Если a = 0, то:
Для более общего случая, когда x содержит ненулевые дробную и целую части, в этом случае уравнение сводится к следующему:
Теперь нужно найти максимальную степень 10, которая будет не больше a, и итерационно выполнять следующее действие:
Здесь мы всего лишь используем умножение и деление дроби на одинаковый множитель — степень десятки, а затем пошагово вычисляем деление и остаток от деления для очередной степени десятки.
Очень полезно будет завести массив степеней десяток от 0 до 18 включительно, поскольку вычислять их совершенно излишне, мы их знаем заранее и требоваться они нам будут часто.
Преобразования типов
Мы знаем и умеем достаточно, чтобы теперь превратить расплывчатые float и double в наш новенький decimal.
Здесь 103 является, по сути, той погрешностью, за которой double перестает быть точным. При желании погрешность можно еще уменьшить, здесь 10 18-15 нужно для наглядности изложения. Нормализация после преобразования нужна будет все равно, поскольку точно double заведомо ниже даже дробной части decimal. Кроме того, нужно учитывать случай, когда double выходит за пределы int64_t, при таких условиях наш decimal не сможет правильно преобразовать целую часть числа.
Все целые числа преобразовываются в decimal без проблем, просто инициализируя поле m_integral. Преобразование в обратную сторону для целых чисел также будет просто возврат m_integral, можно добавить округление m_fractional.
Преобразование из decimal в double и float сводится к вышеуказанной формуле:
Отдельно стоит рассмотреть преобразование в строку и из строки. Целочисленная часть, по сути, преобразуется в строку как есть, после этого остается только вставить decimal separator и вывести дробную часть как целое, отбросив завершающие нули. Также можно ввести поле «точность» m_precision и записывать в строку лишь указанное в нем число десятичных знаков.
Чтение из строки то же, но в обратную сторону. Здесь сложность лишь в том, что и знак, и целая часть, и разделитель дробной и целой части, и сама дробная часть — все они являются опциональными, и это нужно учитывать.
В общем и целом я предоставляю полную свободу при реализации этого класса, но на всякий случай со статьей идет несколько файлов с исходниками одной из возможных реализаций decimal, а также с небольшим тестом вещественных чисел для лучшего усвоения материала.
GITHUB
Со статьей идет несколько файлов с исходниками одной из возможных реализаций decimal, а также с небольшим тестом вещественных чисел для лучшего усвоения материала.
Не уплывай, и точка!
В заключение скажу лишь то, что подобный тип в C/C++ может появиться в весьма специфической задаче. Как правило, проблемы чисел с большой точностью решаются языками типа Python или C#, но если уж понадобилось по 15–18 знаков до запятой и после, то смело используй данный тип.
Получившийся тип decimal решает проблемы с точностью вещественных чисел и обладает большим запасом возможных значений, покрывающим int64_t. С другой стороны, типы double и float могут принимать более широкий интервал значений и выполняют арифметические операции на уровне команд процессора, то есть максимально быстро. Старайся обходиться аппаратно поддерживаемыми типами, не залезая в decimal лишний раз. Но и не бойся использовать данный тип, если есть необходимость в точном вычислении без потерь.
В помощь также знания о двоичном представлении чисел с плавающей точкой, полученные в этой статье. Зная плюсы и минусы формата типов double и float, ты всегда примешь правильное решение, какой тип пользовать. Ведь, возможно, тебе и вовсе требуется целое число, чтобы хранить массу не в килограммах, а в граммах. Будь внимателен к точности, ведь точность наверняка внимательна к тебе!
Впервые опубликовано в журнале Хакер #192.
Автор: Владимир Qualab Керимов, ведущий С++ разработчик компании Parallels
Снова о числах с плавающей точкой
Несмотря на то, что вопросам точности компьютерных вычислений посвящено очень много публикаций, некоторые из них, на наш взгляд, всё же остаются не до конца четко раскрытыми. А именно:
1. Какое количество верных цифр n гарантированно имеет десятичное число, представленное двоичным m разрядным кодом в формате числа с плавающей точкой.
2. Как влияет нормализация чисел с плавающей точкой на точность представления числа при его преобразовании из одной системы счисления в другую и при арифметических действиях, выполняемых на компьютере.
3. Как влияет округление числа, представленного в двоичном виде на его десятичный эквивалент.
4. Как положение виртуальной точки в машинном слове влияет на значение числа, представленного в экспоненциальной форме.
Ниже мы попытаемся ответить на эти вопросы.
В своих рассуждениях мы будем исходить из представлений о числах с позиции классической арифметики. Нами будут рассмотрены числа, количество значащих цифр которых ограничено разрядной сеткой машинного слова. Чтобы упростить изложение будем рассматривать только положительные числа.
Как правило, прежде чем производить какие либо арифметические операции на компьютере над десятичными числами, их представляют в дробном двоичном виде в естественной форме, а затем записывают полученные числа в нормализованном экспоненциальном виде:
где М — мантисса двоичного числа, 2^-p — характеристика числа, ее еще часто называют экспонентой, p — порядок характеристики.
Двоичное число, представленное в десятичном виде, будем называть двоичным эквивалентом десятичного числа. Десятичное число, представленное в двоичном виде, будем называть двоичным эквивалентом десятичного числа.
Если в качестве мантиссы М принять естественную запись числа, то порядок характеристики в приведенной формуле будет равен нулю, а характеристика, соответственно, единице. Мы будем иметь число, представленное как F = M*2^0=M. Например, двоичное число F= 0.011 имеет порядок p=0. Если точку в числе условно поставить перед старшей значащей цифрой, то порядок характеристики p=-1 и F=0.11*10^-1. Если точку в числе условно поставить сразу после старшей значащей цифры, то порядок p=-2 и F=1.1*10^-2. Если виртуальную точку поставить после младшего разряда мантиссы М, то мантисса будет целым числом и для нее p=-3, а F=11*10^-3. Как мы видим, во всех случаях порядок характеристики равен количеству смещений виртуальной точки в числе относительно ее положения в естественной записи. Все виды записи в наших примерах эквивалентны и имеют одно и то же значение.
Таким образом, значение показателя характеристики числа, представленного в экспоненциальном виде, с учетом положения виртуальной точки, определяет место точки в числе, записанном в естественном виде.
Разрядность машинной мантиссы однозначно определяет количество десятичных чисел, которое может быть представлено в этой мантиссе в двоичном виде. А положение виртуальной точки в мантиссе определяет область на числовой оси, где располагаются эти числа. Двоичные числа, количество значащих цифр которых не превышает количества разрядов машинной мантиссы, являются точными, а десятичные числа, полученные конвертацией этих двоичных чисел в десятичные, называются представимыми.
В силу того, что не все значащие цифры двоичного числа могут уместиться в разрядную сетку машинной мантиссы, это число округляют до необходимого количества значащих цифр. Такое число иногда называют округленным до ближайшего представимого. Округленное двоичное число становится приближенным.
Не смотря на то, что все двоичные числа являются эквивалентом представимых десятичных чисел, не все десятичные числа можно представить в машинном слове. Это связано с несоразмерностью десятичной и двоичной систем счисления. Поэтому десятичные числа, в основном, могут быть представлены в двоичном виде приближенно, в то время как все двоичные числа могут быть представлены в десятичном виде точно. Или, другими словами. Двоичный эквивалент десятичного числа конечной длительности может содержать бесконечное количество значащих цифр. Десятичный эквивалент двоичного числа конечной длительности содержит конечное число значащих цифр.
Приближенные числа состоят из верных (в широком смысле) и неверных цифр. Неверные цифры при арифметических действиях искажают конечный результат. Чтобы этого не происходило, приближенные числа округляют до ближайшей верной цифры.
Округление двоичных чисел приводит к уменьшению учитываемых верных цифр в округленном числе и к изменению неверных цифр в его десятичном эквиваленте. Неверные цифры образуют абсолютную погрешность преобразования.
Округление двоичного числа до ближайшего представимого приводит к увеличению погрешности представления десятичного эквивалента этого числа. Это связано с тем, что при округлении двоичного числа уменьшается количество значащих двоичных цифр, участвующих в представлении его десятичного эквивалента.
В результате преобразования десятичного числа в двоичное, с заданным количеством значащих цифр, получается двоичное число, десятичный эквивалент которого будет приближенным числом, содержащим как верные, так и неверные цифры.
Определим, какое количество верных цифр n гарантированно имеет десятичное число, представленное двоичным m разрядным кодом в формате числа с плавающей точкой.
Для того чтобы десятичное число с n значащими цифрами гарантированно было представлено двоичным кодом с мантиссой, имеющей m разрядов, должно выполняться условие: (10^n)-1 ≤ (2^m)-1 или 10^n ≤ 2^m. Откуда log1010^n≤ log102^m или n≤ m log102. Поскольку log102≈0.3, то будет справедливо неравенство n≤0.3m. Поскольку числа m и n целые, для них будет справедливо неравенство
n≤⌊0.3m⌋. Так, для m=8, будем иметь n≤⌊0.3*8⌋=2.
До сих пор мы говорили о целочисленной машинной мантиссе. На практике же принято считать машинную мантиссу дробным числом с виртуальной точкой, стоящей перед старшим разрядом. Эта виртуальная точка преобразует целое число, записанное в машинной мантиссе в дробное число. Преобразование целого двоичного числа с m значащими цифрами в число, которое представляет собой правильную дробь, равносильно умножению этого числа на коэффициент 2^-m. Таким образом, если в каждом разряде m разрядной машинной мантиссы записаны только единицы и при этом предполагается, что виртуальная точка стоит в начале мантиссы, то число, представленное в этой мантиссе, будет максимальным и будет равно Mmaxd = 1-2^-m. Где Mmaxd — максимальное дробное число, которое может быть представлено в m разрядной машинной мантиссе с виртуальной точкой в начале мантиссы.
С другой стороны, если машинную мантиссу считать целочисленной, т.е. полагать, что виртуальная точка стоит сразу за младшим разрядом машинной мантиссы, то максимальное целое число Mmaxc, которое в нее можно записать, будет состоять из одних единиц и равно Mmaxc = (2^m)-1. Если теперь точку переместить в начало мантиссы, то это будет равносильно тому, что Mmaxd= Mmaxc*2^-m.
Таким образом, в m разрядную машинную мантиссу можно записать дробные числа, лежащие в диапазоне от 0 до Mmaxc*2^-m. Где Mmaxc — максимальное целое двоичное число, которое помещается в машинную мантиссу.
В общем случае от выбора виртуальной точки зависит только значение порядка характеристики числа, представленного в экспоненциальном виде. Само же значение числа остается неизменным.
Например, в трехразрядную машинную мантиссу можно записать максимальное целое число 1112 = 7. Это число с виртуальной точкой перед старшим разрядом машинной мантиссы будет иметь значение 0.111=7*2^-3= 0,875. Записанное в машинной мантиссе число 1012 = 5 с виртуальной точкой перед старшим разрядом будет иметь значение 0.1012=5*2^-3= 0,625 и т.д.
В разных источниках в настоящее время высказываются различные мнения о точности представления десятичных чисел в двоичном коде. Не затрагивая здесь вопроса точности двоичных арифметических операций над десятичными числами, представленными в двоичном виде, рассмотрим точность конвертации десятичных чисел в двоичные.
Для того чтобы рассмотренные в примерах выше целочисленные мантиссы сместить в область дробных чисел, надо все числа умножить на масштабный коэффициент 2^-53. Тогда максимальное двоичное дробное число в 53-х разрядной машинной мантиссе будет иметь вид:
0.11111111111111111111111111111111111111111111111111111 = 11111111111111111111111111111111111111111111111111111 *2^-53= 9007199254740991*2^-53= 0,99999999999999988897769753748435. Жирным шрифтом помечены верные цифры, соответствующие значащим десятичным цифрам максимального пятнадцатизначного дробного десятичного числа.
Чтобы десятичное число было преобразовано к двоичному виду максимально точно, необходимо, чтобы в двоичном представлении десятичного числа было учтено как можно больше значащих цифр. В идеале, количество значащих цифр двоичной мантиссы числа должно быть равно количеству разрядов машинной мантиссы. Для этой цели десятичное число разлагают по степеням двойки до тех пор, пока количество значащих цифр двоичного числа не сравняется с количеством разрядов машинной мантиссы или не будет разложено точно. Полученное таким образом число нормализуют, смещая старшую значащую цифру числа в старший разряд машинной мантиссы. А в машинную область порядка характеристики помещают масштабный коэффициент, равный количеству сдвигов старшей значащей цифры числа. Процедура нормализации никак не меняет значение числа, а следовательно и точность его представления.
Для примера возьмем компьютер с 8-ми разрядной мантиссой. Гарантированное количество значащих десятичных цифр, которое можно представить в 8-ми разрядной машинной мантиссе равно n ≤ ⌊0.3*8⌋=2. Пусть нам дано число 0.0012. Это число в двоичном виде будет равно ≈ 0.00000000010011101010010. Округлим это число до 8 значащих цифр, количество которых соответствует размеру разрядной сетки машинной мантиссы. Получим число 0.00000000010011101= 0.00119781494140625≈0.0012. Нормализуем это число, поместив в машинную мантиссу все значащие цифры мантиссы числа. Получим 0.00000000010011101010010= 0.10011101*2^-9 = 0,61328125*2^-9=0,00119781494140625. Как мы видим, значение числа не изменилось после нормализации. Точность представления числа также не изменилась, т.к. количество значащих цифр после нормализации осталось неизменным. Мы просто получили другую форму записи одного и того же числа.
В случае если при нормализации двоичного числа окажется, что количество сдвигов старшей значащей цифры числа превышает количество разрядов машинной области, которая предназначена для записи порядка характеристики числа, то число не может быть записано в нормализованном виде. В этом случае происходит потеря точности за счет того, что часть, или все значащие цифры числа оказываются за пределами разрядной сетки машинной мантиссы.
В том случае, когда в результате арифметической операции получено двоичное число, все значащие цифры которого расположены в пределах машинной мантиссы, нормализация результата, с точки зрения арифметики, не имеет смысла.
Например, пусть мы имеем компьютер с 8-ми разрядной машинной мантиссой, в котором виртуальная точка размещена перед старшим разрядом мантиссы. Найдем разность двух двоичных чисел: 0.10110000-0.10010011=0.00011101. Значащие цифры разности полностью поместились в разрядную сетку машинной мантиссы. Десятичный эквивалент этой разности будет равен 0.6875-0.57421875=0.11328125. Нормализуем число 0.00011101. Будем иметь 0.00011101=0.11101*2^-3=0.90625*2^-3=0.11328125. Мы видим, что нормализация никак не изменила значения разности чисел и поэтому в дальнейших вычислениях эти две записи результата, с точки зрения математики, эквивалентны.
СУХОЙ ОСТАТОК
Любое число можно представить в экспоненциальном виде с виртуальной точкой, расположенной в любом месте мантиссы. Смещение точки от ее естественного положения записывается в область машинного слова, выделенную для записи порядков характеристик.
Разрядность машинной мантиссы определяет количество десятичных чисел, которое может быть представлено в этой мантиссе. А положение виртуальной точки в мантиссе определяет область на числовой оси, где располагаются эти числа.
Положение виртуальной точки никак не влияет на точность представления числа.
Точность представления двоичного числа зависит от количества значащих цифр мантиссы, которое можно записать в машинное слово.
Все двоичные числа являются эквивалентом представимых десятичных чисел. Десятичные числа не все являются представимыми в машинном слове. Десятичные действительные числа, в основном, могут быть представлены в двоичном виде приближенно, в то время как все двоичные числа могут быть представлены в десятичном виде точно.
Округление двоичных чисел приводит к уменьшению учитываемых верных цифр в округленном двоичном числе, за счет отбрасывания цифр, не вместившихся в разрядную сетку машинной мантиссы и, как следствие, к уменьшению точности представления десятичного эквивалента.
Округление двоичных действительных чисел приводит к изменению неверных цифр в их десятичном эквиваленте, но не к ликвидации неверных десятичных цифр.
Неверные десятичные цифры в десятичном эквиваленте двоичного числа, полученного из точного десятичного числа, образуют абсолютную погрешность преобразования.
В машинной мантиссе с m разрядами можно гарантированно представить десятичное число, количество верных цифр в котором n≤⌊0.3m⌋.
Нормализация двоичных чисел никак не меняет значения числа, если она осуществляется без округления.
Нормализация не влияет на точность представления числа, если она осуществляется без округления.