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[7]: Fixed-point to string
От: CreatorCray  
Дата: 01.09.23 02:53
Оценка: +2
Здравствуйте, cppguard, Вы писали:

C>Я не знаток С++, и не знаю, как рассово верно нужно реализовывать такие вещи.

А при чём тут С++?
Вопрос в другом: тебе это надо чтоб работало на девайсе где вообще поддержки float нету ни в каком виде, даже софтварном, что ты так извратился?
Ибо ведь куда проще конвертнуть в float/double и напечатать его.

C>В С++ просмотр исходников STL это боль и унижение

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

C> и чтение кода вида __my_type__ SAFETY_MACRO(__name_stl_xxx__) (__type1__ __ANOTHER_SAFETY_MACRO__(__arg1__)).

Дадада, живые люди так не пишут.
... << RSDN@Home 1.3.110 alpha 5 rev. 62>>
Забанили по IP, значит пора закрыть эту страницу.
Всем пока
Re[10]: Fixed-point to string
От: cppguard  
Дата: 01.09.23 11:05
Оценка: +2
Здравствуйте, Alekzander, Вы писали:

A>А как так вышло, что современный C++ платформой поддерживается, а float — нет? Так вообще бывает?

Не совсем понимаю вопрос. Современный С++ можно хоть на калькуляторе поддерживать, а для аппаратное поддержки float нужны соответствующие инструкции процессора.
Re[11]: Fixed-point to string
От: Alekzander  
Дата: 01.09.23 12:09
Оценка: :))
Здравствуйте, cppguard, Вы писали:

A>>А как так вышло, что современный C++ платформой поддерживается, а float — нет? Так вообще бывает?

C>Не совсем понимаю вопрос. Современный С++ можно хоть на калькуляторе поддерживать, а для аппаратное поддержки float нужны соответствующие инструкции процессора.

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

Возможно, в этом есть какой-то смысл, но он от меня ускользает.
Re[6]: Fixed-point to string
От: T4r4sB Россия  
Дата: 31.08.23 19:54
Оценка: 3 (1)
Здравствуйте, sergii.p, Вы писали:

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


Ну я могу найти ту давнюю тему.
А суть простая: расчёты велись в 4-байтном флоате, в одном месте оптимизатору хватило регистров сопроцессора чтоб хранить промежуточный результат, а в другом нет и он выгружал всё в память, в 4-байтный флоат, с потерей точности.
Короче нельзя закладываться в плавучке на точный результат. Я даже опасаюсь сравнивать с нулём переменную, которую явно инициализировал нулём, но это уже шиза конечно.
Re[2]: Fixed-point to string
От: cppguard  
Дата: 30.08.23 23:43
Оценка: :)
Здравствуйте, CreatorCray, Вы писали:


C>>fixed-point

CC>1.001

Так и запишем — матчасть не выучил
Re[5]: Fixed-point to string
От: пффф  
Дата: 31.08.23 10:31
Оценка: +1
Здравствуйте, cppguard, Вы писали:

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


Для отладки чтобы глазами смотреть — конвертим во float, печатаем sprintf'ом
Re[9]: Fixed-point to string
От: Alekzander  
Дата: 01.09.23 08:44
Оценка: :)
Здравствуйте, cppguard, Вы писали:

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


CC>>Вопрос в другом: тебе это надо чтоб работало на девайсе где вообще поддержки float нету ни в каком виде, даже софтварном, что ты так извратился?

CC>>Ибо ведь куда проще конвертнуть в float/double и напечатать его.
C>Да, микроконтроллеры. Современный С++ со всеми его вычислениями на этапе компиляции позволяет писать низкоуровневый код чуть безопаснее. Всякие ништяки есть типа сoroutines, с помощью которых почти бесплатно реализуется кооперативная многозадачность. Программная поддержка float есть, но для неё генерируется слишком много инструкций, и мало того, что это медленно, так ещё и драгоценная память съедается.

А как так вышло, что современный C++ платформой поддерживается, а float — нет? Так вообще бывает?
Re[12]: Fixed-point to string
От: CreatorCray  
Дата: 01.09.23 19:10
Оценка: +1
Здравствуйте, Alekzander, Вы писали:

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

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

A>Я бы дал голый Си и наказ не выёживаться.

И что, там от этого аппаратная поддержка float появится?

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

Смысл в использовании на порядки более удобного и безопасного языка чем голый С.
... << RSDN@Home 1.3.110 alpha 5 rev. 62>>
Забанили по IP, значит пора закрыть эту страницу.
Всем пока
Re[3]: Fixed-point to string
От: sergii.p  
Дата: 01.09.23 19:12
Оценка: +1
Здравствуйте, T4r4sB, Вы писали:

TB>Неоптимально, смотри алгоритм русского крестьянина для возведения в степень


когда имеем дело с выводом в cout на спичках экономить бессмысленно (ещё и в ущерб читабельности)
Re[13]: Fixed-point to string
От: Alekzander  
Дата: 01.09.23 20:40
Оценка: :)
Здравствуйте, CreatorCray, Вы писали:

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

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

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

Что хоть за камень-то такой, если не секрет?
Fixed-point to string
От: cppguard  
Дата: 30.08.23 22:51
Оценка:
Накидал тут на коленке код для вывода вещественных чисел с фиксированной запятой в привычном виде. Вышло как-то слишком просто. Имеет право на жизнь?
#include <iostream>

template<typename T, unsigned N>
class Q {
    T value;
public:
    Q(T value): value(value) {}
    T integral() const { return value >> N; }
    T fractional() const { return value & (1 << N - 1); }
};

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

template<unsigned Multiplier>
struct FractToString<0, Multiplier> {
    static unsigned value(unsigned value, unsigned acc) {
        return acc;
    }
};


template<typename T, unsigned N>
std::ostream& operator<< (std::ostream&  os, const Q<T, N>& q)
{
    return os << static_cast<int>(q.integral()) << "." << static_cast<int>(FractToString<N>::value(q.fractional()));
}


int main(int argc, char *argv[]) {
    std::cout << Q<uint8_t, 3>(0b101101) << std::endl;
}
Re: Fixed-point to string
От: CreatorCray  
Дата: 30.08.23 23:35
Оценка:
Здравствуйте, cppguard, Вы писали:

C>Вышло как-то слишком просто.

Просто?

C> Имеет право на жизнь?

Покажи как оно выведет аналог 1.001
... << RSDN@Home 1.3.110 alpha 5 rev. 62>>
Забанили по IP, значит пора закрыть эту страницу.
Всем пока
Re[3]: Fixed-point to string
От: CreatorCray  
Дата: 31.08.23 02:13
Оценка:
Здравствуйте, cppguard, Вы писали:

C>>>fixed-point

CC>>аналог 1.001

C>Так и запишем — матчасть не выучил


Я на fixed point рендерер на асме написал в середине 90х.
Ты же походу и читать не умеешь и матчасть совершенно не знаешь, раз вообще не понял про что была речь.
Ну да ладно.

Начнём по порядку:
1. Что оно выведет для Q<uint8_t, 3> ((2 << 3) + 1) и что должно было вывести?
3. Что оно выведет для Q<uint32_t, 8> ((2 << 8) + 1) и что должно было вывести?
2. Что оно выведет для Q<uint32_t, 16> ((2 << 16) + 1) и что должно было вывести?

  спойлеры
1. Выводит 2.0 вместо 2.125. В коде довольно очевидный баг.
2. Выводит (после исправления бага) 2.390625 вместо 2.00390625 ибо ведушие нули протерялись при выводе целого
3. Выводит феерическое 2.-2030932031 вместо 2.0000152587890625 ибо acc переполняется а потом ты это ещё и в signed кастишь, ну и нули протеряны всё равно
... << RSDN@Home 1.3.110 alpha 5 rev. 62>>
Забанили по IP, значит пора закрыть эту страницу.
Всем пока
Re: Fixed-point to string
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 31.08.23 05:50
Оценка:
Здравствуйте, cppguard, Вы писали:

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


Не очень, мягко говоря

Фиксированная точка (запятая) полезна в первую очередь для бухгалтерии/финансов, а там все числа принудительно десятичные. У тебя же рассчитано на двоичку. Я просто не могу себе представить домен, для которого такое нужно. Если какие-нибудь ситуации типа "с датчика снимаем 10 двоичных цифр дробной части ибо АЦП", то для таких лучше форматировать в шестнадцатиричную — типа значение "0x23.fc", а не в десятичную.
Но это особые ситуации и для них вообще неизвестно, стоит ли так стараться.

А для десятичной точки тебе нужно деление на 10**N чтобы нормально отображать. Вот тут можно средствами C++ догнать это до константных операций (деление на константу через обратное умножение).

Про ошибки в коде тебе сказали, а как общий принцип — тесты и ещё раз тесты! Задача такая, что сделать ошибку тривиально. Поэтому все и основные, и маргинальные случаи — подкрепить тестами.
The God is real, unless declared integer.
Re[4]: Fixed-point to string
От: cppguard  
Дата: 31.08.23 06:33
Оценка:
Здравствуйте, CreatorCray, Вы писали:

CC>Ты же походу и читать не умеешь и матчасть совершенно не знаешь, раз вообще не понял про что была речь.

CC>Ну да ладно.
1.001 непредставимо в виде двоичной дроби. О чём речь? Или вопрос был про 0b1001?

CC>
  спойлеры
CC>1. Выводит 2.0 вместо 2.125. В коде довольно очевидный баг.
CC>2. Выводит (после исправления бага) 2.390625 вместо 2.00390625 ибо ведушие нули протерялись при выводе целого
CC>3. Выводит феерическое 2.-2030932031 вместо 2.0000152587890625 ибо acc переполняется а потом ты это ещё и в signed кастишь, ну и нули протеряны всё равно


За тестирование спасибо. Насчёт переполнения — предполагается работа только с uint8_t и форматами чисел Q4.4 и Q1.7, остальное не интересует, поэтому я и не особо старался.
Re[2]: Fixed-point to string
От: cppguard  
Дата: 31.08.23 06:38
Оценка:
Здравствуйте, netch80, Вы писали:

N>Фиксированная точка (запятая) полезна в первую очередь для бухгалтерии/финансов, а там все числа принудительно десятичные. У тебя же рассчитано на двоичку. Я просто не могу себе представить домен, для которого такое нужно. Если какие-нибудь ситуации типа "с датчика снимаем 10 двоичных цифр дробной части ибо АЦП", то для таких лучше форматировать в шестнадцатиричную — типа значение "0x23.fc", а не в десятичную.

Это нужно для отладки математики с фиксированной точкой на микроконтроллерах с аппаратным умножением.

N>Про ошибки в коде тебе сказали, а как общий принцип — тесты и ещё раз тесты! Задача такая, что сделать ошибку тривиально. Поэтому все и основные, и маргинальные случаи — подкрепить тестами.

Хорошо, но вопрос не об этом был. Если исправить ошибку, то код работает, но при этом он довольно компактен. А если загуглить "fixed-point number to string", то находятся какие-то монстры. Поскольку я не считаю себя мега-гуру, то решил спросить у сообщества — правильно ли я делаю.
Re[3]: Fixed-point to string
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 31.08.23 07:43
Оценка:
Здравствуйте, cppguard, Вы писали:

N>>Фиксированная точка (запятая) полезна в первую очередь для бухгалтерии/финансов, а там все числа принудительно десятичные. У тебя же рассчитано на двоичку. Я просто не могу себе представить домен, для которого такое нужно. Если какие-нибудь ситуации типа "с датчика снимаем 10 двоичных цифр дробной части ибо АЦП", то для таких лучше форматировать в шестнадцатиричную — типа значение "0x23.fc", а не в десятичную.

C>Это нужно для отладки математики с фиксированной точкой на микроконтроллерах с аппаратным умножением.

Тогда да, особый случай.
Ну точно надо в hex всё вводить и выводить. В крайнем случае в чистой двоичке.
Но не в десятичке, со всеми этими 0.0009765625 ты повесишься быстрее, чем отладишь.

N>>Про ошибки в коде тебе сказали, а как общий принцип — тесты и ещё раз тесты! Задача такая, что сделать ошибку тривиально. Поэтому все и основные, и маргинальные случаи — подкрепить тестами.

C>Хорошо, но вопрос не об этом был. Если исправить ошибку, то код работает, но при этом он довольно компактен. А если загуглить "fixed-point number to string", то находятся какие-то монстры. Поскольку я не считаю себя мега-гуру, то решил спросить у сообщества — правильно ли я делаю.

Я думаю, потому и монстры, что они десятичные. А у тебя другая цель.
The God is real, unless declared integer.
Re[5]: Fixed-point to string
От: T4r4sB Россия  
Дата: 31.08.23 08:41
Оценка:
Здравствуйте, cppguard, Вы писали:


C>1.001 непредставимо в виде двоичной дроби. О чём речь? Или вопрос был про 0b1001?


В виде double оно тоже не представимо но это не мешает им пользоваться.
Очевидно речь шла про ближайшее приближение к этому числу
Re[6]: Fixed-point to string
От: cppguard  
Дата: 31.08.23 09:11
Оценка:
Здравствуйте, T4r4sB, Вы писали:

TB>В виде double оно тоже не представимо но это не мешает им пользоваться.

TB>Очевидно речь шла про ближайшее приближение к этому числу

Вообще не очевидно. Ближайшее с какой стороны? Для fixed-point неопределены всякие epsilon и так далее. Да и процедуру конвертации я не приводил, чтобы давать такой тест. К чему вообще этот вопрос был?
Re[4]: Fixed-point to string
От: cppguard  
Дата: 31.08.23 09:16
Оценка:
Здравствуйте, netch80, Вы писали:

N>Тогда да, особый случай.

N>Ну точно надо в hex всё вводить и выводить. В крайнем случае в чистой двоичке.
N>Но не в десятичке, со всеми этими 0.0009765625 ты повесишься быстрее, чем отладишь.

Вот взять реальный пример. Есть даные магнитометра — XYZ, нужно перевести эти значения в углы Эйлера. Математика вся с фиксированной точкой, и глазами отлаживать удобно, смотря именно на десятичные значения, потому что в голове идёт привязка к радианам. Я уверен, что это лишь вопрос надроча, но для себя я пока не представляю отладку математики в шеснадцатеричных значениях.
Re: Fixed-point to string
От: SaZ  
Дата: 31.08.23 10:21
Оценка:
Здравствуйте, cppguard, Вы писали:

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

C>...

std::format пробовали?
Re[7]: Fixed-point to string
От: T4r4sB Россия  
Дата: 31.08.23 10:50
Оценка:
Здравствуйте, cppguard

C>Вообще не очевидно. Ближайшее с какой стороны? Для fixed-point неопределены всякие epsilon и так далее.


Для дабла все то же самое, и?
Re[8]: Fixed-point to string
От: cppguard  
Дата: 31.08.23 11:20
Оценка:
Здравствуйте, T4r4sB, Вы писали:

TB>Для дабла все то же самое, и?


Что я должен ответить? Для моих задач писать процедуру преобразования из обыкновенной десятичной дроби в fixed-point нецелесообразоно от слова совсем, поэтому я не знаю, как 1.001 будет выглядеть в формате Q4.4.
Re[2]: Fixed-point to string
От: T4r4sB Россия  
Дата: 31.08.23 11:40
Оценка:
Здравствуйте, netch80, Вы писали:

N>Фиксированная точка (запятая) полезна в первую очередь для бухгалтерии/финансов, а там все числа принудительно десятичные. У тебя же рассчитано на двоичку. Я просто не могу себе представить домен, для которого такое нужно. Если какие-нибудь ситуации типа "с датчика снимаем 10 двоичных цифр дробной части ибо АЦП", то для таких лучше форматировать в шестнадцатиричную — типа значение "0x23.fc", а не в десятичную.

N>Но это особые ситуации и для них вообще неизвестно, стоит ли так стараться.


Я на полном серьезе использовал фиксированную точку в играх.
Причины две:
1. На моих железках она работала быстрее плавучки, особенно если учесть операции округления
2. Гарантированное воспроизведение результата на любой машине. Для реализации демок/реплеев важно. Я даже сетевой код на этом обосновывал — передавать по сети только команды игрока, а дальше оба компа по ним одинаково воспроизведут изменения в игровом мире, без единого бита рассинхрона
Re[5]: Fixed-point to string
От: B0FEE664  
Дата: 31.08.23 14:26
Оценка:
Здравствуйте, cppguard, Вы писали:

C>За тестирование спасибо. Насчёт переполнения — предполагается работа только с uint8_t и форматами чисел Q4.4 и Q1.7, остальное не интересует, поэтому я и не особо старался.

Я правильно понимаю, что:
Q4.4 — это две таблицы по 16 элементов.
Q1.7 — это 0 или 1 и таблица на 128 элементов.
?
И каждый день — без права на ошибку...
Re[3]: Fixed-point to string
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 31.08.23 14:48
Оценка:
Здравствуйте, T4r4sB, Вы писали:

TB>Я на полном серьезе использовал фиксированную точку в играх.

TB>Причины две:
TB>1. На моих железках она работала быстрее плавучки, особенно если учесть операции округления

ok, возможно.

TB>2. Гарантированное воспроизведение результата на любой машине. Для реализации демок/реплеев важно. Я даже сетевой код на этом обосновывал — передавать по сети только команды игрока, а дальше оба компа по ним одинаково воспроизведут изменения в игровом мире, без единого бита рассинхрона


Вот тут... в пределах корректных операций IEEE754 простые операции (арифметика плюс какой-нибудь sqrt) должны считаться везде одинаково, расхождения должны начаться где-то с sin, log* и дальше в трансцедентные функции. И то насчёт sin не уверен.
Хотя, если где-то тупые реализации, ХЗ...
Но это значит что вы сделали свои реализации — пусть с погрешностями, но одинаковые.
The God is real, unless declared integer.
Re[4]: Fixed-point to string
От: T4r4sB Россия  
Дата: 31.08.23 14:58
Оценка:
Здравствуйте, netch80, Вы писали:

N>Вот тут... в пределах корректных операций IEEE754 простые операции (арифметика плюс какой-нибудь sqrt) должны считаться везде одинаково, расхождения должны начаться где-то с sin, log* и дальше в трансцедентные функции. И то насчёт sin не уверен.

N>Хотя, если где-то тупые реализации, ХЗ...
N>Но это значит что вы сделали свои реализации — пусть с погрешностями, но одинаковые.


А вот хрен. Расхождения зависят не только от режима компиляции.
Даже одна и та же формула в разных контекстах может выдать разный результат на одних входных данных.
Так что не доверяй плавучке
Re: Fixed-point to string
От: Maniacal Россия  
Дата: 31.08.23 15:07
Оценка:
Здравствуйте, cppguard, Вы писали:

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


fcvt можно использовать, там можно указывать сколько цифр после запятой обрабатывать. Только функция точку в строку не пишет, но честно возвращает, где она должна стоять. Так что дополнительные телодвижения нужны, чтобы её туда вертать взад.
Re[5]: Fixed-point to string
От: sergii.p  
Дата: 31.08.23 15:28
Оценка:
Здравствуйте, T4r4sB, Вы писали:

TB>Даже одна и та же формула в разных контекстах может выдать разный результат на одних входных данных.


что-то не верится, а пруфы будут?
Re[5]: Fixed-point to string
От: CreatorCray  
Дата: 31.08.23 21:14
Оценка:
Здравствуйте, cppguard, Вы писали:

C>1.001 непредставимо в виде двоичной дроби. О чём речь? Или вопрос был про 0b1001?

Мда... Ты смотрнишь на деревья (пример) но не видишь леса.
Речь шла про дробную часть, которая в десятичном виде будет иметь нули после точки.
Ты когда выводишь дробную часть как int — эти нули протериваются. Т.е. там где у тебя должно было быть 1.0012345ёклмн будет выведено 1.12345ёклмн
Прочитай ещё раз спойлер в моём ответе.
... << RSDN@Home 1.3.110 alpha 5 rev. 62>>
Забанили по IP, значит пора закрыть эту страницу.
Всем пока
Re[6]: Fixed-point to string
От: cppguard  
Дата: 31.08.23 21:41
Оценка:
Здравствуйте, B0FEE664, Вы писали:

C>>За тестирование спасибо. Насчёт переполнения — предполагается работа только с uint8_t и форматами чисел Q4.4 и Q1.7, остальное не интересует, поэтому я и не особо старался.

BFE>Я правильно понимаю, что:
BFE>Q4.4 — это две таблицы по 16 элементов.
BFE>Q1.7 — это 0 или 1 и таблица на 128 элементов.
BFE>?

Олдскул, но да, можно и так. Только есть ненулевая вероятность, что придётся uint16_t поддерживать.
Re[6]: Fixed-point to string
От: cppguard  
Дата: 31.08.23 21:48
Оценка:
Здравствуйте, CreatorCray, Вы писали:

CC>Мда... Ты смотрнишь на деревья (пример) но не видишь леса.

CC>Речь шла про дробную часть, которая в десятичном виде будет иметь нули после точки.
CC>Ты когда выводишь дробную часть как int — эти нули протериваются. Т.е. там где у тебя должно было быть 1.0012345ёклмн будет выведено 1.12345ёклмн
CC>Прочитай ещё раз спойлер в моём ответе.

Тогда формулировка максимально непонятная. Мог же просто сказать: "ведущие нули теряются"? Но вопрос вообще был в целом про подход. Я не знаток С++, и не знаю, как рассово верно нужно реализовывать такие вещи. Если нормально сразу числом отдавать, то можно просто через iomanip установить количество ведущих нулей. Если же надо по одной цифре отдавать, то другой подход. Собственно, об этом и был вопрос. В Java я одним кликом декомпилирую JDK и смотрю, как написаны фундаментальные вещи. В С++ просмотр исходников STL это боль и унижение и чтение кода вида __my_type__ SAFETY_MACRO(__name_stl_xxx__) (__type1__ __ANOTHER_SAFETY_MACRO__(__arg1__)).
Re[8]: Fixed-point to string
От: cppguard  
Дата: 01.09.23 04:40
Оценка:
Здравствуйте, CreatorCray, Вы писали:

CC>Вопрос в другом: тебе это надо чтоб работало на девайсе где вообще поддержки float нету ни в каком виде, даже софтварном, что ты так извратился?

CC>Ибо ведь куда проще конвертнуть в float/double и напечатать его.
Да, микроконтроллеры. Современный С++ со всеми его вычислениями на этапе компиляции позволяет писать низкоуровневый код чуть безопаснее. Всякие ништяки есть типа сoroutines, с помощью которых почти бесплатно реализуется кооперативная многозадачность. Программная поддержка float есть, но для неё генерируется слишком много инструкций, и мало того, что это медленно, так ещё и драгоценная память съедается.

C>>В С++ просмотр исходников STL это боль и унижение

CC>А зачем ты туда вообще смотришь? STL это пример плохого кода и как делать не стоит
Так стандарт в индустрии же?
Re[7]: Fixed-point to string
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 01.09.23 05:22
Оценка:
Здравствуйте, T4r4sB, Вы писали:

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


TB>Ну я могу найти ту давнюю тему.

TB>А суть простая: расчёты велись в 4-байтном флоате, в одном месте оптимизатору хватило регистров сопроцессора чтоб хранить промежуточный результат, а в другом нет и он выгружал всё в память, в 4-байтный флоат, с потерей точности.

Это
Автор: netch80
Дата: 01.12.16
, наверно?
А ещё на хабре.

TB>Короче нельзя закладываться в плавучке на точный результат.


Вообще-то там где нет неожиданного внутреннего расширения и сужения — то есть для x86 это означает только SSE, без x87 FPU — именно этой проблемы уже нет, и, если оптимизатор сам ничего не накрутил — можно предполагать, что два идентичных по форме вычисления дадут одинаковый результат независимо от того, что было в регистрах, а что в памяти.
И даже с x87 можно получить аналогичное с доп. мерами компилятора (-ffloat-store для GCC), но это дороже.

TB> Я даже опасаюсь сравнивать с нулём переменную, которую явно инициализировал нулём, но это уже шиза конечно.


Для точных значений, гарантированно влезающих в самый узкий диапазон точности из использованных — точно шиза.
The God is real, unless declared integer.
Re[5]: Fixed-point to string
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 01.09.23 05:37
Оценка:
Здравствуйте, T4r4sB, Вы писали:

N>>Вот тут... в пределах корректных операций IEEE754 простые операции (арифметика плюс какой-нибудь sqrt) должны считаться везде одинаково, расхождения должны начаться где-то с sin, log* и дальше в трансцедентные функции. И то насчёт sin не уверен.

N>>Хотя, если где-то тупые реализации, ХЗ...
N>>Но это значит что вы сделали свои реализации — пусть с погрешностями, но одинаковые.

TB>А вот хрен. Расхождения зависят не только от режима компиляции.

TB>Даже одна и та же формула в разных контекстах может выдать разный результат на одних входных данных.
TB>Так что не доверяй плавучке

Если я правильно угадал в предыдущем комментарии, что ты имел в виду, то x87 FPU не соответствует строгому IEEE754, и проблемы возникают именно от этого — скрытых от программиста операций сужения точности. С SSE такого не возникает (или можно выключить, как флаг denormals-to-zero).

Но я согласен с тем, что для того, кто этого нюанса не знает, проблема может стать подлым ударом в спину.
The God is real, unless declared integer.
Re[8]: Fixed-point to string
От: T4r4sB Россия  
Дата: 01.09.23 06:01
Оценка:
Здравствуйте, netch80, Вы писали:

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


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


TB>>Ну я могу найти ту давнюю тему.

TB>>А суть простая: расчёты велись в 4-байтном флоате, в одном месте оптимизатору хватило регистров сопроцессора чтоб хранить промежуточный результат, а в другом нет и он выгружал всё в память, в 4-байтный флоат, с потерей точности.

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

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

У меня другая тема была, я рассчитывал, находится ли точка внутри выпуклой комнаты. Считал скалярные произведения и сравнивал с нулём, и внутри цикла одно и то же сравнение было положительное, а другое — отрицательное. И эпсилон тут бы не помог, сами понимаете, просто было бы, что одно сравнение "эпсилон-положительное", а другое "эпсилон-близкое".
Re[9]: Fixed-point to string
От: CreatorCray  
Дата: 01.09.23 08:28
Оценка:
Здравствуйте, cppguard, Вы писали:

C>Современный С++ со всеми его вычислениями на этапе компиляции позволяет писать низкоуровневый код чуть безопаснее.

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

C>Программная поддержка float есть, но для неё генерируется слишком много инструкций, и мало того, что это медленно, так ещё и драгоценная память съедается.

Понятно. Ну тогда есть смысл изгалиться. Только я бы наверное делал форматирование сразу в строку а не городил бы эту борьбу с iostream.

C>Так стандарт в индустрии же?

Стандартная библиотека совершенно не гарантирует качества, она про одинаковость и доступность по умолчанию. Потому там дофига компромиссов и сложностей на ровном месте.
... << RSDN@Home 1.3.110 alpha 5 rev. 62>>
Забанили по IP, значит пора закрыть эту страницу.
Всем пока
Re[10]: Fixed-point to string
От: cppguard  
Дата: 01.09.23 11:13
Оценка:
Здравствуйте, CreatorCray, Вы писали:

C>>Современный С++ со всеми его вычислениями на этапе компиляции позволяет писать низкоуровневый код чуть безопаснее.

CC>А ты смотрел на ассемблер, который получается из твоего кода?
Конечно =) А в чём вопрос? Мне не нравятся многие вещи в С++, но бинарник можно получить действительно почти такой же как на Си — "don't pay for what you don't use" во всей красе.
Re: Fixed-point to string
От: sergii.p  
Дата: 01.09.23 18:20
Оценка:
Здравствуйте, cppguard, Вы писали:

попробовал поправить код:

#include <iostream>

constexpr size_t pow(size_t base, size_t exp, size_t res = 1) {
    return exp > 0 ? pow(base, exp - 1, res * base) : res;
}

template<typename T, unsigned N>
class Q {
    T value;
public:
    Q(T value): value(value) {}
    T integral() const { return value >> N; }
    T fractional() const { return pow(5, N) * (value & ((1 << N) - 1)); }
};

constexpr size_t leading_zeros(auto v, size_t num) {
    return v == 0 ? num : leading_zeros(v / 10, num - 1);
}

template<typename T>
constexpr T remove_trailing_zeros(T v) {
    return v % 10 == 0 ? remove_trailing_zeros(v / 10) : v;
}

std::ostream& print_leading_zeros(std::ostream&  os, size_t n) {
    if(n != 0) { 
        os << "0";
        print_leading_zeros(os, n - 1);
    }
    return os;
}

template<typename T, unsigned N>
std::ostream& operator<< (std::ostream&  os, const Q<T, N>& q)
{
    os << static_cast<int>(q.integral()) << ".";
    print_leading_zeros(os, leading_zeros(q.fractional(), N));
    return os << static_cast<int>(remove_trailing_zeros(q.fractional()));
}

void println(auto v) {
    std::cout << v << std::endl;
}

int main(int argc, char *argv[]) {
    println(Q<int, 4>(0b101101));
    println(Q<int, 4>(0b101000)); // trailng zeros
    println(Q<int, 3>(0b101101));
    println(Q<int, 4>(0b100001)); // leading zeros
}


Вывод:

2.8125
2.5
5.625
2.0625

Re[2]: Fixed-point to string
От: T4r4sB Россия  
Дата: 01.09.23 18:38
Оценка:
Здравствуйте, sergii.p, Вы писали:

SP>constexpr size_t pow(size_t base, size_t exp, size_t res = 1) {

SP> return exp > 0 ? pow(base, exp — 1, res * base) : res;
SP>}

Неоптимально, смотри алгоритм русского крестьянина для возведения в степень
Re[11]: Fixed-point to string
От: CreatorCray  
Дата: 01.09.23 19:10
Оценка:
Здравствуйте, cppguard, Вы писали:

C>>>со всеми его вычислениями на этапе компиляции

CC>>А ты смотрел на ассемблер, который получается из твоего кода?
C>Конечно =) А в чём вопрос?
Насколько много там было выполнено в compile time для этого кода?
Я там процитировал чутка больше чем следовало, пардон.
... << RSDN@Home 1.3.110 alpha 5 rev. 62>>
Забанили по IP, значит пора закрыть эту страницу.
Всем пока
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>А ещё на хабре.

Сорян за запоздалый оффтоп. Но я прочитал каменты на хабое и у меня сгорела жопа.
Сколько же даунов без мозга пишет "надо просто добавить эпсилон". И плюсует. И еще язвит типа автор лох такой крутой геометр а про эпсилон не знает. Тупо мантру повторяют не понимая смысла. Дебилы
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...
Пока на собственное сообщение не было ответов, его можно удалить.