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)

use decimal64 decimal128 и не ипите мозг сами себе.

перевожу - есть стандарты, лучше использовать(и реализовывать) их..там даже местами аппаратная поддержка

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

use decimal64 decimal128 и не ипите мозг сами себе.

Так это плавающая точка, а не фиксированная!

annulen ★★★★★
()

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

Для чего такой колхоз вместо исключений?

no-such-file ★★★★★
()

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

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

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

Скорость

Скорость чего? Если возникло переполнение, то вычисление можно выкинуть на помойку и всё что от него зависит тоже. Это всё равно что на ноль делить (буквально, деление на ноль гарантированно вызывает переполнение). Если уж хочется без исключений, то нужно реализовать бесконечность, не костыль втыкать.

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

Скорость чего? Если возникло переполнение, то вычисление можно выкинуть на помойку и всё что от него зависит тоже.

Это не так работает. Ты можешь колхозить арифметику больших чисел используя проц с 8-битным регистром например, который ну никак в лоб не может такие числа считать. Строго говоря её так и делают, просто прячут от тебя детали реализации. Но да, там думать надо и понимать что делаешь, а ошибки дадут непредсказуемую лажу.

peregrine ★★★★★
()

Я всегда к инт приводил. Зачем плодить лишние сущности?

Psilocybe ★★★★
()

Лучше всего реализовать длинные числа. Как в питончике. Для начала можно только один вид, целые там или вещественные. Также хорошо было бы иметь либу натуральных дробей. В современых калькуляторах дроби натуральные уже есть, а вот в стандартные библиотеки пока не завезли. (Я не говорю про математические языки или библиотеки, не сталкивался, речь только о стандартных средствах)

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

Молодцы конечно. Только вот плюсы это зло - надеюсь вы знали об этом.

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

Никакой скорости там быть не может. Если они возвращают optional, то они уже делают проверку на переполнение – возьмем наличие этой проверки за baseline.

Далее они возвращают optional – результат + флаг. Пользователь вынужден проверять флаг, чтобы узнать статус результата – но этот статус уже был известен, пользователь выполняет лишнюю работу.

Исключения в С++ бесплатны на happy path. Если бы их заботила скорость, они бы использовали исключения. Но, конечно, лучше копировать функциональщину, в которой принято делать вид, что исключений не существует.

Siborgium ★★★★★
()

все операции возвращают std::optional

Т.е. с вашими числами нельзя писать формулы, a = b*c + d? Вроде бы даже у, прости господи, финансистов в формулах обычно больше одного оператора. И вообще, получается, нельзя этот fixed передать шаблону вместо double, чтобы все продолжило работать, но уже с новой арифметикой.

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

Лучше всего реализовать длинные числа.

Всё уже давно есть в boost multiprecision и во многих других либах. Чем ещё одна либа будет лучше? Пока нет ответа на этот вопрос, то лучше даже не начинать что-то реализовывать.

fsb4000 ★★★★★
()

Библиотека спроектирована так чтобы ни один бит информации ни при каких манипуляциях не терялся бы, либо если бит надо потерять, то это надо сделать явно и число станет неточным

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

Zeta_Gundam
()

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

Для чего сделали? Для DSP?

Тогда у меня для тебя плохие новости:

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

А должно быть округление и предупреждение, как максимум.

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

Ну и зря! man saturated arithmetic

shkolnick-kun ★★★★★
()

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

На практике нужен формат fix16_t (число 32 бит, дробная часть 16 бит).

shkolnick-kun ★★★★★
()
Ответ на: комментарий от fsb4000

Так Вы из ФСБ ?!

Господин начальник не ругайте меня пожалуйста!

pup_kin
()

Вот если бы сделали float16, - это было бы нужно.

А для фиксированной точки есть куча либ

shkolnick-kun ★★★★★
()
Ответ на: комментарий от peregrine

Боюсь все плохо.

Когда нам нужна скорость и фихед пойнт, то мы просто и тупо работаем с целыми. Переполнение - алгоритм и код строится так, что бы его не могло быть.

Но кстати от такого переполнения как минимум одна ракета у европейцев упала.

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

все операции возвращают std::optional

Т.е. с вашими числами нельзя писать формулы, a = b*c + d?

То что std::optional неудобно использовать в составных операциях — это в общем-то решаемая проблема (и давно решённая в том же Haskell). То есть претензии здесь д.б. не к автору либы, который сделал всё по STL фен-шую, а к самому языку. И уже в C++20, если очень захотеть, можно [1] делать красиво

[1] https://www.reddit.com/r/cpp/comments/6ly4rz/it_had_to_be_done_abusing_co_awa...

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

Ой, блин, я не знал про std::decimal. Это вроде действительно плавающая точка, но надо ознакомиться, да, спасибо.

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

https://www.boost.org/doc/libs/1_73_0/libs/multiprecision/doc/html/boost_multiprecision/tut/mixed.html

mpfr_float_50         a(2), b;
mpfr_float_100        c(3), d;
...
d = a * c;  // OK, result of operand is an mpfr_float_100.

По-моему тут результатом должен быть mpfr_float_150, при умножении точность складывается. fixed определит точность результата сам во время компиляции, т.е. такое даже не скомпилируется, или не должно скомпилироваться.

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

Но ознакомимся, спасибо. Были идеи вкорячить более широкие числа в качестве хранилища, ну вот например как 256-битные инты из буста. Но когда-нибудь потом.

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

Идея была в том, что можно сделать

expect_is_accurate(x + y);

И внутри expect_is_accurate() выбросить исключение, или вызвать std::abort(), как вариант. Про выбрасывание исключений - принято, какой-нибудь detail::expect() в отдалённых размытых планах, или нет, надо подумать.

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

Т.е. с вашими числами нельзя писать формулы, a = b*c + d?

Можно, операторы поддерживают опшиналы и сами их разименуют предварительно проверив. Если один из операндов сравнивается с false, то результатом операции опять будет пустой std::optional.

Тут засада в том, что a / b вернёт не число, а дробь, из которой надо достать число с нужной точностью, и только потом продолжать вычисления. Но b*c + d должно работать, если не работает, то это баг какой-то.

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

Или я не так понял, или fixed не про это. Выделение бит на целую и дробную части - для этого действительно есть много библиотек. Тут десятичная точность, на дробную часть выделяются десятичные знаки, а не биты.

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

Рекомендую ознакомиться, например, с toml++. Если исключения доступны (не отключены флагом компилятора или специальным дефайном), то используются именно они, в противном случае возвращается result – результат, ошибка и соотв. флаг.

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

что со спецфункциями?

Ничего, только базовые операции. А что за спецфункции, синус, косинус, тангенс или что-то другое?

область применения?

Считать деньги.

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

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

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

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

область применения?

Считать деньги.

Вы - дебилы.

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

А что за спецфункции, синус, косинус, тангенс или что-то другое?

Да.

Считать деньги.

Я не очень понимаю, там разве точность плавает?

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

спецфункции

Пока не планировали.

Я не очень понимаю, там разве точность плавает?

Так и в fixed вроде не плавает. Пацаны, у нас какие-то непонятки. Внутри fixed только целые числа, флоатов нет ни одного, на целых числах представлены числа с фиксированной запятой.

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

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

это же целочисленные операции

Под капотом да, все операции целочисленные.

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

Да ладно, fixed-point arithmetic – страница кода на любом языке. Базовые операции, разумеется. Без логарифмов и синусов.

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

Молодцы конечно. Только вот плюсы это зло - надеюсь вы знали об этом.

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

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

Далее они возвращают optional – результат + флаг. Пользователь вынужден проверять флаг, чтобы узнать статус результата – но этот статус уже был известен, пользователь выполняет лишнюю работу.

Исключения в С++ бесплатны на happy path. Если бы их заботила скорость, они бы использовали исключения. Но, конечно, лучше копировать функциональщину, в которой принято делать вид, что исключений не существует.

Тебе сказали – не хочешь, не проверяй, это не исключительная ситуация. Исключения – тормозное дерьмо и они вообще не про это. Переполнение есть и в обычной арифметике. Обычное дело.

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

И скока будет 1/2+1/2?:-)

Гдэ то сэм, восэм …

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

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

чтобы все были довольны

Хочешь полирнуть сразу всем?

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

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

ТруТайп свои координаты в фиксед-пойнт хранит. Для компактности и скорости. И точки. Не только финики.

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

Это сложение дробей, оно в такой записи не поддерживается. Из дроби 1/2 надо достать число с какой-то точностью, затем сложить.

Как достанете - столько и получится. Если обе дроби получить с одним десятичным знаком (0.5) и сложить, то должно быть 1.0 тоже с одним знаком, число должно быть точным.

Если одно из чисел достать с нулём знаков после запятой и сложить, то получится 0.5 и число будет неточным. Если обе дроби достать с нулевой точностью, то должен получиться неточный 0..

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

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

Кажется на Си это делается макросом. Фиксированная точка. Мнимая запятая…

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

Такие вещи, если важна точность, надо делать в рациональных числах.

В бухгалтерских алгоритмах это неизбежно, так как используются значения разной точности.

В SQL операции над значениями разной точности так вообще отдельная вселенная с своими правилами округления и вычисления выражений …

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