LINUX.ORG.RU

Математика с фиксированной запятой для C++20

 


0

3

Посоны,

Мы тут сделали библиотеку для математики с фиксированной запятой на С++. Вот так работает:

const auto x = fixed::make::p<2>(1, 11);

Это число с двумя десятичными знаками после запятой представляющее 1.11.

const auto y = fixed::make::p<1>(1, 0);

Это число с одним десятичным знаком после запятой, представляющее 1.0.

const auto z = x + y;

Это сумма двух чисел.

А вот это ошибка компиляции:

fixed::make::p<3, uint8_t>((uint8_t)0, 0);

Компилятор видит, что в uint8_t нельзя представить три десятичных знака и выдаёт ошибку.

Если сделать операцию которая увеличивает точность результата, например умножение, так что требуемая точность не может быть представлена, то это тоже будет ошибка компиляции.

Во время выполнения, переполнение тоже контролируется, все операции возвращают std::optional который при переполнении будет пустым.

Библиотека спроектирована так чтобы ни один бит информации ни при каких манипуляциях не терялся бы, либо если бит надо потерять, то это надо сделать явно и число станет неточным, на что тоже есть признак, который можно проверить вызвав fixed::is_accurate(x), и если какие-то биты были потеряны, то функция вернёт false.

Можете посмотреть и покритиковать, всё ли по феншую, может чего-то не хватает или что-то лишнее? Пока что финальная версия на сегодняшний день, но было бы интересно узнать какое-нибудь мнение, или даже полезно. Довольно много строк кода, больше тысячи, но не очень много всего, только четыре операции + две трансформации + утилиты.

Ссылка: https://bitbucket.org/alekseyt/fixed

Более полный пример использования: https://bitbucket.org/alekseyt/fixed/src/master/examples/basic.cpp

Заранее спасибо.



Последнее исправление: aleksey_tulinov (всего исправлений: 1)
Ответ на: комментарий от anonymous

Идея была в том, что ни один бит не потеряется пока ты не напишешь код который умышленно теряет бит. Когда ты работаешь с деньгами, потеря бита означает потерянные деньги.

И даже если ты такой код напишешь, то по результату вычислений будет понятно, что бит потерян.

aleksey_tulinov
() автор топика
Последнее исправление: aleksey_tulinov (всего исправлений: 1)

Спасибо, пацаны. Все комментарии офигенно полезны, когда будет вдохновение посмотрим такие вещи:

  1. Операции с дробями, так чтобы 1/3+1/3+1/3 давало точную единицу
  2. Обработку ошибок с точки зрения различных предпочтений как именно это делать
aleksey_tulinov
() автор топика
Ответ на: комментарий от aleksey_tulinov

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

ИМНО надо таки начинать с вменяемого тз, формулировки на бумажке как именно этот велосипед будет использоваться.

AntonI ★★★★★
()
Ответ на: комментарий от aleksey_tulinov

Идея была в том, что ни один бит не потеряется пока ты не напишешь код который умышленно теряет бит. Когда ты работаешь с деньгами, потеря бита означает потерянные деньги.

Не, бред. Деньги можно терять спокойно. Доллар после четвёртого знака после запятой ничего не стоит. Идеи вообще никакой нету. Потеря бита ничего не значит. Имеет значение, чтобы не было типа

0.1 + 0.1 = 0.2
0.2 + 0.1 = 0.30000000000000004

Отсечение бита не изменило бы ничего.

anonymous
()
Ответ на: комментарий от aleksey_tulinov

Операции с дробями, так чтобы 1/3+1/3+1/3 давало точную единицу

Это только рациональные числа могут дать.

anonymous
()
Ответ на: комментарий от aleksey_tulinov

Не ребят, ошибки округления вы так не поборете.

Как только в вашей вундервафле возникнет необходимость раскидать сумму по разным счетам в каких то долях (сумма долей строго единица) тут же полезут ошибки округления. Если доли заданы во флоатинг/фихед пойнт эти ошибки в общем случае неустранимы и вам останется тока молиться.

AntonI ★★★★★
()
Последнее исправление: AntonI (всего исправлений: 1)
Ответ на: комментарий от AntonI

эти ошибки в общем случае неустранимы и вам останется тока молиться

Но их и не надо устранять. Достаточно понизить их значимость до ничтожной.

anonymous
()
Ответ на: комментарий от anonymous

Да, я неправ. Достаточно сделать операцию раскидывания по счетам атомарной, с контролем суммы долей и случайным выбором того кому достанется/не хватит копейки.

Мы так шоколадки в походах делили:-)

AntonI ★★★★★
()
Ответ на: комментарий от AntonI

Думаю за помощь ТС может перевести каждому по трети копейки.

anonymous
()
Ответ на: комментарий от AntonI

Это 10**4 на пустом месте, нам самом низком уровне, где проверка вообще ничего не должна стоить.

Zeta_Gundam
()
Ответ на: комментарий от AntonI

Вообще тут есть офигенный подводный камень. Если речь о розничной торговле. И у меня есть 100 пряников общей ценой 100.50 ₽, то бишь 100 рублей и 50 копеек. Я продаю их в розницу. У меня в базе один стоит 1.005 ₽, но на ценнике 1 ₽, т.к. я не могу уходить дальше копейки. И если покупатель берёт 5 пряников, то сумма получается 5.025 ₽. По ЗоЗПП все вычисления округляются в пользу покупателя и получается 5.02 ₽. Но на ценнике-то 1 ₽. А это мошенничество – 2 копейки нарисованы с куста. Поэтому с самого начал нужно накинуть и разделить и округлить до копейки. Иначе 2С* не получится.

  • – шутка про 1С.
anonymous
()
Ответ на: комментарий от anonymous

Б-же, как же хорошо что я занимаюсь простым и понятным численным моделированием физических процессов а не вот этой всей бухгалтерией… ;-)

AntonI ★★★★★
()
Ответ на: комментарий от AntonI

Ты слушаешь фантазии идиота.

У меня в базе один стоит 1.005 ₽, но на ценнике 1 ₽, т.к. я не могу уходить дальше копейки. И если покупатель берёт 5 пряников, то сумма получается 5.025 ₽.

Если покупатель берёт 5 пряников по 1₽ каждый, то на чеке будет 1 * 5₽ = 5₽ всего. Розница торгует строго по ценнику с точностью до копейки, а свою базу тот безграмотный школьник может засунуть себе в то место, откуда вылезли авторы этого топика.

Откуда, сука, под конец года в интернет вылезло столько наглухо отбитых дегенератов? В дурдоме новогодние каникулы, чтоле.

LamerOk ★★★★★
()
Ответ на: комментарий от LamerOk

Розница торгует строго по ценнику с точностью до копейки, а свою базу тот безграмотный школьник может засунуть себе в то место, откуда вылезли авторы этого топика.

Откуда, сука, под конец года в интернет вылезло столько наглухо отбитых дегенератов? В дурдоме новогодние каникулы, чтоле.

Сам ты ценник.

anonymous
()
Ответ на: комментарий от LamerOk

Розница торгует строго по ценнику

Вы так и не поняли? Если у него получится 5 ₽, то продавец просрёт 50 коп со 100 пряников и уйдёт в минуса. Еврей корчится в муках. Правильный ответ 5.05 ₽. И на ценнике 1.01 ₽. Или ещё точнее – точность нафиг не нужна. Нужно округлять заранее в свою пользу.

anonymous
()
Ответ на: комментарий от anonymous

Судя по постам ТС он разработал API, для работы с а-ля бухгалтерскими алгоритмами

Основная проблема предложенного API - fixed in compile time number of decimals. Не взлетит. Одно это делает usability близким к нулю. Но рано или поздно ТС сам к этому придёт.

bugfixer ★★★★★
()
Ответ на: комментарий от AntonI

Нет бы цены писать с погрешностями. :)

Потом вдруг выясниться, что рост или падение ВВП меньше точности вычисления. Значит, можно любой процент писать, один хрен не проверишь.

anonymous
()
Ответ на: комментарий от anonymous

Потом вдруг выясниться, что рост или падение ВВП меньше точности вычисления.

Так не бывает.

bugfixer ★★★★★
()
Ответ на: комментарий от bugfixer

Можешь развить мысль?

fixed::p<5> * fixed::p<5> -> fixed::p<10>

Точность меняется прямо время компиляции и компилятор проверяет точность перед тем как скомпилировать программу:

fixed::p<10> * fixed::p<10> -> fixed::p<20>

Это уже не скомпилируется, надо решить проблему с математикой перед тем как запускать программу.

Не вижу где точность зафиксирована во время компиляции, она меняется, но никогда не плавает.

aleksey_tulinov
() автор топика

Я думаю, что библиотека получилась не очень в духе С++. Хочется складывать числа, и чтобы библиотека сама просигнализировала об ошибке, выбросила бы исключение, например, без всякой фигни типа кучи проверок. А тут надо всё проверять, а вдруг переполнение, а где оно произошло.

Возможно std::optional - не идеальный вариант и было бы хорошо иметь unchecked-вариант математики с игнорированием ошибок или с исключениями.

aleksey_tulinov
() автор топика
Ответ на: комментарий от aleksey_tulinov

Можешь развить мысль?

Могу. Разные продукты торгуются по разному, и это runtime параметры. Я не очень понимаю в чём смысл конкретно этого exercise, если в целях самообучения - никто же не против. Я бы даже сказал - познавательно. Но как это применить на практике - я в упор не вижу.

ПыСы. Если аудитория другая - тогда я растворяюсь в тумане. Может быть все на 1e-2 работают «вне зависимости от». Я так подозреваю на это Вы и рассчитывали.

bugfixer ★★★★★
()
Ответ на: комментарий от bugfixer

Ага, т.е. не хватает фабрики которая вернёт число с нужной точностью в рантайме. Ну в принципе можно подумать об этом, тогда же в рантайме можно при умножении опять вернуть число с другой точностью.

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

Я не очень понимаю в чём смысл конкретно этого exercise

Это экспериментальная библиотека, в ней делаются эксперименты.

aleksey_tulinov
() автор топика
Ответ на: комментарий от aleksey_tulinov

Это экспериментальная библиотека, в ней делаются эксперименты.

Это экспериментальная библиотека, в ней делаются экстрименты.

anonymous
()
Ответ на: комментарий от aleksey_tulinov

Это экспериментальная библиотека, в ней делаются эксперименты.

Я Вам пытаюсь показать «что не так». Если Вам всё равно - кто же против. Как я уже говорил - у всех уже все написано давно, и я так думаю что поэффективней будет.

bugfixer ★★★★★
()
Ответ на: комментарий от bugfixer

Как я уже говорил - у всех уже все написано давно, и я так думаю что поэффективней будет.

Может быть у ТС планируется развитие этого API …

anonymous
()
Ответ на: комментарий от aleksey_tulinov

Ага, т.е. не хватает фабрики

Не хватает API который бы позволил делать то что нужно людям, как можно более эффективно. И был бы универсален. То что Вы предложили, как бы так сказать - пионерство. Но мне даже интересно посмотреть во что оно разовьется - Вы же это просто так не оставите, я надеюсь :)

bugfixer ★★★★★
()
Ответ на: комментарий от AntonI

Будет дробь, x/y - это x/y. Точность дроби заранее неизвестна.

Из дроби надо вынуть число с какой-то точностью, в зависимости от результата деления, это число будет или точным или неточным. Результат некоторых дробей всегда неточный, например 1/3.

aleksey_tulinov
() автор топика
Ответ на: комментарий от bugfixer

Нам всем интересно посмотреть, что получится, для этого и делаем.

aleksey_tulinov
() автор топика
Ответ на: комментарий от AntonI

Да, у каждого числа есть флаг точно ли оно или нет, он ставится в рантайме и наследуется между числами. Любая операция над неточным числом сделает другое неточное число.

aleksey_tulinov
() автор топика
Ответ на: комментарий от AntonI

У числа есть флаг точное оно или неточное?

Даешь «арифметику» погрешностей!

anonymous
()

Вместо того чтобы орать «ненужно!» (кому-то, думаю, будет таки нужно), посмотрел код. Разобраться со стороны сложно, мешают не очень понятные именования (e.hpp, f.hpp, p.hpp). И вот это вот: файл opers.hpp, внутри «#ifndef FIXED_OPERATORS_HPP» (кстати, почему не «#pragma once»?), а папочка с операторами и вовсе называется ops.

Waterlaz ★★★★★
()
Ответ на: комментарий от Waterlaz

посмотрел код.

Поосторожней, развидеть бывает сложно

anonymous
()
Ответ на: комментарий от aleksey_tulinov

А Вы проверяли какой процент чисел на выходе какого нить реального алгоритма становится неточным? ИМНО их доля будет близка к 100%, так надо ли огород городить?

И я до сих пор не очень понимаю в чем профит от того что Вы можете как то рулить положением запятой;-(

то есть скажем есть фихед-пойнт занимающий 32 бита. Мы говорим - нам нужна вот такая точность, 4 двоичных знака после запятой. Допустим мы знаем что целая часть с гарантией влезает в 27 бит, один бит на знак. Все, поехали?

AntonI ★★★★★
()
Последнее исправление: AntonI (всего исправлений: 1)
Ответ на: комментарий от Waterlaz

Вместо того чтобы орать «ненужно!» (кому-то, думаю, будет таки нужно)

Камень в мой огород, я так понимаю? Ну да ладно. Логика нужная в узких областях, но не очень правильно сделана, причём imho даже на уровне интерфейсов. Но об этом я уже писал.

посмотрел код. Разобраться со стороны сложно

А это уже не в мой огород камень… Писать «простой для восприятия» код - это искусство, возможно с годами придёт.

мешают не очень понятные именования (e.hpp, f.hpp, p.hpp). И вот это вот: файл opers.hpp, внутри «#ifndef FIXED_OPERATORS_HPP» (кстати, почему не «#pragma once»?), а папочка с операторами и вовсе называется ops.

Это всё можно причесать, не в этом «цимес». Я просто пока ещё не услышал про target audience, и какие практические задачи этот код должен решать, желательно с примерами.

ПыСы. Раньше чем в выходные даже возможности посмотреть на конкретику не будет…

bugfixer ★★★★★
()
Ответ на: комментарий от AntonI

И я до сих пор не очень понимаю в чем профит от того что Вы можете как то рулить положением запятой;-(

Вот здесь, при всём моём уважении, ты не прав. Смотреть нужно трошки шире. Есть некий unit, и есть некоторое их дискретное количество. Нужно написать математику, желательно тоже дискретную, причём так чтобы наиболее частые операции были наиболее эффективны. Чтобы это правильно сделать нужно очень хорошо понимать прикладную область, а мне так до сих пор и не ответили на вопрос для чего вот это всё затевалось.

bugfixer ★★★★★
()
Ответ на: комментарий от AntonI

Не уверен, что правильно понимаю вопрос. Смысл в том чтобы предотвратить уничтожение информации. Библиотека определяет и генерирует различные ошибки.

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

aleksey_tulinov
() автор топика

Можете посмотреть и покритиковать, всё ли по феншую, может чего-то не хватает или что-то лишнее?

Лишняя библиотека целиком, если она для бухгалтерии.

Арифметика в бухе в принципе целочисленная и все проблемы должны решаться на уровне конкретных алгоритмов в каждом конкретном случае.

К примеру, пришла сумма а ее надо раскидать на налог (пусть будет 0.2) и чистую сумму.

Тогда, к примеру, налог= округление(0.2 * сумма); а чистую сумму показываем как (сумма-налог).

Всё должно сходиться до копейки и никакая заумная библиотека тут не поможет.

Psilocybe ★★★★
()
Ответ на: комментарий от bugfixer

Чтобы это правильно сделать нужно очень хорошо понимать прикладную область, а мне так до сих пор и не ответили на вопрос для чего вот это всё затевалось.

Так я ровно о том же. Пока что даже моего извращенного воображения не хвататет что бы понять куда это можно пристроить:-)

AntonI ★★★★★
()
Ответ на: комментарий от aleksey_tulinov

Вопросов тут собственно два:

  1. какая доля неточных чисел на выходе любого актуального алгоритма? Мне кажется близка к 100%, и если это так то непонятно зачем с неточностью морочиться.

  2. Чем Ваш подход лучше подхода, когда мы заранее оценив необходимую точность выделяем сколько то знаков до запятой, сколько то после запятой и работаем с 32/64 битами? В случае неточных чисел очевидно до запятой выделяем сколько надо, после запятой все остальное и имеем профит в скорости и максимально возможную для фиксед-пойнт точность.

Если говорить про библиотеку для автоматической оценки точности алгоритмов (подставил свой класс вместо чисел - скомпилял - прогнал на типовых данных - получил оценку точности ответа с т.з. ошибки округления) то это наверное интересно. Если это будет для флоатинг пойнт то я бы даже протестил.

AntonI ★★★★★
()
Ответ на: комментарий от Psilocybe

Всё должно сходиться до копейки и никакая заумная библиотека тут не поможет.

Ну вообще это можно завернуть в какую нить заумную библиотоеку и станет удобнее. А так согласен

AntonI ★★★★★
()
Ответ на: комментарий от bugfixer

Камень в мой огород, я так понимаю? Ну да ладно.

Ни в коем случае! Скорее укол в сторону общелоровского настроения.

Логика нужная в узких областях, но не очень правильно сделана, причём imho даже на уровне интерфейсов. Но об этом я уже писал.

Согласен.

Это всё можно причесать, не в этом «цимес». Я просто пока ещё не услышал про target audience, и какие практические задачи этот код должен решать, желательно с примерами.

Да, причесать можно. Я просто хотел глянуть, как оно там внутри, но увидев e.hpp, f.hpp и p.hpp, дальше разбираться обленился.

А так, видеть числовой тип, который либо гарантирует точность вычисления, либо точно знает накопившуюся ошибку, если она таки была допущена, я бы хотел.

Waterlaz ★★★★★
()
Последнее исправление: Waterlaz (всего исправлений: 1)
Ответ на: комментарий от Waterlaz

А так, видеть числовой тип, который либо гарантирует точность вычисления

Для этого нужно оставаться в домене rationals - медленно, но верно.

либо точно знает накопившуюся ошибку, если она таки была допущена

«Точно» здесь ключевое слово. Если интересует только оценка сверху - rationals можно обогнать.

bugfixer ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.