Всем привет! На этот раз разберемся с числами с фиксированной точкой. До этого момента мы рассматривали и проводили операции только с целыми числами, однако, не одними целыми числами живы вычисления. Существует немало областей, где без представления числа с дробной частью просто не обойтись. В зарубежных источниках предмет нашего рассмотрения называется числа с фиксированной точкой, мы же больше привыкли к запятой.
В программах верстки на виртуальных листах бумаги расстояния измеряются долями миллиметров.
Точное время без долей записать невозможно, денежные операции зачастую сопряжены с дроблением валюты. Уж если мы заговорили об обработке сигнала, то необходимо особо подчеркнуть, что одними целыми числами мы никак не обойдемся.
При обработке сигнала использование чисел с фиксированной точкой строго обязательно.
Как устроено двоичное число с дробной частью?
Теперь настало время разобраться с этими числами подробнее. Число с фиксированной точкой это двоичное число, имеющее строго определенное назначение разрядов. Старший бит такого числа отвечает за его знак.
Остальные делятся на отвечающие за целую и дробную части. Конструкцию числа можно задать двумя параметрами. Параметр N показывает сколько бит в числе, параметр Q показывает сколько бит выделяется под дробную часть.
Со знаком и целой частью все предельно ясно и без сюрпризов, а о дробной части стоит поговорить подробнее. Как мы знаем, у чисел с дробной частью после запятой расположена часть единицы. Все так и остается, двоичное число показывает сколько долей единицы от максимально возможного. Это немного непривычно и более-менее становится понятно при помощи пропорции, которой можно вычислить двоичный код дробной части.
В представленном примере необходимо получить двоичный код числа 0.6, при этом под дробную часть выделено 4 бита. Значит 5-й бит это уже будет целая единица, тогда при помощи пропорции находим решение.
Очень важный вопрос о той самой запятой между целой и дробной частью. В действительности, никакая схемотехника не поддерживает существование этой запятой. Это всего лишь вопрос интерпретации пользователя. Преобразование чисел из прямого в дополнительный код, сложение и вычитание чисел - ни на какую из этих операций эта самая запятая (точка) не влияет. Исключением является умножение.
Умножение при помощи целочисленного умножителя
Для того, чтобы умножить два числа, используют целочисленный аппаратный умножитель, присутствующий в ПЛИС. Этот блок умеет умножать целые числа, а уж если возникла необходимость учитывать знаки множителей, их дробные части и преобразовать форматы, так это целиком и полностью забота программиста.
По счастью, ничего сложного во всем перечисленном нет. Знак результата это операция исключающего ИЛИ над старшими битами. Если оба множителя положительные или оба отрицательные, то результат положителен. В чистом виде операция XOR. При учете количества знаков после запятой можно просто провести сдвиг результата вправо и тем самым сохранить нужно число бит в дробной части.
Пожалуй, стоит детально разобрать только необходимость преобразования множителей из дополнительного кода в прямой. Умножитель справится со своей работой только в этом случае.
Прямой и дополнительный код числа
Создадим пару модулей преобразования чисел из прямого в дополнительный код и обратно. Как мы помним, для положительных чисел их двоичные представления совпадают. Но как только мы имеем дело с отрицательными числами, то появляется необходимость в несложных операциях преобразования.
Для начала преобразуем число из прямого кода в дополнительный. Отрицательный знак оставляем в покое, потом инвертируем (~) все справа от знака и прибавляем единицу.
Обратное преобразование происходит так: оставляем отрицательный знак в покое, вычитаем единицу и инвертируем (~).
Как происходит умножение?
Небольшое отступление. Произведение двух чисел с фиксированной запятой в десятичной и двоичной системах счисления происходит похожим образом. Поразрядным умножением получаем несколько слагаемых, что в двоичной системе счисления упрощает все оборудование, так как вариантов всего два. Это единица и ноль. Сложение чисел в обеих системах счисления не представляет ничего сложного, в двоичной системе с этой задачей хорошо справляется арифметико-логическое устройство. Как можно заметить под результат умножения необходимо выделить в два раза больше бит, чем требуется для множителя.
Как и в случае десятичной системы счисления, в двоичной системе результат отделяется запятой ровно через столько разрядов, сколько разрядов после запятой в совокупности у каждого из множителей. Если попытаться привести результат в такой же формат данных, как и у множителей, то необходимо будет оставить три разряда после запятой. Это приведет к потери части результата и приводит к шуму округления. Это несоответствие между настоящим результатом и округленным значением
Блок умножения чисел с фиксированной точкой
Так же, как это делается в тетрадках в клетку в младшей школе, умножение чисел с дробной частью подчиняется определенному правилу. Запятой в результате отделяется столько цифр, сколько их отделено у множителей. В двоичном представлении правило не меняется.
В разработанном умножителе в первую очередь происходит преобразование чисел в прямой код (модули d2p). Под результат целочисленного умножения выделяется в два раза больше бит, чем во множителе. При учете количества бит в дробной части отбрасываются младшие биты результата и не забываем про обратное преобразование числа в дополнительный код (модуль p2d).
Моделируем
Напишем небольшой тестовый модуль, где умножим два положительных числа 2,5 на 6,25. Потом попробуем совершить умножение чисел с разными знаками.
В результате, нас ждут довольно сложные в интерпретации результаты, но лучше день потерять, но потом за 5 минут долететь. Так что во избежании ошибок в дальнейшем, интерпретируем, дамы и господа!
При умножении двух положительных чисел знак положительный, результат 15,625 что соответствует истине. При умножении 2,5 на -0,999.. результат -2,5, что тоже верно (точность - не лучшее качество цифровых вычислителей).
Поддержите статью лайком если понравилось и подпишитесь чтобы ничего не пропускать.