Re[13]: Fixed-point to string
От: Alekzander  
Дата: 01.09.23 20:40
Оценка: :)
Здравствуйте, CreatorCray, Вы писали:

A>>А удивляет меня, что под контроллер без FPU кто-то пишет компилятор современного C++

CC>Меня удивляет что тебя это удивляет.

Когда я последний раз программировал под процессор без аппаратной поддержки плавающей точки — и могу подтвердить, что эмуляция, слава богу, доступная в компиляторе, была охрененно медленной — это было такое убожество. А сам я ещё в школу ходил. (Кстати, процессор тот назывался 80286, а компилятор — Turbo Pascal). Я думал, что с тех пор или чипы в целях экономии клепают всё по тем же чертежам и пишут на Си, или уж если делают новое, то с FPU. Гармонично как-то всё должно быть — и процессор, и компилятор, и мысли, и душа

Что хоть за камень-то такой, если не секрет?
Re[12]: Fixed-point to string
От: cppguard  
Дата: 01.09.23 22:17
Оценка: 2 (1) +1
Здравствуйте, Alekzander, Вы писали:

A>Я думаю, ты понимаешь вопрос, а не понимаешь, что меня удивляет. А удивляет меня, что под контроллер без FPU кто-то пишет компилятор современного C++ (какой, кстати? 17? 20?). Я бы дал голый Си и наказ не выёживаться.

A>Возможно, в этом есть какой-то смысл, но он от меня ускользает.

Так никто в 2023-ем не пишет компилятор с нуля. Есть frontend, есть backend. В какой-то момент написали backend для AVR, архитектура там несложная А вот всё новое в С++17, 20, 23, 100500 это как раз больше работа над frontend. Но справедливости ради стоит отметить, что с 2020-го ходят слухи о том, чтобы убрать эту ветку из gcc, ибо stm32 всё больше захватывает рынок, а желающих выжимать какждый байт из avr всё меньше. Видимо, avr ждёт судьба pic.
Re[12]: Fixed-point to string
От: cppguard  
Дата: 01.09.23 22:24
Оценка:
Здравствуйте, CreatorCray, Вы писали:

CC>Насколько много там было выполнено в compile time для этого кода?


Если передавать константу, то получается 100% compile-time, если переменную, то получается unfold. Другой вопрос, что цикл, вероятно, был бы оптимальнее по размеру, ибо вывод в строку не является узким местом и не будет вызываться часто. Но приминительно к микроконтроллерам вычисления compile-time удобны для других вещей. Например, зная тактовую частоту, можно посчитать prescaler.
Re[5]: Fixed-point to string
От: andyp  
Дата: 11.09.23 22:59
Оценка:
Здравствуйте, cppguard, Вы писали:


C>Вот взять реальный пример. Есть даные магнитометра — XYZ, нужно перевести эти значения в углы Эйлера. Математика вся с фиксированной точкой, и глазами отлаживать удобно, смотря именно на десятичные значения, потому что в голове идёт привязка к радианам. Я уверен, что это лишь вопрос надроча, но для себя я пока не представляю отладку математики в шеснадцатеричных значениях.


Ну так отладь на большом компьютере, потом перетащи на контроллер и прогони тесты, чтобы совпадало с большим.
Re[6]: Fixed-point to string
От: andyp  
Дата: 11.09.23 23:00
Оценка:
Здравствуйте, sergii.p, Вы писали:

SP>что-то не верится, а пруфы будут?


Для 64битных прог скорее не будет
Re[8]: Fixed-point to string
От: T4r4sB Россия  
Дата: 25.09.23 11:59
Оценка:
Здравствуйте, netch80, Вы писали:

N>Это
Автор: netch80
Дата: 01.12.16
, наверно?

N>А ещё на хабре.

Сорян за запоздалый оффтоп. Но я прочитал каменты на хабое и у меня сгорела жопа.
Сколько же даунов без мозга пишет "надо просто добавить эпсилон". И плюсует. И еще язвит типа автор лох такой крутой геометр а про эпсилон не знает. Тупо мантру повторяют не понимая смысла. Дебилы
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re: Fixed-point to string
От: fk0 Россия https://fk0.name
Дата: 28.09.23 07:44
Оценка:
Здравствуйте, cppguard, Вы писали:

C>Накидал тут на коленке код для вывода вещественных чисел с фиксированной запятой в привычном виде. Вышло как-то слишком просто. Имеет право на жизнь?


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

C>template<typename T, unsigned N>

C>class Q {
C> T value;
C>public:
C> Q(T value): value(value) {}
C> T integral() const { return value >> N; }
C> T fractional() const { return value & (1 << N — 1); }
C>};

Вот здесь 1 — это int, а sizeof(value) может быть меньше чем sizeof(intmax_t).

А что если N задали нулевым? Можно же!

А что если value -- знаковое. Сдвиг вправо может быть с сохранением знака.
А может быть без.

C>template<unsigned Position, unsigned Multiplier = 5>

C>struct FractToString {
C> static unsigned value(unsigned value, unsigned acc = 0) {
C> auto d = (value >> (Position — 1) & 1) * Multiplier;
C> return FractToString<Position — 1, Multiplier * 5>::value(value, acc * 10 + d);
C> }
C>};

Такой параметр шаблона по-умолчанию -- источник ошибок.

Опять же Position = 0 и UB.

Почему пятёрка захардкожена, что она значит?

Может быть переполнение в acc. Вообще непонятно зачем здесь какие-то рекурсивные вычисления,
по-моему всё сводится к одной линейной арифметической операции и отправке числа в ostream/snprintf/std::format.

Вообще для ввода-вывода (где перформанс не критичен) не зазорно сконвертировать в double.
Если конечно эти фиксированные числа туда влазят.

Вообще почему именно "числа с фиксированной точкой", а не рационалные дроби? Кажется не нужна эта
фиксированная точка вовсе. Проще тупо считать всегда в целых, домножив всё на некий коэффициент K
чтоб значения не утонули в шумах квантизации. А потом при выводе обратно поделить на K. В принципе
число с фиксированной точкой это оно и есть, только там K -- обязательно 2^N. Но это же совершенно
не обязательно. Хотя и удобно для быстрого деления. Но давно уже не обязательно: современные процессоры
уж точно наверняка имеют быстрый умножитель, с помощью которого деление или умножение на константу
делается быстро.
Re[2]: Fixed-point to string
От: cppguard  
Дата: 28.09.23 23:36
Оценка:
Здравствуйте, fk0, Вы писали:

fk0> Вот здесь 1 — это int, а sizeof(value) может быть меньше чем sizeof(intmax_t).

И? Каким боком sizeof тут?

fk0> А что если N задали нулевым? Можно же!

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

fk0> А что если value -- знаковое. Сдвиг вправо может быть с сохранением знака.

fk0>А может быть без.
Может. А ещё value может быть классом, ссылкой и т.д.


fk0> Такой параметр шаблона по-умолчанию -- источник ошибок.

А какой — не источник?

fk0> Опять же Position = 0 и UB.

Но зачем?

fk0> Почему пятёрка захардкожена, что она значит?

Она означает множитель, который изначально равен 5.

fk0> Может быть переполнение в acc. Вообще непонятно зачем здесь какие-то рекурсивные вычисления,

fk0>по-моему всё сводится к одной линейной арифметической операции и отправке числа в ostream/snprintf/std::format.
Предлагаешь длинную арифметику вводить для acc? ostream/snprintf/std::format — ничего из этого не умеет выводить fixed-point.

fk0> Вообще для ввода-вывода (где перформанс не критичен) не зазорно сконвертировать в double.

Боюсь, что конвертация fixed-pont -> floating-point может выйти ещё тяжелее чем вывод в строку.

fk0> Вообще почему именно "числа с фиксированной точкой", а не рационалные дроби? Кажется не нужна эта

fk0>фиксированная точка вовсе. Проще тупо считать всегда в целых, домножив всё на некий коэффициент K
fk0>чтоб значения не утонули в шумах квантизации. А потом при выводе обратно поделить на K. В принципе
fk0>число с фиксированной точкой это оно и есть, только там K -- обязательно 2^N. Но это же совершенно
fk0>не обязательно. Хотя и удобно для быстрого деления. Но давно уже не обязательно: современные процессоры
fk0>уж точно наверняка имеют быстрый умножитель, с помощью которого деление или умножение на константу
fk0>делается быстро.
Смешались в кучу кони-люди. Фиксированная точка удобна там, где вычисления находятся в одном диапазоне, а аппаратной поддержки float/double нет. Рациональные числа вообще никак не решают проблему, потому что переполнение с числителе или знаменетеле так и останется переполнением.
Re[3]: Fixed-point to string
От: fk0 Россия https://fk0.name
Дата: 29.09.23 09:42
Оценка:
Здравствуйте, cppguard, Вы писали:

C>Здравствуйте, fk0, Вы писали:


fk0>> Вот здесь 1 — это int, а sizeof(value) может быть меньше чем sizeof(intmax_t).

C>И? Каким боком sizeof тут?

Сдвигая единицу влево на сколько угодно, она так и остаётся интом.
Если инт 32-битный, то сдвигать на 62 влево бессмысленно -- UB.
А сдвиг инта на 63 тоже UB ибо знаковый...

fk0>> А что если N задали нулевым? Можно же!

C>Можно, конечно. Ещё можно комп облучить, и тогда некоторые биты в памяти поменяют своё значение.

Можно хотя бы статик ассерт написать. Чтоб не ловить потом UB в продакшене.
Потому, что ноль туда может прийти неочевидным образом.

fk0>> А что если value -- знаковое. Сдвиг вправо может быть с сохранением знака.

fk0>>А может быть без.
C>Может. А ещё value может быть классом, ссылкой и т.д.

Довод за концепты. А то чё там будет с std::ostream вообще жутко представить.
Можно хотя бы enable_if написать и/или static_assert опять же.

fk0>> Такой параметр шаблона по-умолчанию -- источник ошибок.

C>А какой — не источник?

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

fk0>> Опять же Position = 0 и UB.

C>Но зачем?

В любом ПО существуют ошибки. Или даже не ошибки, а просто граничные условия не продуманы.

fk0>> Почему пятёрка захардкожена, что она значит?

C>Она означает множитель, который изначально равен 5.

Любое волшебное число в коде, кроме 1, 0, 1000, 1000000 и т.п. -- источник ошибок.
И на код ревью сразу нужно спрашивать, почему именно 5, а не 42. Что это значит.
И если это что-то значит, то может быть стоит завести именнованную константу с этим числом.

fk0>> Может быть переполнение в acc. Вообще непонятно зачем здесь какие-то рекурсивные вычисления,

fk0>>по-моему всё сводится к одной линейной арифметической операции и отправке числа в ostream/snprintf/std::format.
C>Предлагаешь длинную арифметику вводить для acc? ostream/snprintf/std::format — ничего из этого не умеет выводить fixed-point.

Я длинной арифметики тут не увидел. Она спрятана за типом T? Опять же довод за то, что нужны
концепты, enable_if, std::detected, std::is_base_of хотя бы. Чтоб было вообще понятно, что T
это вовсе не любой T, а подмножество таких T обладающих определёнными свойствами. И опять же
чтоб по ошибке не подсунуть чёрти что и долго не удивляться.

fk0>> Вообще для ввода-вывода (где перформанс не критичен) не зазорно сконвертировать в double.

C>Боюсь, что конвертация fixed-pont -> floating-point может выйти ещё тяжелее чем вывод в строку.

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

fk0>> Вообще почему именно "числа с фиксированной точкой", а не рационалные дроби? Кажется не нужна эта

fk0>>фиксированная точка вовсе. Проще тупо считать всегда в целых, домножив всё на некий коэффициент K
fk0>>чтоб значения не утонули в шумах квантизации. А потом при выводе обратно поделить на K. В принципе
fk0>>число с фиксированной точкой это оно и есть, только там K -- обязательно 2^N. Но это же совершенно
fk0>>не обязательно. Хотя и удобно для быстрого деления. Но давно уже не обязательно: современные процессоры
fk0>>уж точно наверняка имеют быстрый умножитель, с помощью которого деление или умножение на константу
fk0>>делается быстро.
C>Смешались в кучу кони-люди. Фиксированная точка удобна там, где вычисления находятся в одном диапазоне, а аппаратной поддержки float/double нет. Рациональные числа вообще никак не решают проблему, потому что переполнение с числителе или знаменетеле так и останется переполнением.

Ничего не смешалось, я прекрасно представляю проблемную область и утверждаю, что фиксированная точка
особенного смысла не имеет. Все вычисления делятся на в общем-то три следующих класса:

1. целочисленные;
2. плавающая точка;
3. целочисленные вычисления с неограниченной разрядностью
(библиотеки bignum и т.п., обычно криптография, бухгалтерия с числами в ASCII);
4. экзотические системы счисления: фиббоначиева, логарифмическая и т.п.

Фиксированная точка -- по сути это первое (или третье). И в ней именно как в "фиксированной точке" нет никакого смысла,
это именно то что я сказал: данные домножены на 2^N и младшие биты это именно то, что после точки. Называть
это специальным термином, как-то обособлять нет смысла. Вообще смысла думать о точке смысла нет. Повторюсь,
что домжножать можно на любое K не являющееся 2^N в общем случае. И о какой-то точке вообще не задумываться.
Вместо точки рациональная дробь (x/K), где K можно вообще вынести за скобки и только на выводе домножить
на 2^N/К и распечатать потом результат поделённый на 2^N, точку, и результат по модулю 2^N — 1. Если вообще
кому-то эта точка нужна. Можно просто оперировать всегда целыми числами домноженными в голове на K и забыть
про точку.

Обычно рациональные числа идут в комплекте с bignum, так что переполнения там нет. Только для практических
задач оно бессмысленно: для криптографии все операции по-модулю, для научно-технических задач плавающая
точка подходит лучше. А в бухгалтерии таких денег никто не видел.

И кстати, плавающая точка может оказаться БЫСТРЕЙ фиксированной точки. И часто оказывается.
Могу рассказать историю, как я заморачивался с вычислениями с фиксированной точкой на микроконтроллере.
С теми же соображениями. Но в 16 бит не влезает ничего толком, значит надо 32. И потом оказалось, что
32-битное целочисленное решение тот же алгоритм считает МЕДЛЕНЕЙ, чем с программной реализацией операций
с плавающей точкой. Как так? Да просто: в плавающей точке 24 бита считать надо, а не 32. А контроллер
вообще 8-битный, он 32 бит не за одну, а за 4-8 операций сложит только. С перемножением ещё хуже: там
32 бита перемножить сильно сложней, чем 24 бита мантиссы. А порядок же тупо складывается при перемножении,
быстро, одной инструкцией. Зато нормализация при сложении медленей. Но в целом -- обогнало.
На 32-битном контроллере конечно целочисленные вычисления быстрей, но тоже могут быть нюансы: если есть
FPU, то вычисления с плавающей точкой могут оказаться быстрей просто потому, что блок FPU работает
параллельно с целочисленным АЛУ. И компилятор распараллелит инструкции так, что в целом всё быстрей
посчитается, чем только на одном целочисленном АЛУ.
Re[4]: Fixed-point to string
От: cppguard  
Дата: 29.09.23 18:55
Оценка:
Здравствуйте, fk0, Вы писали:

fk0>Здравствуйте, cppguard, Вы писали:


fk0> Сдвигая единицу влево на сколько угодно, она так и остаётся интом.

fk0>Если инт 32-битный, то сдвигать на 62 влево бессмысленно -- UB.
fk0>А сдвиг инта на 63 тоже UB ибо знаковый...
Ок, я понял идею. Для целевой платформы изпользовать что-то кроме uint8_t не имеет смысла, потому что аппаратное умножение фиксированной точки есть только для однобайтных чисел.

fk0> Можно хотя бы статик ассерт написать. Чтоб не ловить потом UB в продакшене.

fk0>Потому, что ноль туда может прийти неочевидным образом.
fk0>Можно хотя бы enable_if написать и/или static_assert опять же.
Minimal reproducible example.

fk0> Интерфейсы должны быть очевидные и по-возможности не допустать неправильного использования.

fk0>В том числе неявным образом. Потому, что шаблон могут передать в другой шаблон и там инстанцировать
fk0>с третьим шаблоном и всё это может быть сделано из макроса. И на каждом этапе вроде очевидно, а потом
fk0>нашла коса на камень. И гораздо лучше если оно не компилируется сразу, чем потом глючит.
Ну, не работает это в С++. Я могу с полпинка сломать STL, передав неправильный параметр шаблону. Инструменты нужно использовать с умом, а не бить молотком по пальцу и ругаться, что больно. Из-за вот этой вот нездоровой гонки за мнимой типобезопасностью в С++ язык стал такой уровдливый и неповоротливий — UB на каждый чих.


fk0> Любое волшебное число в коде, кроме 1, 0, 1000, 1000000 и т.п. -- источник ошибок.

fk0>И на код ревью сразу нужно спрашивать, почему именно 5, а не 42. Что это значит.
fk0>И если это что-то значит, то может быть стоит завести именнованную константу с этим числом.
Это просто тупо и попахивает слепым следованием "священным заветам". Pi — тоже константа, предложишь и её сделать какой-то именованой? Типа не Pi, а HalfAroundCircleInUnitsThatCorrespondToDegreesWithMultiplier57? В данном случае 5 это константа, которая выводится после оптимизации алгоритма вывода числа. Как её ещё можно назвать кроме как "множитель"?


fk0> Это не принципиально, если только где-то не распечатываются гигабайты строк в секунду.

Это принципиально, если на каждый цикл нужно выводить число на экран. Речь про embedded, тут нельзя написать тысячу строк, загрузив ЦП на пару секунд, и сказать, что наш super-senior software donnut eater так видит.

fk0> 1. целочисленные;

fk0> 2. плавающая точка;
fk0> 3. целочисленные вычисления с неограниченной разрядностью
fk0> (библиотеки bignum и т.п., обычно криптография, бухгалтерия с числами в ASCII);
fk0> 4. экзотические системы счисления: фиббоначиева, логарифмическая и т.п.

fk0>Если вообще

fk0>кому-то эта точка нужна. Можно просто оперировать всегда целыми числами домноженными в голове на K и забыть
fk0>про точку.

fk0> Обычно рациональные числа идут в комплекте с bignum, так что переполнения там нет. Только для практических

fk0>задач оно бессмысленно: для криптографии все операции по-модулю, для научно-технических задач плавающая
fk0>точка подходит лучше. А в бухгалтерии таких денег никто не видел.
Сам предложил рациональные числа, сам ответил, что из-за bignum не имеет смысла их использовать

fk0> И кстати, плавающая точка может оказаться БЫСТРЕЙ фиксированной точки. И часто оказывается.

fk0>Могу рассказать историю, как я заморачивался с вычислениями с фиксированной точкой на микроконтроллере.
fk0>С теми же соображениями. Но в 16 бит не влезает ничего толком, значит надо 32. И потом оказалось, что
fk0>32-битное целочисленное решение тот же алгоритм считает МЕДЛЕНЕЙ, чем с программной реализацией операций
fk0>с плавающей точкой. Как так? Да просто: в плавающей точке 24 бита считать надо, а не 32. А контроллер
fk0>вообще 8-битный, он 32 бит не за одну, а за 4-8 операций сложит только. С перемножением ещё хуже: там
fk0>32 бита перемножить сильно сложней, чем 24 бита мантиссы. А порядок же тупо складывается при перемножении,
fk0>быстро, одной инструкцией. Зато нормализация при сложении медленей. Но в целом -- обогнало.
fk0>На 32-битном контроллере конечно целочисленные вычисления быстрей, но тоже могут быть нюансы: если есть
fk0>FPU, то вычисления с плавающей точкой могут оказаться быстрей просто потому, что блок FPU работает
fk0>параллельно с целочисленным АЛУ. И компилятор распараллелит инструкции так, что в целом всё быстрей
fk0>посчитается, чем только на одном целочисленном АЛУ.
Всё попахивает непониманием матчасти. Fixed-point это обычные целочисленные вычисления с одним нюансом: при умножении нужно делать сдвиг. В моём случае у AVR есть инструкция, которая делает умножение и сдвиг за то же время, что и обычное умножение. Так что ни при каких условиях (кроме ASIC и/или наличия супер-скалярной архитектуры) не может fixed-point быть медленнее floating-point.
Re[5]: Fixed-point to string
От: fk0 Россия https://fk0.name
Дата: 30.09.23 09:41
Оценка:
Здравствуйте, cppguard, Вы писали:

C>Здравствуйте, fk0, Вы писали:


fk0>>Здравствуйте, cppguard, Вы писали:


fk0>> Сдвигая единицу влево на сколько угодно, она так и остаётся интом.

fk0>>Если инт 32-битный, то сдвигать на 62 влево бессмысленно -- UB.
fk0>>А сдвиг инта на 63 тоже UB ибо знаковый...
C>Ок, я понял идею. Для целевой платформы изпользовать что-то кроме uint8_t не имеет смысла, потому что аппаратное умножение фиксированной точки есть только для однобайтных чисел.

Но компилятор же небось инлайнит как (a*(2^8) + b) * (c*(2^8) + d) = a*c*(2^16) + (b*c + a*d)*(2^8) + b*d
(4 умножения, два сдвига и три сложения). Или по крайней мере вызывает встроенную функцию.
Последнее кстати плохо (т.к. регистровый аллокатор уже не понимает какие регистры испортятся и вынужден
что попало заталкивать в стек).

А какой смысл в "фиксированной точке" и 8-битных числах??? Любые практические данные
которые потребуется обрабатывать едва ли влезут в 16 бит. Хорошо бы иметь 24. А 32 может быть даже много.
Звук например. Сам-то прекрасно в 12 бит влезает, но какая-то обработка в два раза больше потребует.
Например, цифровой фильтр который выделяет узкую полосу частот. Помню сигнал с акселерометра обсчитывал.
Там и 8 бит на входе более чем, но нужно же считать векторные произведения, потом тригонометрические функции,
в итоге 16 не хватает для получения приемлемой точности. Вот 24 в самый раз.

fk0>> Интерфейсы должны быть очевидные и по-возможности не допустать неправильного использования.

fk0>>В том числе неявным образом. Потому, что шаблон могут передать в другой шаблон и там инстанцировать
fk0>>с третьим шаблоном и всё это может быть сделано из макроса. И на каждом этапе вроде очевидно, а потом
fk0>>нашла коса на камень. И гораздо лучше если оно не компилируется сразу, чем потом глючит.
C>Ну, не работает это в С++. Я могу с полпинка сломать STL, передав неправильный параметр шаблону. Инструменты нужно использовать с умом, а не бить молотком по пальцу и ругаться, что больно. Из-за вот этой вот нездоровой гонки за мнимой типобезопасностью в С++ язык стал такой уровдливый и неповоротливий — UB на каждый чих.

Просто нужно нормально писать код. Что на C, что на C++. Со временем пишешь сам так, что компилятор
варнингов даже не даёт. Обычный набор варнингов для GCC/Clang: -Wall -Wextra -Wcast-align -fstrict-aliasing,
ещё -Wconversion, но не все, от проекта зависит. И полезно прогонять с -fsanitize=undefined.

fk0>> Любое волшебное число в коде, кроме 1, 0, 1000, 1000000 и т.п. -- источник ошибок.

fk0>>И на код ревью сразу нужно спрашивать, почему именно 5, а не 42. Что это значит.
fk0>>И если это что-то значит, то может быть стоит завести именнованную константу с этим числом.
C>Это просто тупо и попахивает слепым следованием "священным заветам". Pi — тоже константа, предложишь и её сделать какой-то именованой?

Нет, это попахивает полезными практиками промышленного программирования.
Потому, что когда ты будешь вбивать число пи руками в код, то можешь на одну циферку
где-то недалеко от точки ошибиться. И никто не заметит даже. А потом Луна-25 улетает
на полной скорости в Луну. И ошмётков не соберёшь. Потому, что орбиту с неправильным пи
посчитало тоже неправильно.

Число пи, кстати, в стандартной C/C++-библиотеке существует как константа M_PI.
И в C++20 там ещё массу всяких констант добавили.

C> Типа не Pi, а HalfAroundCircleInUnitsThatCorrespondToDegreesWithMultiplier57?

C> В данном случае 5 это константа, которая выводится после оптимизации алгоритма вывода числа. Как её ещё можно назвать кроме как "множитель"?

Мне как потенциальному код-ревьюверу, например, совершенно непонятно, почему 5, а не 6 или не 4.

fk0>> Это не принципиально, если только где-то не распечатываются гигабайты строк в секунду.

C>Это принципиально, если на каждый цикл нужно выводить число на экран. Речь про embedded, тут нельзя написать тысячу строк, загрузив ЦП на пару секунд, и сказать, что наш super-senior software donnut eater так видит.

Нет. Каждый раз когда хотят выводить в цикле число на экран делают какую-то ерунду.
Потому, что экран никто не будет читать 100500 раз в секунду. Это даже монитор не покажет,
он обновляется только 60 раз в секунду. И если речь именно об экране, то чаще чем 60 раз в секунду
(а практически, чаще чем 5 раз в секунду -- человек не прочитает) обновлять информацию нет смысла.
И если цикл короче сотни миллисекунд, то нет никакого смысла выводить в каждом цикле. Есть смысл
в очередном цикле подумать а нужно ли выводить, и выводить только если времени с предыдущего вывода
прошло более 100мс, например. Или вовсе выводить информацию независимо, по таймеру, просто тупо 5
раз в секунду. Независимо от циклов. Из переменной в памяти, в которую можно складывать хоть 100500
раз в секунду. Разумеется не строки, а ещё числа.

fk0>> И кстати, плавающая точка может оказаться БЫСТРЕЙ фиксированной точки. И часто оказывается.

fk0>>Могу рассказать историю, как я заморачивался с вычислениями с фиксированной точкой на микроконтроллере.
fk0>>С теми же соображениями. Но в 16 бит не влезает ничего толком, значит надо 32. И потом оказалось, что
fk0>>32-битное целочисленное решение тот же алгоритм считает МЕДЛЕНЕЙ, чем с программной реализацией операций
fk0>>с плавающей точкой. Как так? Да просто: в плавающей точке 24 бита считать надо, а не 32. А контроллер
fk0>>вообще 8-битный, он 32 бит не за одну, а за 4-8 операций сложит только. С перемножением ещё хуже: там
fk0>>32 бита перемножить сильно сложней, чем 24 бита мантиссы. А порядок же тупо складывается при перемножении,
fk0>>быстро, одной инструкцией. Зато нормализация при сложении медленей. Но в целом -- обогнало.
fk0>>На 32-битном контроллере конечно целочисленные вычисления быстрей, но тоже могут быть нюансы: если есть
fk0>>FPU, то вычисления с плавающей точкой могут оказаться быстрей просто потому, что блок FPU работает
fk0>>параллельно с целочисленным АЛУ. И компилятор распараллелит инструкции так, что в целом всё быстрей
fk0>>посчитается, чем только на одном целочисленном АЛУ.
C>Всё попахивает непониманием матчасти. Fixed-point это обычные целочисленные вычисления с одним нюансом: при умножении нужно делать сдвиг. В моём случае у AVR есть инструкция, которая делает умножение и сдвиг за то же время, что и обычное умножение. Так что ни при каких условиях (кроме ASIC и/или наличия супер-скалярной архитектуры) не может fixed-point быть медленнее floating-point.

См. выше и читай ещё раз. Алгоритмы с плавающей точкой используют мантиссу меньшего размера, чем
алгоритмы с целыми числами. Потому, что у них есть байт порядка. А целочисленные алгоритмы вынуждены
использовать больше разрядов, иначе им просто большие числа не представить. И мантисса меньшего размера
перемножаться может быстрей, чем более длинные целочисленные числа. Что тут непонятного? Конечно это
справедливо для 8/16-битных процессоров с медленным умножением. А на 64-битном всё будет наоборот.

Сдвиг при умножении это всего лишь нормализация. Которая на самом деле не имеет смысла на каждой операции
в алгоритме: совершенно не важно, что в середине вычислений в промежуточном результате единица предствляется
именно единицей, а не как 65536. Можно всё посчитать и в конце нормализовать. Или только на отдельных
этапах вычислений (чтоб умещалось в разрядность). На каждом шаге это делать -- просто дурость. Я потому и говорю
нет смысла говорить о "фиксированной точке", как о чём-то особенном, проще сразу считать в целых числах.
Помимо прочего, из-за нормализации промежуточных результатов запросто получится потеря точности потому, что
промежуточные результаты могут быть плохо представимы с фиксированной точкой, там может очень большие или очень
маленькие значения выходить будут.
Re[6]: Fixed-point to string
От: cppguard  
Дата: 01.10.23 09:58
Оценка:
Здравствуйте, fk0, Вы писали:

fk0> Но компилятор же небось инлайнит как (a*(2^8) + b) * (c*(2^8) + d) = a*c*(2^16) + (b*c + a*d)*(2^8) + b*d

fk0>(4 умножения, два сдвига и три сложения). Или по крайней мере вызывает встроенную функцию.
fk0>Последнее кстати плохо (т.к. регистровый аллокатор уже не понимает какие регистры испортятся и вынужден
fk0>что попало заталкивать в стек).
fk0> А какой смысл в "фиксированной точке" и 8-битных числах??? Любые практические данные
fk0>которые потребуется обрабатывать едва ли влезут в 16 бит. Хорошо бы иметь 24. А 32 может быть даже много.
fk0>Звук например. Сам-то прекрасно в 12 бит влезает, но какая-то обработка в два раза больше потребует.
fk0>Например, цифровой фильтр который выделяет узкую полосу частот. Помню сигнал с акселерометра обсчитывал.
fk0>Там и 8 бит на входе более чем, но нужно же считать векторные произведения, потом тригонометрические функции,
fk0>в итоге 16 не хватает для получения приемлемой точности. Вот 24 в самый раз.
Странные вопросы, потому что Я вроде уже несколько раз писал слово "AVR". Иногда полезно проветривать комнату и открывать окно, чтобы увидеть что-то кроме x86-64.

fk0> Просто нужно нормально писать код. Что на C, что на C++. Со временем пишешь сам так, что компилятор

fk0>варнингов даже не даёт. Обычный набор варнингов для GCC/Clang: -Wall -Wextra -Wcast-align -fstrict-aliasing,
fk0>ещё -Wconversion, но не все, от проекта зависит. И полезно прогонять с -fsanitize=undefined.
Это позиция вчерашнего студента. Сходи в большие корпорации, и посмотри, сколько там предупреждений компилятора, и какие проверки отключены.

fk0> Нет, это попахивает полезными практиками промышленного программирования.

fk0>Потому, что когда ты будешь вбивать число пи руками в код, то можешь на одну циферку
fk0>где-то недалеко от точки ошибиться. И никто не заметит даже. А потом Луна-25 улетает
fk0>на полной скорости в Луну. И ошмётков не соберёшь. Потому, что орбиту с неправильным пи
fk0>посчитало тоже неправильно.
Ты предлагаешь "5" вынести в константу, а потом эту константу использовать в шаблоне? Если так, то это шиза.

fk0> Число пи, кстати, в стандартной C/C++-библиотеке существует как константа M_PI.

Непереносимо, читай стандарт.

fk0> Мне как потенциальному код-ревьюверу, например, совершенно непонятно, почему 5, а не 6 или не 4.

Если бы ты проверял код Кармака с быстрым обратным корнем, то столкнулся бы с такой же проблемой. Это математика, в ней немножечко есть константы.

Я понимаю, что у тебя подгорает, что где-то кто-то что-то делает не так, как пишут в умных книжках. Я именно из-за этой проблемы и ушёл из программирования — надоели офисные крысы, которые знают много умных слов, но работающую программу с начала до конца никогда не напишут, потому что привыкли протирать штаны в корпорациях, где всего-то и нужно что подкрутить винтик тут, открутить там. Так что предлагаю прекратить дискуссию.
Re[7]: Fixed-point to string
От: fk0 Россия https://fk0.name
Дата: 01.10.23 15:11
Оценка:
Здравствуйте, cppguard, Вы писали:

C>Здравствуйте, fk0, Вы писали:


fk0>> Но компилятор же небось инлайнит как (a*(2^8) + b) * (c*(2^8) + d) = a*c*(2^16) + (b*c + a*d)*(2^8) + b*d

fk0>>(4 умножения, два сдвига и три сложения). Или по крайней мере вызывает встроенную функцию.
fk0>>Последнее кстати плохо (т.к. регистровый аллокатор уже не понимает какие регистры испортятся и вынужден
fk0>>что попало заталкивать в стек).
fk0>> А какой смысл в "фиксированной точке" и 8-битных числах??? Любые практические данные
fk0>>которые потребуется обрабатывать едва ли влезут в 16 бит. Хорошо бы иметь 24. А 32 может быть даже много.
fk0>>Звук например. Сам-то прекрасно в 12 бит влезает, но какая-то обработка в два раза больше потребует.
fk0>>Например, цифровой фильтр который выделяет узкую полосу частот. Помню сигнал с акселерометра обсчитывал.
fk0>>Там и 8 бит на входе более чем, но нужно же считать векторные произведения, потом тригонометрические функции,
fk0>>в итоге 16 не хватает для получения приемлемой точности. Вот 24 в самый раз.
C>Странные вопросы, потому что Я вроде уже несколько раз писал слово "AVR". Иногда полезно проветривать комнату и открывать окно, чтобы увидеть что-то кроме x86-64.

Да хоть PIC18F2520! Лишь бы не 16F628!
Я утверждаю, что почти не вижу смысла в 8 битах вообще, независимо от того, на какой платформе.
Все практически существующие виды аналоговых данных в 8 бит едва ли лезут. Кроме каких-то очень узко специальных случаев.
Вроде алгоритма им. АОНа и т.п. Впрочем там фиксированная точка не нужна.

fk0>> Просто нужно нормально писать код. Что на C, что на C++. Со временем пишешь сам так, что компилятор

fk0>>варнингов даже не даёт. Обычный набор варнингов для GCC/Clang: -Wall -Wextra -Wcast-align -fstrict-aliasing,
fk0>>ещё -Wconversion, но не все, от проекта зависит. И полезно прогонять с -fsanitize=undefined.
C>Это позиция вчерашнего студента. Сходи в большие корпорации, и посмотри, сколько там предупреждений компилятора, и какие проверки отключены.

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

fk0>> Нет, это попахивает полезными практиками промышленного программирования.

fk0>>Потому, что когда ты будешь вбивать число пи руками в код, то можешь на одну циферку
fk0>>где-то недалеко от точки ошибиться. И никто не заметит даже. А потом Луна-25 улетает
fk0>>на полной скорости в Луну. И ошмётков не соберёшь. Потому, что орбиту с неправильным пи
fk0>>посчитало тоже неправильно.
C>Ты предлагаешь "5" вынести в константу, а потом эту константу использовать в шаблоне? Если так, то это шиза.

И это в том числе. А для начала обосновать вообще, почему 5, а не 4.
Код ревью в большой корпорации вот точно не пройдёт.

fk0>> Число пи, кстати, в стандартной C/C++-библиотеке существует как константа M_PI.

C>Непереносимо, читай стандарт.

Если переносимо, то 4*atan(1). Но в любом случае нужно сделать константу, хоть свою самодельную, а не вписывать
в каждую функцию, чтоб случайно там вместо пи не оказался твой телефонный номер или что-то вроде.

fk0>> Мне как потенциальному код-ревьюверу, например, совершенно непонятно, почему 5, а не 6 или не 4.

C>Если бы ты проверял код Кармака с быстрым обратным корнем, то столкнулся бы с такой же проблемой. Это математика, в ней немножечко есть константы.

А в константах легко ошибиться. Почему константы в коде -- зло. Если вот прямо совсем не очевидны,
что в них невозможно ошибиться и легко проверить.

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

C>Я понимаю, что у тебя подгорает, что где-то кто-то что-то делает не так, как пишут в умных книжках. Я именно из-за этой проблемы и ушёл из программирования — надоели офисные крысы, которые знают много умных слов, но работающую программу с начала до конца никогда не напишут, потому что привыкли протирать штаны в корпорациях, где всего-то и нужно что подкрутить винтик тут, открутить там. Так что предлагаю прекратить дискуссию.


Гыгыгы. А где знают как писать работающую программу? Наверное там, где их пишут и зарплату за это получают.

Отредактировано 01.10.2023 15:13 fk0 . Предыдущая версия .
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.