Рассмотрим умножение вещественных чисел (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 можно использовать арифметический сдвиг влево (записывается,