Простейшее форматирование плавающего в строку
От: Went  
Дата: 10.09.19 11:21
Оценка:
Доброго дня. Простите за ламерский вопрос, но все же
Мне нужно превратить стандартными средствами float в строку, чтобы соблюдались следующие условия:
1. Лишние нули не пишутся, лишняя точка тоже. 1.1000f -> "1.1", 4f -> "4"
2. Точность не больше двух знаков после запятой. 1.234 -> "1.23".
Все!
В С-шном формате есть %f и %g, но я нашел комбинации флагов, дающей нужного результата. Первый постоянно дописывает ненужные нули, даже если они не нужны, второе переводит в экспоненциальный формат, если число очень близко к нулю.
С++-ные варианты выдают подмножество С-шных, которые не устраивают.
Наверное, сейчас в меня полетят помидоры
Re: Простейшее форматирование плавающего в строку
От: Maniacal Россия  
Дата: 10.09.19 11:24
Оценка:
Здравствуйте, Went, Вы писали:

W>Доброго дня. Простите за ламерский вопрос, но все же

W>Мне нужно превратить стандартными средствами float в строку, чтобы соблюдались следующие условия:
W>1. Лишние нули не пишутся, лишняя точка тоже. 1.1000f -> "1.1", 4f -> "4"
W>2. Точность не больше двух знаков после запятой. 1.234 -> "1.23".

W>В С-шном формате есть %f и %g, но я нашел комбинации флагов, дающей нужного результата. Первый постоянно дописывает ненужные нули, даже если они не нужны, второе переводит в экспоненциальный формат, если число очень близко к нулю.

%.2f / %.2g?

Есть ещё набор функций gcvt/ecvt/fcvt, там нельзя указать сколько знаков после запятой должно быть, но функции gcvt и ecvt возвращают положение десятичной точки (указатель char*), отступив пару цифр и влепив туда '\0' можно обрезать до двух знаков.
Отредактировано 10.09.2019 11:31 Maniacal . Предыдущая версия .
Re[2]: Простейшее форматирование плавающего в строку
От: Went  
Дата: 10.09.19 11:29
Оценка:
Здравствуйте, Maniacal, Вы писали:
M>%.2f / %.2g?
W>>В С-шном формате есть %f и %g, но я нашел комбинации флагов, дающей нужного результата. Первый постоянно дописывает ненужные нули, даже если они не нужны, второе переводит в экспоненциальный формат, если число очень близко к нулю.
Я что-то неправильно пишу в строке форматирования?
Вариант %.2f всегда будет добавлять нули: 1.0f -> "1.00"
Вариант %.2g грешит экспонентами: 0.0000001f -> "1e-007p"
Re: Простейшее форматирование плавающего в строку
От: night beast СССР  
Дата: 10.09.19 11:32
Оценка:
Здравствуйте, Went, Вы писали:

W>В С-шном формате есть %f и %g, но я нашел комбинации флагов, дающей нужного результата. Первый постоянно дописывает ненужные нули, даже если они не нужны, второе переводит в экспоненциальный формат, если число очень близко к нулю.


а если близкое к нулю вручную округлить?
Re[2]: Простейшее форматирование плавающего в строку
От: Went  
Дата: 10.09.19 11:33
Оценка:
Здравствуйте, night beast, Вы писали:
NB>а если близкое к нулю вручную округлить?
Да, думаю, это будет работать. Просто, думал, есть стандартное решение.
Re[3]: Простейшее форматирование плавающего в строку
От: watchmaker  
Дата: 10.09.19 11:56
Оценка: 4 (1)
Здравствуйте, Went, Вы писали:

W>Вариант %.2f всегда будет добавлять нули: 1.0f -> "1.00"

Делаешь так, потом удаляешь из получившейся строки завершающие нули.
Под твои требования у printf-like функций или у iostream, действительно, нет нужной комбинации флагов форматирования.
Re[3]: Простейшее форматирование плавающего в строку
От: Maniacal Россия  
Дата: 10.09.19 12:03
Оценка:
Здравствуйте, Went, Вы писали:

W>Здравствуйте, night beast, Вы писали:

NB>>а если близкое к нулю вручную округлить?
W>Да, думаю, это будет работать. Просто, думал, есть стандартное решение.

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

char acBuf[100];
float f = 10.0000001f;
sprintf(acBuf, "%.2g", int(f * 100) / 100.0);
Re[4]: Простейшее форматирование плавающего в строку
От: Went  
Дата: 10.09.19 12:33
Оценка:
Здравствуйте, Maniacal, Вы писали:

M>
M>char acBuf[100];
M>float f = 10.0000001f;
M>sprintf(acBuf, "%.2g", int(f * 100) / 100.0);
M>

Да, я так сначала и сделал, но оно стало превращать числа больше ста в экспоненту. Поэтому ".2" там не нужно, по-моему, просто "%g" достаточно.
Re[5]: Простейшее форматирование плавающего в строку
От: night beast СССР  
Дата: 10.09.19 12:42
Оценка:
Здравствуйте, Went, Вы писали:

W>Да, я так сначала и сделал, но оно стало превращать числа больше ста в экспоненту. Поэтому ".2" там не нужно, по-моему, просто "%g" достаточно.


а тебе для чего? если для рублей, то лучше в целыми работать..
Re: Простейшее форматирование плавающего в строку
От: rg45 СССР  
Дата: 10.09.19 13:24
Оценка:
Здравствуйте, Went, Вы писали:

W>Доброго дня. Простите за ламерский вопрос, но все же

W>Мне нужно превратить стандартными средствами float в строку, чтобы соблюдались следующие условия:
W>1. Лишние нули не пишутся, лишняя точка тоже. 1.1000f -> "1.1", 4f -> "4"
W>2. Точность не больше двух знаков после запятой. 1.234 -> "1.23".
W>Все!
W>В С-шном формате есть %f и %g, но я нашел комбинации флагов, дающей нужного результата. Первый постоянно дописывает ненужные нули, даже если они не нужны, второе переводит в экспоненциальный формат, если число очень близко к нулю.
W>С++-ные варианты выдают подмножество С-шных, которые не устраивают.

Может, стоит таки смириться с лишними нулями? Это действительно принципиально, или у тебя чисто спортивный интерес?
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 10.09.2019 13:29 rg45 . Предыдущая версия . Еще …
Отредактировано 10.09.2019 13:25 rg45 . Предыдущая версия .
Re: Простейшее форматирование плавающего в строку
От: rg45 СССР  
Дата: 10.09.19 14:55
Оценка: 5 (1)
Здравствуйте, Went, Вы писали:

W>Доброго дня. Простите за ламерский вопрос, но все же

W>Мне нужно превратить стандартными средствами float в строку, чтобы соблюдались следующие условия:
W>1. Лишние нули не пишутся, лишняя точка тоже. 1.1000f -> "1.1", 4f -> "4"
W>2. Точность не больше двух знаков после запятой. 1.234 -> "1.23".
W>Все!
W>В С-шном формате есть %f и %g, но я нашел комбинации флагов, дающей нужного результата. Первый постоянно дописывает ненужные нули, даже если они не нужны, второе переводит в экспоненциальный формат, если число очень близко к нулю.
W>С++-ные варианты выдают подмножество С-шных, которые не устраивают.

Ну если очень-очень нужно

https://ideone.com/uxF948

size_t digits_before_point(double x)
{
    return x ? size_t(std::max(0.0, floor(std::log(std::abs(x)) / std::log(10.0) + 1))) : 0;
}

std::string format(double x, size_t precision = 2)
{
    std::ostringstream output;
    output << std::setprecision(digits_before_point(x) + precision) << x;
    return output.str();
}
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[2]: Простейшее форматирование плавающего в строку
От: watchmaker  
Дата: 10.09.19 15:07
Оценка: 19 (2)
Здравствуйте, rg45, Вы писали:

R>Ну если очень-очень нужно :)


R>https://ideone.com/uxF948


Коду нужно больше юниттестов :)
Например, format(0.0625) выведет три символа после точки: 0.062. А format(1e-20) выведет в экспоненциальной форме 1e-20.
Re[3]: Исправленная версия
От: rg45 СССР  
Дата: 10.09.19 20:22
Оценка: 5 (1)
Здравствуйте, watchmaker, Вы писали:

R>>https://ideone.com/uxF948


W>Коду нужно больше юниттестов

W>Например, format(0.0625) выведет три символа после точки: 0.062. А format(1e-20) выведет в экспоненциальной форме 1e-20.

Ну вот, исправил: https://ideone.com/xGqir9

Самым сложным оказалось добиться, чтоб 0.005 выводилось как 0.01 (при точности 2).

std::string make_string(double x, int precision)
{
    std::ostringstream output;
    output << std::setprecision(precision) << x;
    return output.str();
}

int digits_before_point(double x)
{
    return x ? int(floor(std::log(std::abs(x)) / std::log(10.0) + 1)) : 0;
}

std::string format(double x, int precision = 2)
{
    const auto dbp = digits_before_point(x);
    if (dbp + precision > 0)
    {
        return make_string(x, dbp + precision);
    }
    else if (dbp + precision == 0)
    {
        const double min_value = std::exp(-precision * std::log(10));
        if (x >= min_value / 2)
        {
            return make_string(min_value, 1);
        }
        else if(x <= -min_value / 2)
        {
            return make_string(-min_value, 1);
        }
    }
    return "0";
}
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[4]: Исправленная версия
От: watchmaker  
Дата: 10.09.19 22:40
Оценка: 7 (1)
Здравствуйте, rg45, Вы писали:

R>Ну вот, исправил: https://ideone.com/xGqir9

Всё равно иногда выводится по три символа после точки: например https://ideone.com/msG3Jo
Re: Простейшее форматирование плавающего в строку
От: c-smile Канада http://terrainformatica.com
Дата: 11.09.19 02:27
Оценка:
Здравствуйте, Went, Вы писали:


Как-то так (псевдо код)

int n = int(f * 100);


string s = itoa(n / 100);
int cents = n % 100;
if( cents ) {
  s += ".";
  s += itoa(cents / 10);
  if( cents % 10 ) 
    s += itoa(cents % 10);
}
Re[5]: Исправленная версия
От: rg45 СССР  
Дата: 11.09.19 04:46
Оценка: :)
Здравствуйте, watchmaker, Вы писали:

R>>Ну вот, исправил: https://ideone.com/xGqir9

W>Всё равно иногда выводится по три символа после точки: например https://ideone.com/msG3Jo

Задокументируем как фичу.

Ну а если серьезно, то понятно, что происходит. Фактический параметр: 99999999999999.90625 — мантисса слишком велика, что вызывает ожидаемую погрешность в математических выражениях. А так как число близко к целой степени десятки, функция digits_before_point выдает 15 вместо ожидаемых 14. Что поделаешь, все имеет свою погрешность и свою область применимости. Наверное, и это можно исправить, путем усложнения функции digits_before_point. Стоит ли игра свеч, зависит от строгости требований.

В то же время, судя потому, как тебе пришлось извратиться для получения нужного литерала, попасть в такую ситуацию не так-то просто
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 11.09.2019 6:20 rg45 . Предыдущая версия . Еще …
Отредактировано 11.09.2019 6:17 rg45 . Предыдущая версия .
Отредактировано 11.09.2019 6:16 rg45 . Предыдущая версия .
Отредактировано 11.09.2019 6:07 rg45 . Предыдущая версия .
Отредактировано 11.09.2019 6:05 rg45 . Предыдущая версия .
Отредактировано 11.09.2019 6:05 rg45 . Предыдущая версия .
Отредактировано 11.09.2019 5:52 rg45 . Предыдущая версия .
Отредактировано 11.09.2019 5:49 rg45 . Предыдущая версия .
Re[2]: Простейшее форматирование плавающего в строку
От: rg45 СССР  
Дата: 11.09.19 05:27
Оценка:
Здравствуйте, c-smile, Вы писали:


CS>Как-то так (псевдо код)


CS>
CS>int n = int(f * 100);
CS> . . .
CS>


И что получится, например, при f = 1e+10 ?
--
Не можешь достичь желаемого — пожелай достигнутого.
Re: Вариант №2 (постпроцессинг строк)
От: rg45 СССР  
Дата: 11.09.19 07:27
Оценка: 5 (1)
Здравствуйте, Went, Вы писали:

W>Доброго дня. Простите за ламерский вопрос, но все же

W>Мне нужно превратить стандартными средствами float в строку, чтобы соблюдались следующие условия:
W>1. Лишние нули не пишутся, лишняя точка тоже. 1.1000f -> "1.1", 4f -> "4"
W>2. Точность не больше двух знаков после запятой. 1.234 -> "1.23".
W>Все!
W>В С-шном формате есть %f и %g, но я нашел комбинации флагов, дающей нужного результата. Первый постоянно дописывает ненужные нули, даже если они не нужны, второе переводит в экспоненциальный формат, если число очень близко к нулю.
W>С++-ные варианты выдают подмножество С-шных, которые не устраивают.

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

https://ideone.com/2iDTT4

std::string format(double x, int precision = 2)
{
    std::ostringstream output;
    output << std::fixed << std::setprecision(precision) << x;
    std::string s = output.str();

    if (precision > 0)
    {
        const auto pos = s.find_last_not_of('0');
        assert(pos != std::string::npos);        

        if (pos != s.size() - 1)
        {
            const auto count = s[pos] != '.' ? pos + 1 : pos;
            return s.substr(0, count);
        }
    }
    return s;
}
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 11.09.2019 7:41 rg45 . Предыдущая версия .
Re: Простейшее форматирование плавающего в строку
От: Videoman Россия https://hts.tv/
Дата: 11.09.19 07:34
Оценка: 16 (3)
Здравствуйте, Went, Вы писали:

W>Мне нужно превратить стандартными средствами float в строку, чтобы соблюдались следующие условия:

W>1. Лишние нули не пишутся, лишняя точка тоже. 1.1000f -> "1.1", 4f -> "4"
W>2. Точность не больше двух знаков после запятой. 1.234 -> "1.23".
W>Все!

std::to_chars() c флагом std::chars_format::fixed делает то, что тебе нужно.
Кстати делает это исключительно быстро, т.к. ей плевать на локаль и подобную ересь
Отредактировано 11.09.2019 7:39 Videoman . Предыдущая версия .
Re[2]: Простейшее форматирование плавающего в строку
От: rg45 СССР  
Дата: 11.09.19 08:21
Оценка:
Здравствуйте, Videoman, Вы писали:

W>>Мне нужно превратить стандартными средствами float в строку, чтобы соблюдались следующие условия:

W>>1. Лишние нули не пишутся, лишняя точка тоже. 1.1000f -> "1.1", 4f -> "4"
W>>2. Точность не больше двух знаков после запятой. 1.234 -> "1.23".
W>>Все!

V>std::to_chars() c флагом std::chars_format::fixed делает то, что тебе нужно.


Я попытался адаптировать пример здесь под требования задачи, но компилер ругается на std::chars_format почему-то

Я усомнился, а действительно ли он отбрасывает лишние нули. Ну, допустим, отбрасывает, тогда вопрос, как добиться такого же результата, как при работе с потоками и манипулятором std::fixed — то есть, чтоб нули НЕ отбрасывались?
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 11.09.2019 8:22 rg45 . Предыдущая версия .
Re[3]: Простейшее форматирование плавающего в строку
От: Videoman Россия https://hts.tv/
Дата: 11.09.19 09:28
Оценка:
Здравствуйте, rg45, Вы писали:

R>Я усомнился, а действительно ли он отбрасывает лишние нули. Ну, допустим, отбрасывает, тогда вопрос, как добиться такого же результата, как при работе с потоками и манипулятором std::fixed — то есть, чтоб нули НЕ отбрасывались?


Я его использую при сериализации и десериализации. На что ругается компилятор?
Re[4]: Простейшее форматирование плавающего в строку
От: rg45 СССР  
Дата: 11.09.19 09:38
Оценка:
Здравствуйте, Videoman, Вы писали:

R>>Я усомнился, а действительно ли он отбрасывает лишние нули. Ну, допустим, отбрасывает, тогда вопрос, как добиться такого же результата, как при работе с потоками и манипулятором std::fixed — то есть, чтоб нули НЕ отбрасывались?


V>Я его использую при сериализации и десериализации. На что ругается компилятор?


Оригинальный пример здесь: https://en.cppreference.com/w/cpp/utility/to_chars
Адаптированный вариант: https://coliru.stacked-crooked.com/view?id=3ba817e3a6e479bf

Сообщение компилятора:
g++ -std=c++2a -pthread  -O2 -Wall -Wextra -pedantic -pthread -pedantic-errors main.cpp -lm  -latomic -lstdc++fs  && ./a.out
main.cpp: In function 'int main()':
main.cpp:12:14: error: 'std::chars_format' has not been declared
   12 |         std::chars_format::fixed, 2);
      |              ^~~~~~~~~~~~
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[5]: Простейшее форматирование плавающего в строку
От: Videoman Россия https://hts.tv/
Дата: 11.09.19 10:08
Оценка:
Здравствуйте, rg45, Вы писали:

R>Сообщение компилятора:

R>
R>g++ -std=c++2a -pthread  -O2 -Wall -Wextra -pedantic -pthread -pedantic-errors main.cpp -lm  -latomic -lstdc++fs  && ./a.out
R>main.cpp: In function 'int main()':
R>main.cpp:12:14: error: 'std::chars_format' has not been declared
R>   12 |         std::chars_format::fixed, 2);
R>      |              ^~~~~~~~~~~~
R>


Да... в VS 2017 вплоть до версии 15.9.16, to_chars с параметром precision — не реализована, помечена как =delete. Мне как раз нужна была другая реализация, как у автора топика, и я не доглядел насчет precision. Жалко, будем ждать пока Microsoft сподобится все реализовать по стандарту.

charconv from_chars() and to_chars() are available for integers. The timeline for floating-point from_chars() and floating-point to_chars() is as follows:

VS 2017 15.7: Integer from_chars() and to_chars().
VS 2017 15.8: Floating-point from_chars().
VS 2017 15.9: Floating-point to_chars() overloads for shortest decimal.
VS 2019 16.0: Floating-point to_chars() overloads for shortest hex and precision hex.
VS 2019 16.2: Floating-point to_chars() overloads for precision fixed and precision scientific.
Not yet implemented: The floating-point to_chars() overload for precision general.

Отредактировано 11.09.2019 10:11 Videoman . Предыдущая версия .
Re[6]: Простейшее форматирование плавающего в строку
От: Videoman Россия https://hts.tv/
Дата: 11.09.19 10:20
Оценка:
Здравствуйте, Videoman, Вы писали:

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


R>>Сообщение компилятора:

R>>
R>>g++ -std=c++2a -pthread  -O2 -Wall -Wextra -pedantic -pthread -pedantic-errors main.cpp -lm  -latomic -lstdc++fs  && ./a.out
R>>main.cpp: In function 'int main()':
R>>main.cpp:12:14: error: 'std::chars_format' has not been declared
R>>   12 |         std::chars_format::fixed, 2);
R>>      |              ^~~~~~~~~~~~
R>>


V>Да... в VS 2017 вплоть до версии 15.9.16, to_chars с параметром precision — не реализована, помечена как =delete. Мне как раз нужна была другая реализация, как у автора топика, и я не доглядел насчет precision. Жалко, будем ждать пока Microsoft сподобится все реализовать по стандарту.

V>

V>charconv from_chars() and to_chars() are available for integers. The timeline for floating-point from_chars() and floating-point to_chars() is as follows:

V>VS 2017 15.7: Integer from_chars() and to_chars().
V>VS 2017 15.8: Floating-point from_chars().
V>VS 2017 15.9: Floating-point to_chars() overloads for shortest decimal.
V>VS 2019 16.0: Floating-point to_chars() overloads for shortest hex and precision hex.
V>VS 2019 16.2: Floating-point to_chars() overloads for precision fixed and precision scientific.
V>Not yet implemented: The floating-point to_chars() overload for precision general.


P.S. Сорри не обратил внимание что вы на gсс.
Отредактировано 11.09.2019 10:21 Videoman . Предыдущая версия .
Re[6]: Простейшее форматирование плавающего в строку
От: Went  
Дата: 11.09.19 10:24
Оценка:
Здравствуйте, night beast, Вы писали:
NB>а тебе для чего? если для рублей, то лучше в целыми работать..
Для размеров в условных пикселях, которые могут быть и дробными, но редко
Re[2]: Простейшее форматирование плавающего в строку
От: Went  
Дата: 11.09.19 10:25
Оценка:
Здравствуйте, rg45, Вы писали:
R>Может, стоит таки смириться с лишними нулями? Это действительно принципиально, или у тебя чисто спортивный интерес?
Не стоит. Сильно захламляет вывод, потому что в 99% случаях все числа снапнуты к целым и мы работаем как с целыми. Но после некоторых преобразований данные могут стать дробными и пользователь должен это увидеть.
Re: Простейшее форматирование плавающего в строку
От: Went  
Дата: 11.09.19 10:36
Оценка:
Здравствуйте. Раз эта тема вызывала интерес, выложу свое неоптимальное, но простое решение (MFC )
CString rounded_float_to_string(Float value, Int precision)
{
  Float factor = pow(10.0f, precision);
  Int whole = round(value * factor);
  Float fract = whole / factor;
  CString result;
  result.Format("%g", fract);
  return result;
}

Вроде бы на практике работает. Но сваливается в экспоненту на очень больших числах.
Re[7]: Простейшее форматирование плавающего в строку
От: night beast СССР  
Дата: 11.09.19 11:19
Оценка:
Здравствуйте, Went, Вы писали:

NB>>а тебе для чего? если для рублей, то лучше в целыми работать..

W>Для размеров в условных пикселях, которые могут быть и дробными, но редко

думаю фиксед и для пикселей пойдет.
Re[2]: Простейшее форматирование плавающего в строку
От: rg45 СССР  
Дата: 11.09.19 12:37
Оценка: :))
Здравствуйте, Went, Вы писали:

W>Здравствуйте. Раз эта тема вызывала интерес, выложу свое неоптимальное, но простое решение (MFC )


??? MFC ???

--
Не можешь достичь желаемого — пожелай достигнутого.
Re[3]: Простейшее форматирование плавающего в строку
От: IID Россия  
Дата: 11.09.19 16:40
Оценка: +1
Здравствуйте, rg45, Вы писали:

R>И что получится, например, при f = 1e+10 ?


1) диапазон не оговаривался
2) если всё-таки надо — можно взять int64_t

kalsarikännit
Re[4]: Простейшее форматирование плавающего в строку
От: rg45 СССР  
Дата: 11.09.19 20:02
Оценка:
Здравствуйте, IID, Вы писали:

R>>И что получится, например, при f = 1e+10 ?


IID>1) диапазон не оговаривался


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

IID>2) если всё-таки надо — можно взять int64_t

IID>

Ну вместо 1e+10 напишем 1e+20 и получится то же самое
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 11.09.2019 20:13 rg45 . Предыдущая версия . Еще …
Отредактировано 11.09.2019 20:07 rg45 . Предыдущая версия .
Re[3]: Простейшее форматирование плавающего в строку
От: c-smile Канада http://terrainformatica.com
Дата: 11.09.19 21:57
Оценка:
Здравствуйте, rg45, Вы писали:

CS>>Как-то так (псевдо код)


CS>>
CS>>int n = int(f * 100);
CS>> . . .
CS>>


R>И что получится, например, при f = 1e+10 ?


Что надо то и получится. Я же написал что это псевдо код. Т.е. тот int может быть int64
в который точно поместится целочисленная мантисса от double (2^52).
Re[5]: Простейшее форматирование плавающего в строку
От: c-smile Канада http://terrainformatica.com
Дата: 11.09.19 22:17
Оценка:
Здравствуйте, rg45, Вы писали:

IID>>2) если всё-таки надо — можно взять int64_t

IID>>

R>Ну вместо 1e+10 напишем 1e+20 и получится то же самое


А что выведет твоя printf функция если ей дать 1e+20 число? Количество зеленых попугаев, а не число ибо все что дальше единиц и после запятой там уже будет случайными числами (в данном контексте).

Т.е. для doubles больше std::numeric_limits<double>::digits10 / 100 исходная задача вообще смысла не имеет. Такие вот, брат, пироги.
Re[6]: Простейшее форматирование плавающего в строку
От: rg45 СССР  
Дата: 12.09.19 05:04
Оценка:
Здравствуйте, c-smile, Вы писали:

R>>Ну вместо 1e+10 напишем 1e+20 и получится то же самое


CS>А что выведет твоя printf функция если ей дать 1e+20 число? Количество зеленых попугаев, а не число ибо все что дальше единиц и после запятой там уже будет случайными числами (в данном контексте).


Выведет ровно то же самое, что выводят стандартные потоки: https://ideone.com/2iDTT4. А главное, она никогда не породит UB.

CS>Т.е. для doubles больше std::numeric_limits<double>::digits10 / 100 исходная задача вообще смысла не имеет. Такие вот, брат, пироги.


Исходная задача
Автор: Went
Дата: 10.09.19
формулировалась так: "Мне нужно превратить стандартными средствами float в строку...". Стандартными, понимаешь? Ни одно из стандартных средств не накладывает никаких ограничений на диапазоны входных значений. Mы вполне можем вывести в стандартный поток и 1е+100, и 1e+300, и получим всего лишь потерю точности, но не UB, и не откровенную белиберду.
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 12.09.2019 13:31 rg45 . Предыдущая версия . Еще …
Отредактировано 12.09.2019 12:17 rg45 . Предыдущая версия .
Отредактировано 12.09.2019 7:45 rg45 . Предыдущая версия .
Отредактировано 12.09.2019 7:18 rg45 . Предыдущая версия .
Отредактировано 12.09.2019 7:17 rg45 . Предыдущая версия .
Отредактировано 12.09.2019 7:17 rg45 . Предыдущая версия .
Отредактировано 12.09.2019 6:44 rg45 . Предыдущая версия .
Отредактировано 12.09.2019 5:52 rg45 . Предыдущая версия .
Отредактировано 12.09.2019 5:51 rg45 . Предыдущая версия .
Отредактировано 12.09.2019 5:49 rg45 . Предыдущая версия .
Отредактировано 12.09.2019 5:47 rg45 . Предыдущая версия .
Отредактировано 12.09.2019 5:39 rg45 . Предыдущая версия .
Отредактировано 12.09.2019 5:38 rg45 . Предыдущая версия .
Отредактировано 12.09.2019 5:36 rg45 . Предыдущая версия .
Отредактировано 12.09.2019 5:34 rg45 . Предыдущая версия .
Отредактировано 12.09.2019 5:28 rg45 . Предыдущая версия .
Re[4]: Простейшее форматирование плавающего в строку
От: rg45 СССР  
Дата: 12.09.19 05:07
Оценка:
Здравствуйте, c-smile, Вы писали:


R>>И что получится, например, при f = 1e+10 ?


CS>Что надо то и получится. Я же написал что это псевдо код. Т.е. тот int может быть int64

CS>в который точно поместится целочисленная мантисса от double (2^52).

А при чем тут мантисса? Ты же в int загоняешь не мантиссу, а все число. А диапазон покрываемый double на многие порядки шире, чем диапазон, покрываемый int и int64, поэтому на абсолютно подавляющем подмножестве значений double мы получим UB. И замена int на int64 лишь слегка-слегка отодвигает проблему, но не решает ее (только задумайся о разнице между 1е+18 и 1е+308).

P.S. А вот если бы ты действительно поотдельности обработал мантиссу и экспоненциальную часть на целых числах, без UB, вот тогда бы я первый воскликнул "браво!"
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 12.09.2019 13:06 rg45 . Предыдущая версия . Еще …
Отредактировано 12.09.2019 8:41 rg45 . Предыдущая версия .
Отредактировано 12.09.2019 8:34 rg45 . Предыдущая версия .
Отредактировано 12.09.2019 6:16 rg45 . Предыдущая версия .
Отредактировано 12.09.2019 6:02 rg45 . Предыдущая версия .
Отредактировано 12.09.2019 5:58 rg45 . Предыдущая версия .
Re[5]: Простейшее форматирование плавающего в строку
От: c-smile Канада http://terrainformatica.com
Дата: 15.09.19 03:04
Оценка:
R>>>И что получится, например, при f = 1e+10 ?

CS>>Что надо то и получится. Я же написал что это псевдо код. Т.е. тот int может быть int64

CS>>в который точно поместится целочисленная мантисса от double (2^52).

R>А при чем тут мантисса? Ты же в int загоняешь не мантиссу, а все число. А диапазон покрываемый double на многие порядки шире, чем диапазон, покрываемый int и int64, поэтому на абсолютно подавляющем подмножестве значений double мы получим UB. И замена int на int64 лишь слегка-слегка отодвигает проблему, но не решает ее (только задумайся о разнице между 1е+18 и 1е+308).


double может хранить целочисленные значения. Но только в том случае если целое число помещается в поле мантиссы (52 бита).
Что есть заведомо меньше чем int64.

А для чисел больше 252 / 100 ( в данном случае) задача вывода цифирей после запятой смысла не имеет по определению — там будут случайные числа.

Т.е. в случае double и int64 мой алгоритм выведет точно то что нужно. Как и в случае float и int32.

Мне кажется что сие очевидно, нет?

На этом, кстати, построена вся целочисленная арифметика JavaScript: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER
Re[6]: Простейшее форматирование плавающего в строку
От: rg45 СССР  
Дата: 15.09.19 08:27
Оценка:
Здравствуйте, c-smile, Вы писали:



CS>А для чисел больше 252 / 100 ( в данном случае) задача вывода цифирей после запятой смысла не имеет по определению — там будут случайные числа.


CS>Мне кажется что сие очевидно, нет?


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

P.S. До предлагаемого тобой решения ТС без труда мог бы додуматься и сам, уж поверь мне, как его бывшему коллеге
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 15.09.2019 8:34 rg45 . Предыдущая версия . Еще …
Отредактировано 15.09.2019 8:28 rg45 . Предыдущая версия .
Re: Простейшее форматирование плавающего в строку
От: кт  
Дата: 26.09.19 06:28
Оценка: :)
Здравствуйте, Went, Вы писали:

W>Доброго дня. Простите за ламерский вопрос, но все же


В названии «простейший» кроется подвох: по заданию требуется искать замыкающий ноль, а это в стандартах, как правило, не предусмотрено. О чем здесь и говорили. И я так понял, что лишние пробелы не нужны (иначе, зачем нужно убирать замыкающий ноль?)
Тогда прямо по заветам Д.Ритчи: «если тебе нужен PL/1, ты знаешь, где его взять»

test_format:proc main;

dcl s char(*) var, x float(53);

do repeat;
   get list(x);
   if x     -trunc(x+0.5e0)     <5e-3 then put string(s) edit(x)(f(15));   else
   if x*10e0-trunc(x*10e0+0.5e0)<5e-2 then put string(s) edit(x)(f(15,1)); else
                                           put string(s) edit(x)(f(15,2));
   put list('отформатировано:',trim(s));
   put skip;
end repeat;

end test_format;


результат для положительных чисел:

1.0000
отформатировано: 1
4
отформатировано: 4
1.234
отформатировано: 1.23
1234e9
отформатировано: 1234000000000
Re[2]: Упс... и неправильно
От: кт  
Дата: 26.09.19 13:26
Оценка:
Упс… и неправильно, вместо 3.16 пишет не то,
тогда вот так:
test_format:proc main;

dcl s char(*) var, x float(53);

do repeat;
   get list(x);
   if x   -trunc(x)   <5e-3   then put string(s) edit(x)(f(15));   else
   if x   -trunc(x)   >0.99e0 then put string(s) edit(x)(f(15));   else
   if x*10-trunc(x*10)<5e-2   then put string(s) edit(x)(f(15,1)); else
   if x*10-trunc(x*10)>0.99e0 then put string(s) edit(x)(f(15,1)); else
                                   put string(s) edit(x)(f(15,2));
   put list('отформатировано:',trim(s));
   put skip;
end repeat;

end test_format;

и нужный результат получается:
3.00
отформатировано: 3
3.10
отформатировано: 3.1
3.11
отформатировано: 3.11
3.1000000000001
отформатировано: 3.1
3.15
отформатировано: 3.15
3.16
отформатировано: 3.16
534534534534.6667
отформатировано: 534534534534.67
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.