Класс с фиксированной точкой - Использование класса fixed_point

ОГЛАВЛЕНИЕ

 

Использование класса fixed_point

Для начала нужно включить заголовочный файл fixed_point.h:

#include <fixed_point.h>

Класс fixed_point<B, I, F> определен внутри пространства имен fmpl. Для его использования можно писать перед именем класса fpml:: везде, где он необходим, или использовать утверждение using:

using namespace fmpl;

Это два принципиально разных сценария использования класса fixed_point<B, I, F>. Вы можете использовать его в только что написанном коде, в котором требуется математика с фиксированной точкой, или в котором вы контролируете поведение, т.е., устанавливаете число целых и дробных битов. Чрезвычайно простой пример кода первого сценария использования может выглядеть так:

#include <fixed_point.h>

using namespace fpml;


main()

{
    fixed_point<int, 16> a = 256;

    fixed_point<int, 16> b = sqrt(a);

}

Разумеется, можно использовать все остальные операторы и функции. Класс fixed_point<B, I, F> содержит реализации для всех важных операторов и функций, которые вы можете принимать как должное при работе с числами с плавающей точкой.

Второй сценарий использования – это сценарий переноса, когда код уже существует, и был написан с использованием типов с плавающий точкой или с плавающей точкой двойной точности. Если вы тщательно проверили код на наличие проблем с диапазоном и точностью и приняли решение, что код по-прежнему будет работать, если вычисления с плавающей точкой заменить на вычисления с фиксированной точкой, то вы можете использовать #define для переноса кода с выполнением незначительных изменений в исходном коде:

#include <fixed_point.h>

#define double fpml::fixed_point<int, 16>


… original code here, unchanged …


#undef double

Однако необходимо быть очень осторожным, так как диапазон и точность чисел с фиксированной точкой будут отличаться от тех, которые имеют исходные числа с плавающей точкой, и это приведет к появлению ошибок в исходном коде. Особенно когда используются такие функции, как sqrt, log иди sin, распространение ошибок чисел с фиксированной точкой больше не является линейным, и можно столкнуться с неожиданностями.

Реализация класса fixed_point

Одна из целей заключается в том, что код должен быть как можно более обобщенным. Для достижения этого можно применять шаблоны. Были использованы три параметра шаблона:

template<typename B, unsigned char I, unsigned char F

        = std::numeric_limits<B>::digits - I>

class fixed_point

{

    BOOST_CONCEPT_ASSERT((boost::Integer<B>));

    BOOST_STATIC_ASSERT(I + F == std::numeric_limits<B>::digits);

    ...

private:

    B value_;

}

B – базовый тип. Это должен быть целый тип. Данный тип используется для большинства вычислений. Примеры – беззнаковый символьный тип, знаковое короткое целое, или целое. Этот тип может быть выбран в зависимости от размера, точности и требований к производительности. Если выбран беззнаковый тип, поведение класса fixed_point беззнаковое; в противном случае оно знаковое. Знаковое поведение более точно совпадает с поведением встроенных типов с плавающей точкой.

Оператор BOOST_CONCEPT_ASSERT((boost::Integer<B>)) в теле класса обеспечивает, что только целые типы могут использоваться в качестве базового типа. Обратите внимание, что базовый тип не используется здесь в смысле базового класса. Более того, класс fixed_point не является производным ни от какого базового класса, будучи самостоятельным.

I – число битой целой части, не считая знаковый бит. Оно определяет диапазон чисел, который может быть представлен.

F – число битов дробной части. Оно определяет точность чисел, которая может быть представлена. Нет необходимости устанавливать F, когда создается экземпляр шаблона, так как оно всегда может быть автоматически вычислено из базового типа B и числа целых битов I. Но если оно задано, оно должно быть правильным. Оператор BOOST_STATIC_ASSERT(I + F == std::numeric_limits<B>::digits) в теле класса обеспечивает выполнение этого условия.

 

Число битов базового типа должно удовлетворять следующей формуле: #(B)=S+I+F, где:

  • #(B) – число битов базового типа,
  • S равен 1 для знаковых типов и 0 для беззнаковых типов,
  • I – число целых битов,
  • F – число дробных битов.

В следующей таблице перечислены используемые типы и требования к I и F:

B

#(B)

S

I

F

знаковый символьный

8

1

7…1

0…6

беззнаковый символьный

8

0

8…1

0…7

короткий целый

16

1

15…1

0…14

беззнаковый короткий целый

16

0

16…1

0…15

целый

32

1

31…1

0…30

беззнаковый целый

32

0

32…1

0…31

Объекты типа fixed_point<B, I, F> имеют такой же размер, что и лежащий в их основе тип B. Класс был тщательно спроектирован так, чтобы не навязывать дополнительные требования к размеру.

Путем использования всех доступных битов для целой части достигается целочисленное поведение. Но можно вызывать определенные функции, такие как sqrt или log и т.д., для целых чисел. Далее об этих функциях рассказывается более подробно.

Типы больше 32 бит еще не поддерживаются. Одна причина этого в том, что для некоторых функций требуются в два раза большие внутренние результаты. Если разрешить 64 битовые типы, то эти внутренние результаты будут иметь размер 128 бит. Вторая причина в том, что если вы можете выделить 64 бита для типа с фиксированной точкой, то вы также сможете использовать тип с двойной точностью во многих случаях.