Namespaces
Variants
Views
Actions

Talk:cpp/string/basic string/to string

From cppreference.com

[edit] Not round-trip safe.

Please mention that std::to_string is not round-trip-safe for floating point numbers. Please mention "not round-trip-safe" explaining that converting a double to a std::string and then back to a double will not yield the same number in every case.

Please provide an example of how to perform a round-trip-safe conversion, e.g. maybe something similar to this:

#include <iostream>
#include <cstdio>
#include <iomanip>
#include <string>
#include <limits>
#include <sstream>
#include <cassert>
 
void print(const char *msg, double numBefore, const std::string &sNum, bool maybeLucky = false)
{
    double numAfter;
    std::istringstream(sNum) >> numAfter;
    std::cout << msg << sNum;
    if (numBefore == numAfter) {
        std::cout << " \t(round-trip-safe!";
        if (maybeLucky) {
            std::cout << " - lucky here: too simple number";
        }
        std::cout << ')';
    }
    std::cout << '\n';
}
 
void run(const double x)
{
    const auto maxDig10 = std::numeric_limits<decltype(x)>::max_digits10;
    std::ostringstream oss;
    oss << std::setprecision(maxDig10) << x;
    const std::string sNum    = oss.str();
 
    oss.str(""); oss.clear();
    oss << std::setprecision(maxDig10-1) << std::scientific << x;
    const std::string sNumFixed = oss.str();
 
    char sNumAlt[1000]; // must be large enough (todo: what is just-just large enough??)
    std::snprintf(sNumAlt, sizeof(sNumAlt), "%.*g", maxDig10, x);
 
    const std::string sNumBad = std::to_string(x);
 
    print("ostringstream                      -> ", x, sNum);
    print("ostringstream with std::scientific -> ", x, sNumFixed);
    print("snprintf                           -> ", x, sNumAlt);
    print("std::to_string()                   -> ", x, sNumBad, true);
}
 
int main()
{
    for (double x : {3.3333333333333333333, 0.00033333333333333333333333, 1., 1000000000000000000000., std::numeric_limits<double>::lowest()}) {
        run(x);
        std::cout << '\n';
    }
}

(By the way: how could something so ugly (std::to_string not doing the normal good sane thing), get into the standard??)

The notes allude to this and the example makes it obvious. That said, it is changing behaviour in C++26 to print the same way std::to_chars does (i.e. shortest representation that preserves round trip) --Ybab321 (talk) 11:49, 26 July 2023 (PDT)
The size of char sNumAlt[X]; "just-just large enough" buffer could be estimated like this:
#include <limits>
#include <iostream>
 
int main()
{
    using your_numeric_type_t = long double; // or any other of arithmetic types
 
    constexpr std::size_t X
        = std::numeric_limits<your_numeric_type_t>::max_exponent10
        + std::numeric_limits<your_numeric_type_t>::max_exponent10 / 3 + 1
            // for possible locale dependent thousands separators
        + 3 // for sign, null-terminator, digital dot
        + 6 // trailing 6 digits of "fixed" format
        + 1 // rounding and safety.)
        ;
    std::cout << X << '\n';
}

Possible output:

6587
--Space Mission (talk) 15:59, 26 July 2023 (PDT)