Кастомный вывод double, помогите
От: Sm0ke Россия ksi
Дата: 25.07.21 06:31
Оценка:
#include <iostream>

using real = double;
using Char = wchar_t;

enum class n_show_sign { only_negative, always, never };

struct traits_show {
    static constexpr n_show_sign show_sign = n_show_sign::only_negative;
    static constexpr const Char negative_ending[] = L"";
    static constexpr bool always_show_dot = false;
    static constexpr const Char zero[] = L"0";
    static constexpr const Char nan[] = L"nan"; // not a number
    static constexpr const Char inf[] = L"inf"; // infinity
    static constexpr const Char inf_[] = L"-inf"; // negative infinity
};

struct traits_dump {
    static constexpr n_show_sign show_sign = n_show_sign::never;
    static constexpr const Char negative_ending[] = L"_";
    static constexpr bool always_show_dot = true;
    static constexpr const Char zero[] = L"0.0";
    static constexpr const Char nan[] = L"$float.nan#"; // not a number
    static constexpr const Char inf[] = L"$float.inf#"; // infinity
    static constexpr const Char inf_[] = L"$float.inf_#"; // negative infinity
};

template <class Traits>
void show(std::wostream & wo, real n) {}


Помогите написать функцию show(), которая работает следующим образом.

Если n isnan, то просто вывод Traits::nan
Если n плюс бесконечность, то просто вывод Traits::inf
Если n минус бесконечность, то просто вывод Traits::inf_
Если n 0, то просто вывод Traits::zero

* Если n < 10000 && n >= 0.001, то выводит обычным образом (без 'e')
* Иначе выводит один знак до точки, а всё остальное после точки и печатает e[+/-]NNN (напр: 1.234e+7 7.0e-9_)

* При этом учитывает параметры Traits::show_sign
* Для Traits::always_show_dot == true приписывает ".0", если точку ещё не вывели (это до 'e' когда есть)
* Для отрицательных конечных чисел вконце выводит Traits::negative_ending
Выводит все хранимые цифры без округления (макс точность).

И чтобы я мог заменить: using real = double; на: using real = long double; в дальнейшем, если захочу.
Заранее спасибо.
double iostream custom
Re: Кастомный вывод double, помогите
От: B0FEE664  
Дата: 28.07.21 12:20
Оценка:
Здравствуйте, Sm0ke, Вы писали:

S>Выводит все хранимые цифры без округления (макс точность).

Насколько я понимаю, это возможно для вывода в формате использующим основание исчисления кратное двум. Так что в десятичном виде не получится.
И каждый день — без права на ошибку...
Re[2]: Кастомный вывод double, помогите
От: watchmaker  
Дата: 28.07.21 13:48
Оценка: 5 (1)
Здравствуйте, B0FEE664, Вы писали:

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


S>>Выводит все хранимые цифры без округления (макс точность).

BFE>Насколько я понимаю, это возможно для вывода в формате использующим основание исчисления кратное двум. Так что в десятичном виде не получится.

Любую конечную двоичную дробь можно записать в виде конечной десятичной дроби. То есть возможно вывести в десятичном виде любое конечное значение, сохранённое в IEEE754 float, double и т.п. Другое дело, что такая запись может получится длинной: может потребоваться столько же десятичных цифр, сколько было двоичных в оригинальной дроби.

Это в обратную сторону возникают проблемы (например, с числом 0.3, которое непредставимо в виде конечной двоичной дроби).

Кстати, практичные библиотеки часто не выводят полную десятичную дробь. А останавливаются когда уже выведенное десятичное значение оказывается самым ближним к выводимому floating-point числу среди всех floating-point чисел, представимых в заданном типе.
То есть условный FloatToString выводит минимальную по длине строку, для которой начинает выполнятся StringToFloat(FloatToString(x)) == x. Так десятичная дробь хотя и оказывается формально не равной исходному числу, но при чтении однозначно превращается в него обратно, так как длинный хвост младших десятичных разрядов всё равно не представим в исходном double или float. И поэтому для целей конвертации потери точности не происходит, а длина записи получается значительно короче.
Re[3]: Кастомный вывод double, помогите
От: B0FEE664  
Дата: 28.07.21 14:48
Оценка:
Здравствуйте, watchmaker, Вы писали:

W>Любую конечную двоичную дробь можно записать в виде конечной десятичной дроби. То есть возможно вывести в десятичном виде любое конечное значение, сохранённое в IEEE754 float, double и т.п. Другое дело, что такая запись может получится длинной: может потребоваться столько же десятичных цифр, сколько было двоичных в оригинальной дроби.


Действительно: конечная сумма двоек возведённых в отрицательную степень всегда имеет конечное представление в десятичной записи. Почему-то я об этом не думал никогда...

W>Кстати, практичные библиотеки часто не выводят полную десятичную дробь. А останавливаются когда уже выведенное десятичное значение оказывается самым ближним к выводимому floating-point числу среди всех floating-point чисел, представимых в заданном типе.

W>То есть условный FloatToString выводит минимальную по длине строку, для которой начинает выполнятся StringToFloat(FloatToString(x)) == x. Так десятичная дробь хотя и оказывается формально не равной исходному числу, но при чтении однозначно превращается в него обратно, так как длинный хвост младших десятичных разрядов всё равно не представим в исходном double или float. И поэтому для целей конвертации потери точности не происходит, а длина записи получается значительно короче.

Я правильно понимаю, что стандартных средств для этого такого вывода (без потери точности) в С/С++ нет?
И каждый день — без права на ошибку...
Re[4]: Кастомный вывод double, помогите
От: watchmaker  
Дата: 28.07.21 19:06
Оценка:
Здравствуйте, B0FEE664, Вы писали:


W>>То есть условный FloatToString выводит минимальную по длине строку, для которой начинает выполнятся StringToFloat(FloatToString(x)) == x. Так десятичная дробь хотя и оказывается формально не равной исходному числу, но при чтении однозначно превращается в него обратно, так как длинный хвост младших десятичных разрядов всё равно не представим в исходном double или float. И поэтому для целей конвертации потери точности не происходит, а длина записи получается значительно короче.


BFE>Я правильно понимаю, что стандартных средств для этого такого вывода (без потери точности) в С/С++ нет?


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

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


А если хочешь получить длинную запись, то, конечно, никто тебя не ограничивает в возможности указать и получить свои 54 значащих цифры для double, явно передав нужное число в условный sprintf. Только это может оказаться примером ложной точности: цифр много, но при этом последние ⅔ из них ни на что не влияют — если их изменить и прочитать обратно double, то на результат это никак не повлияет.
Но стандартный способ есть.
Re[4]: Кастомный вывод double, помогите
От: Chorkov Россия  
Дата: 30.07.21 08:48
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Я правильно понимаю, что стандартных средств для этого такого вывода (без потери точности) в С/С++ нет?


В fortran для решения этой проблемы используется шестнадцатеричное представление с плавающей точкой. Но как-то не стало популярным в других языках. Хотя, если решается задачи сериализации в текстовый формат, без требования человеко-читаемости, то это простой в реализации вариант.
Re[5]: Кастомный вывод double, помогите
От: B0FEE664  
Дата: 30.07.21 09:10
Оценка:
Здравствуйте, Chorkov, Вы писали:

BFE>>Я правильно понимаю, что стандартных средств для этого такого вывода (без потери точности) в С/С++ нет?

C>В fortran для решения этой проблемы используется шестнадцатеричное представление с плавающей точкой. Но как-то не стало популярным в других языках. Хотя, если решается задачи сериализации в текстовый формат, без требования человеко-читаемости, то это простой в реализации вариант.

Да, два года назад мне так и посоветовали делать:
printf("The pi = %a\n", pi);


однако интересен именно десятичный формат, мне std::to_chars пока не доступен, впрочем и задача не актуальна для меня.
И каждый день — без права на ошибку...
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.