Рассмотрим умножение вещественных чисел (a, b — исходные вещественные числа; r — вещественный результат).
Преобразуем вещественные числа из float-point во fractional и распишем операцию умножения подробнее
a = A×2−n1 — это множимое;
b = B×2−n2 — множитель;r = R×2−n — результат операции.a, b, r — это вещественные числа
A, B, R — мантиссы (целые числа)
n1, n2, n — показатели степени или порядки чисел (целые числа)
Правило перемножения вещественных чисел
При умножении вещественных чисел, которые представлены как мантисса и порядок, мантисы перемножаются, а порядки складываются.
Перемножение мантисс: R=A×B
Сложение порядков: n=n1+n2
При умножении чисел в формате fractional производится только умножение
Формат представления вещественных чисел
Выберем формат данных для примеров. Формат Qm.n, где
Общее количество бит (b) | |||
Количество бит для целой части (m) | |||
Количество бит для дробной части (n) |
Легко заметить, что после выполнения операции умножения формат результата изменяется (было Q., стало Q.) и требует бит в целом (было ).
Для приведения результата операции к тому же формату, что и аргументы, необходимо разделить результат умножения
Операция перемножения вещественных чисел в формате fractional.
Перемножение мантисс: R=A×B
Восстановление формата: R=R×2−
Деление целых чисел на
Также для деление целых чисел на
Пример реализации умножения чисел в формате fractional на языке С.
#include <stdio.h> #include <stdint.h> #include <stdlib.h> #define FRACTIONAL_BASE (1<<) #define INTMAX ((1<<)-1) #define INTMIN (-1<<) #define INTMAX ((1U<<)-1) int_t mul(int_t A, int_t B) { int_t R; R = (int_t)A * B; if(R == (1<<)) return INTMAX; R <<= ; return R; } int_t getHi(int_t x) { union { int_t x; struct { int_t lo; int_t hi; }; } hl; hl.x = x; return hl.hi; } int_t mul(int_t A, int_t B) { int_t R; R = mul(A,B); return getHi(R); } int_t float2fixed(float x) { x *= FRACTIONAL_BASE; x = min(x,INTMAX); x = max(x,INTMIN); return (int_t)x; } float fixed2float(int_t x) { return x/(float)FRACTIONAL_BASE; } void main() { float a = 0.1f; float b = 0.5f; float r; int16_t A, B, R; A = float2fixed(a); B = float2fixed(b); R = mul(A,B); r = fixed2float(R); printf("%f*%f=%f\n",a,b,r); }
Рассмотрим приведенный пример. Для начала вещественные значения a и b конвертируются во fractional A и B с помощью вызова функции float2fixed. При конвертации производится проверка на область допустимых значений с помощью библиотечных float-point функций min/max. Для получения вещественного результата r используется обратная функции fixed2float, в которой используется float-point деление.
При разработке программ, рассчитанных на выполнение в реальном времени, очень важно различать какие операции выполняются с использованием "медленных" библиотечных float-point функций, а какие исполняются "быстрыми" операциями.
Функция умножения
Если данные уже сконвертированы в формат fractional, то сами операции умножения и коррекции формата занимают 2-3 ассемблерные инструкции и могут выполняться за один такт процессорного времени!
Умножение fractional на integer
В некоторых случаях необходимо умножать вещественное число на целое число. Рассмотрим, как это сделать используя целочисленную арифметику. Вещественное число представляется в формате fractional, а целое число в формате integer. Значение integer обычно больше единицы (или меньше −1) и его нужно представить в виде fractional мантиссы и integer порядка. Тогда мы сможем умножить исходное вещественное число на мантиссу, а результат сдвинуть на полученный порядок.
Для разложения целого числа на мантиссу и порядок необходимо найти ближайшую степень двойки которая больше или равна исходному целому числу.
Например, если исходное целое — i, нам нужно найти порядок e, который удовлетворяет условию:
Результат разложения исходного целого числа на мантиссу и порядок:
Целое (i) | |
Мантисса (s) | |
Мантисса (f) | |
Порядок (e) |
После разложения целого числа на мантиссу и порядок, нам нужно умножить исходное fractional на мантиссу, а потом умножить полученный результат на 2e. Для умножения на 2e можно использовать арифметический сдвиг влево (записывается,