0

I have a requirement to store very small double numbers into a string format and then later reverse them. However, when I try to run std::to_string() on a small number like 4.7816457028269855e-143 then it simply makes it 0.

I referred Set precision of std::to_string when converting floating point values and its linked duplicate. But will setting the precision to a very large number solve this issue in all the platforms?

How to fix this issue?
Using any alternatives to to_string() is fine.

iammilind
  • 68,093
  • 33
  • 169
  • 336
  • Hmm, it's not even that small, as DBL_MIN = 2.2250738585072013830902327173324040642192159804623318306e-308 – JHBonarius Oct 22 '20 at 11:51
  • 1
    If you are looking for high fidelity for round trip, and you want text rather than binary, and you don't care if the text isn't very legible, you can use the floating point hexadecimal format. – Eljay Oct 22 '20 at 12:23
  • 1
    @Eljay or if you can require new enough compilers, you could use https://en.cppreference.com/w/cpp/utility/to_chars – Dan M. Oct 23 '20 at 11:04

2 Answers2

2

No, the problem is not precision, but the format. You want to print in scientific format (with exponent) but std::to_string() uses the fixed format by default and I'm not aware of any way to change this.

However, streams use scientific if appropriate or you can force it with std::scientific:

std::ostringstream oss;
oss << 4.7816457028269855e-143;
std::string numberAsString = oss.str(); // stores "4.78165e-143"  

Of course you can increae the precision in addition to this.

If for whatever reason you don't want to use scientific format, you can use the fixed format with a high enough precision. "High enough" meaning more than 142 in this case, because there will be 142 leading zeroes:

oss << std::fixed << std::setprecision(142 + x);

But the scientific format is better suited, I guess.

Lukas-T
  • 11,133
  • 3
  • 20
  • 30
  • I don't have any requirement to store it as a scientific format. It can be any format. Main concern is that, it should not convert it to 0.0. Can you edit your answer accordingly. – iammilind Oct 22 '20 at 12:09
  • @iammilind And that's exactly what this code does. Did you try it? Btw. you **need** scientific format. How many leading zeroes would this number have in fixed format? – Lukas-T Oct 22 '20 at 12:22
2

As already mentioned, it's impossible to achieve this with std::to_string, but outputting the number to std::ostringstream with large enough precision will solve your issue.

The required precision can be computed in a cross-platform way using the C++ Standard Library facilities, specifically the std::numeric_limits<T>::max_digits10 constant: https://en.cppreference.com/w/cpp/types/numeric_limits/max_digits10

For example:

std::string to_string_exact(double x) {
  std::ostringstream os;
  os << std::setprecision(std::numeric_limits<double>::max_digits10) << x;
  return os.str();
}

See also https://possiblywrong.wordpress.com/2015/06/21/floating-point-round-trips/ for some caveats.

Mikhail Maltsev
  • 1,632
  • 11
  • 21
  • Even without setting the precision, I see that the `ostringstream` solves the problem. However is it cross platform? – iammilind Oct 22 '20 at 12:17
  • It's in the std namespace so obviously it must be cross platform, because it's defined by the C++ standard – phuclv Oct 22 '20 at 12:30