5

Since the 1990s it is known how to print floating-point numbers quickly and accurately. The Scheme and Java programming languages have implemented this algorithm, but I couldn't find anything similar in C++.

Essentially, I'm looking for a small, efficient piece of code that satisfies the following test cases:

void test(double dbl, const char *expected) {
    std::string actual = ...;
    assert(actual == expected);
}

test(3.0, "3.0");
test(3.1, "3.1");
test(0.1, "0.1");
test(1.0 / 3.0, "0.3333333333333333");   // Or maybe one more digit?

The double literal is converted to a floating-point number, which may or may not be the same as the literal. Then, the floating-point number is converted back to a string. This string should be as short as possible and at the same time represent the decimal number that, when interpreted as a double literal, will yield the same floating-point number again.

How do I print a double value with full precision using cout? looks related, but the code from the accepted answer doesn't handle the 3.1 case correctly.

Roland Illig
  • 40,703
  • 10
  • 88
  • 121
  • 1
    There is actually no such floating-point number as 3.1. If you're seeing output that is not `"3.1"`, then you _are_ getting an accurate result!! Can you be more clear about what results you are looking for? – Lightness Races in Orbit Jan 27 '19 at 00:13
  • 3
    @LightnessRacesinOrbit I suspect he's talking about printing the shortest representation that yields back the exact same `double`. – Matteo Italia Jan 27 '19 at 00:21
  • I added that in the question. Thanks for the hint. – Roland Illig Jan 27 '19 at 00:21
  • 3
    maybe related: https://stackoverflow.com/questions/54370268/java-style-printing-of-double-in-c – NathanOliver Jan 27 '19 at 00:22
  • 2
    in C# there's the `R` format specifier for round-trip conversion [How to know if a double string is round-trip safe?](https://stackoverflow.com/q/12069664/995714). The source code is available so you can [check how it's done](https://stackoverflow.com/q/24299692/995714). [Compute the shortest decimal representation of a IEEE 754 double-precision binary floating-point number](https://codegolf.stackexchange.com/q/42119/13151), [The Shortest Decimal String That Round-Trips May Not Be The Nearest](https://www.exploringbinary.com/the-shortest-decimal-string-that-round-trips-may-not-be-the-nearest/) – phuclv Jan 27 '19 at 01:42
  • @RolandIllig Can you give the precise definition of the return value? It is not obvious what `1.0 / 3.0` should convert to since you can't describe it precisely with finite number of digits. – L. F. Jan 27 '19 at 02:30
  • @L.F.: The definition is in the question. `1.0/3.0` is a floating-point value, having been computed with floating-point arithmetic. (Using IEEE-754 basic 64-bit binary, it will be 0.3333333432674407958984375.) When we convert it to a decimal numeral with some number of digits and then convert it back to the floating-point format, we may get the same number (0.3333333432674407958984375) or a different number. If we use all the digits of the exact numeral, we will get the same number. But a smaller number might suffice, depending on the specifics of the numbers involved… – Eric Postpischil Jan 27 '19 at 03:52
  • … The desired output is the **shortest** decimal numeral with that property, that converting it back yields the original floating-point number. (There may be some incidental details such as including a mathematically unnecessary 0 before the “.” for silly human aesthetics.) – Eric Postpischil Jan 27 '19 at 03:54
  • @L.F.: There are formal definitions of this in the Java and/or ECMAScript (Javascript) specifications I can dig up if desired. – Eric Postpischil Jan 27 '19 at 03:56
  • [This answer](https://stackoverflow.com/a/49386744/298225) has a reference to the ECMAScript specification. – Eric Postpischil Jan 27 '19 at 04:00
  • This property of printing the shortest form is very important for interpreted languages such as Smalltalk Python Ruby etc... especially when they are used as REPL. The 1st requirement is that every two different float should print differently. The naive solution: always output many digits (like 17 for IEEE double) somehow works, but you don't get the short form you entered. Thus this second requirement. Scheme had it right, not a surprise that it can be used as a REPL. It's a pity that such important feature did not enter in stdlib or std c++ yet. – aka.nice Jan 27 '19 at 15:54
  • Per NathanOliver, which I noticed after providing a very similar answer: possible duplicate of [Java-style printing of \`double\` in C++](https://stackoverflow.com/questions/54370268/java-style-printing-of-double-in-c) – Davis Herring Jan 28 '19 at 06:03
  • @EricPostpischil for an IEEE double, 17 decimal digits are always enough. No need to use all the digits. – n. m. could be an AI Jan 28 '19 at 07:03
  • 1
    @n.m.: 17 significant digits always suffices for IEEE-754 basic 64-bit binary floating-point. They are not always necessary. For many values, a shorter string suffices. The question asks for the shortest string that suffices. – Eric Postpischil Jan 28 '19 at 09:05

1 Answers1

1

Your task can be solved using std::to_chars function appeared in C++17: https://en.cppreference.com/w/cpp/utility/to_chars

Sample solution:

#include <cassert>
#include <charconv>
#include <string>

void test(double dbl, const char *expected) {
    std::string actual;
    actual.resize(64);
    auto end = std::to_chars(actual.data(), actual.data() + actual.size(), dbl).ptr;
    actual.resize(end - actual.data());
    assert(actual == expected);
}

int main() {
    test(3.0, "3");
    test(3.1, "3.1");
    test(0.1, "0.1");
    test(1.0 / 3.0, "0.3333333333333333"); 
}

Demo in Compiler Explorer: https://gcc.godbolt.org/z/G7zrTPKTh

Fedor
  • 17,146
  • 13
  • 40
  • 131
  • 1
    The code looks very low-level. Given that it solves a very simple task, I call it outright ugly and bloated, due to its large amount of low-level memory handling and size fiddling. But as this seems to be the best solution that C++ can offer as of 2022, so be it. I know that you are [only the messenger](https://en.wikipedia.org/wiki/Shooting_the_messenger). :) – Roland Illig Jan 29 '22 at 21:33