Реализация собственной библиотеки decimal.h.
- Аннотация
- Тип Decimal
- Двоичное представление
- Реализованные функции
- Структура проекта
- Установка и сборка
- Цели Makefile
В данном проекте представлена реализация библиотеки decimal.h на языке программирования С. Хотя тип «decimal» отсутствует в стандарте языка он, тем не менее, критически важен, например, для финансовых расчетов, где недопустимы погрешности вычислений, свойственные типам с плавающей точкой. Данный проект позволяет познакомится с задачами обработки финансовой информации, погрузится в вопросы внутреннего представления различных типов данных и закрепить структурный подход.
Тип Decimal представляет десятичные числа в диапазоне от положительных 79,228,162,514,264,337,593,543,950,335 до отрицательных 79,228,162,514,264,337,593,543,950,335. Значение Decimal по умолчанию равно 0. Decimal подходит для финансовых расчетов, которые требуют большого количества значимых целых и дробных цифр и отсутствия ошибок округления. Этот тип не устраняет необходимость округления. Скорее, сводит к минимуму количество ошибок из-за округления.
Когда результат деления и умножения передается методу округления, результат не страдает от потери точности.
Decimal число - это значение с плавающей точкой, состоящее из знака, числового значения, где каждая цифра находится в диапазоне от 0 до 9, и коэффициента масштабирования, который указывает положение десятичной точки, разделяющей целые и дробные части числового значения.
Двоичное представление Decimal состоит из 1-разрядного знака, 96-разрядного целого числа и коэффициента масштабирования, используемого для деления 96-разрядного целого числа и указания того, какая его часть является десятичной дробью. Коэффициент масштабирования неявно равен числу 10, возведенному в степень в диапазоне от 0 до 28. Следовательно, двоичное представление Decimal имеет вид ((от -2^96 до 2^96) / 10^(от 0 до 28)), где -(2^96-1) равно минимальному значению, а 2^96-1 равно максимальному значению.
Коэффициент масштабирования также может сохранять любые конечные нули в Decimal. Эти конечные нули не влияют на значение в арифметических операциях или операциях сравнения.
Двоичное представление Decimal состоит из 1-разрядного знака, 96-разрядного целого числа и коэффициента масштабирования, используемого для деления целого числа и указания того, какая его часть является десятичной дробью. Коэффициент масштабирования неявно равен числу 10, возведенному в степень в диапазоне от 0 до 28.
Decimal реализован в виде четырех 32-разрядных целых чисел без знака(int bits[4];):
typedef struct {
uint32_t lo32;
uint32_t mi32;
uint32_t hi32;
uint32_t flags;
} decimal;
lo32, mi32, и hi32 содержат младшие, средние и старшие 32 бита 96-разрядного целого числа соответственно.
flags содержит коэффициент масштабирования и знак, и состоит из следующих частей:
- Биты от 0 до 15, младшее слово, не используются и должны быть равны нулю.
- Биты с 16 по 23 должны содержать показатель степени от 0 до 28, который указывает степень 10 для разделения целого числа.
- Биты с 24 по 30 не используются и должны быть равны нулю.
- Бит 31 содержит знак; 0 означает положительный, а 1 означает отрицательный.
Битовое представление различает отрицательные и положительные нули. Эти значения считаются эквивалентными во всех операциях.
| Название оператора | Оператор | Функция |
|---|---|---|
| Сложение | + | int add(decimal value_1, decimal value_2, decimal *result) |
| Вычитание | - | int sub(decimal value_1, decimal value_2, decimal *result) |
| Умножение | * | int mul(decimal value_1, decimal value_2, decimal *result) |
| Деление | / | int div(decimal value_1, decimal value_2, decimal *result) |
| Остаток от деления | Mod | int mod(decimal value_1, decimal value_2, decimal *result) |
Функции возвращают код ошибки:
- 0 - OK
- 1 - число слишком велико или равно бесконечности
- 2 - число слишком мало или равно отрицательной бесконечности
- 3 - деление на 0
Уточнение про числа, не вмещающиеся в мантиссу:
- При получении чисел, не вмещающихся в мантиссу при арифметических операциях, использовать банковское округление (например, 79,228,162,514,264,337,593,543,950,335 - 0.6 = 79,228,162,514,264,337,593,543,950,334)
| Название оператора | Оператор | Функция |
|---|---|---|
| Меньше | < | int is_less(decimal, decimal) |
| Меньше или равно | <= | int is_less_or_equal(decimal, decimal) |
| Больше | > | int is_greater(decimal, decimal) |
| Больше или равно | >= | int is_greater_or_equal(decimal, decimal) |
| Равно | == | int is_equal(decimal, decimal) |
| Не равно | != | int is_not_equal(decimal, decimal) |
Возвращаемое значение:
- 0 - FALSE
- 1 - TRUE
| Преобразователь | Функция |
|---|---|
| Из int | int from_int_to_decimal(int src, decimal *dst) |
| Из float | int from_float_to_decimal(float src, decimal *dst) |
| В int | int from_decimal_to_int(decimal src, int *dst) |
| В float | int from_decimal_to_float(decimal src, float *dst) |
Возвращаемое значение - код ошибки:
- 0 - OK
- 1 - ошибка конвертации
Уточнение про преобразование числа типа float:
- Если числа слишком малы (0 < |x| < 1e-28), вернуть ошибку и значение, равное 0
- Если числа слишком велики (|x| > 79,228,162,514,264,337,593,543,950,335) или равны бесконечности, вернуть ошибку
- При обработке числа с типом float преобразовывать все содержащиеся в нём цифры
Уточнение про преобразование из числа типа decimal в тип int:
- Если в числе типа decimal есть дробная часть, то её следует отбросить (например, 0.9 преобразуется 0)
| Описание | Функция |
|---|---|
| Округляет указанное Decimal число до ближайшего целого числа в сторону отрицательной бесконечности. | int floor(decimal value, decimal *result) |
| Округляет указанное Decimal число до ближайшего целого числа в сторону положительной бесконечности. | int ceil(decimal value, decimal *result) |
| Округляет Decimal до ближайшего целого числа согласно режиму округления. | int round(decimal value, decimal *result) |
| Возвращает целые цифры указанного Decimal числа; любые дробные цифры отбрасываются, включая конечные нули. | int truncate(decimal value, decimal *result) |
| Возвращает результат умножения указанного Decimal на -1. | int negate(decimal value, decimal *result) |
Возвращаемое значение - код ошибки:
- 0 - OK
- 1 - ошибка вычисления
В текущей версии библиотеки поддреживается 4 типа округления, которые определены в перечислении:
typedef enum {
BANK = 0, // банковское округление
FLOOR = 1, // округление вниз
CEIL = 2, // округление вверх
MATH = 3 // математическое округление (по умолчанию)
} decimal_rounding_t;Управление и контроль типо округления, реализованного в функции round, осуществляется с помощью функций:
| Описание | Функция |
|---|---|
Устанавливает тип округления согласно decimal_rounding_t. В случае успеха возвращает true, иначе - false. |
bool decimal_set_round(int mode) |
| Возвращает текущий тип округления. | int decimal_get_round(void) |
Проект имеет модульную структуру для лучшей организации кода.
└── Decimal/ # Репозиторий с исходным кодом и ресурсами
├── misc/ # Файлы, используемые в документации
├── tests/ # Директория с модульными тестами
├── src/ # Директория с реализацией библиотеки
│ ├── arithmetic/ # Директория с модулем арифметических операций
│ ├── comparison/ # Директория с модулем операций сравнения
│ ├── conversion/ # Директория с модулем операций преобразования
│ ├── math/ # Директория с модулем математических операций
│ ├── private/ # Директория с модулем приватных вспомогательных функций
│ └── types.h # Хаголовочный файл с общими типами
├── decimal.h # Главный заголовочный файл библиотеки
├── Makefile # Управление сборкой
└── README.md # Документация на русскомДля сборки проекта требуется make и компилятор gcc.
- Клонируйте репозиторий:
git clone [ссылка на РЕПОЗИТОРИЙ]- Данный проект собирается в виде статической библиотеки
my_string.a. Модуль тестирования собирается и запускается командамиmake allилиmake test. Отчет о тестировании заносится в файл test_report.log, который генерируется автоматически при запуске указанных команд. - Для запуска тестирования необходимо установить библиотеку
Check, например дляUbuntu:
sudo apt install check- Для формирования отчета о тестировании необходимо установить
gcov, например дляUbuntu:
sudo apt install gcov| Команда | Описание |
|---|---|
make all |
Сборка библиотеки и запуск тестов |
make library |
Сборка статической библиотеки my_string.a |
make test |
Сборка библиотеки и запуск тестов |
make gcov_report |
Сборка библиотеки и запуск тестов с выпуском отчета о тестировании |
make view-report |
Выполнение цели gcov_report и открытие отчета о тестировании |
make clean |
Очистка сборки |
Для форматирования кода потребуется clang-format, исходные настройки стиля (.clang-format) прилагаются к проекту. Для статического анализа кода потребуется cppcheck (2.7).
| Команда | Описание |
|---|---|
make format |
Форматирование кода clang-format |
make check-format |
Проверка стиля кода |
make cppcheck |
Статический анализатор кода |
Для проверки утечек памяти потребуется valgrind (3.18.1).
| Команда | Описание |
|---|---|
make valgrind |
Анализ программы на наличие утечек памяти |
