Can't use operator<< with std::float128_t; how do I print it?

970 Views Asked by At

I have the following code, which doesn't compile with x86_64 GCC 13:

#include <iostream>
#include <stdfloat>

int main() {
    std::cout << std::float128_t{1} << '\n';
}

This gives me the following error:

<source>: In function 'int main()':
<source>:5:15: error: ambiguous overload for 'operator<<' (operand types are 'std::ostream' {aka 'std::basic_ostream<char>'} and 'std::float128_t' {aka '_Float128'})
    5 |     std::cout << std::float128_t{1} << '\n';
      |     ~~~~~~~~~ ^~      ~~~~~~~~~~~~~
      |          |            |
      |          |            std::float128_t {aka _Float128}
      |          std::ostream {aka std::basic_ostream<char>}

The listed ambiguous overloads are:

  • operator<<(long)
  • operator<<(unsigned long)
  • operator<<(bool)
  • ...

Surprisingly, operator<<(float) and other floating point types aren't listed.

I've checked the compiler support page for C++23, and this feature should be supported:

C++23 features Paper(s) libstdc++ libc++ MVSV STL
Standard names and library
support for extended floating-point types
P1467R9 13 19.37**

See C++23 compiler support page

Am I misunderstanding something? Is cppreference wrong and extended floating-point types aren't fully supported yet? How do I print a std::float128_t without third-party libraries?

2

There are 2 best solutions below

0
On BEST ANSWER

operator<<(std::float128_t) is optional

None of the operator<< overloads for extended floating-point types from <stdfloat> are guaranteed to exist. For std::float128_t it is quite common that this isn't the case. On x86_64:

  • long double is typically an 80-bit floating point type, and
  • std::float128_t is a quadruple precision IEEE-754 floating-point type.

This means that std::float128_t has a greater conversion rank1) than long double. As a result, operator<<(std::float128_t) is optional:

Otherwise, if the floating-point conversion rank of extended-floating-point-type is less than or equal to that of long double, the formatting conversion occurs as if it performed the following code fragment:

bool failed = use_facet<num_put<charT, ostreambuf_iterator<charT, traits>>>(getloc()).put(*this, *this, fill(), static_cast<long double>(val)).failed();

Otherwise, an invocation of the operator function is conditionally supported with implementation-defined semantics.

- [ostream.formatted]/[ostream.inserters.arithmetic] §5

GCC isn't required to support it, and you should consider alternatives to operator<< when printing extended floating point types.

Alternatives

#include <iostream>
#include <stdfloat>
#include <format>

int main() {
    std::cout << std::format("{}", std::float128_t{1}) << '\n';
}

This solution currently works, and is guaranteed to work. The std::formatter for extended floating-point types is implemented using std::to_chars, which is required to support all arithmetic types.

#include <print>
#include <stdfloat>

int main() {
    std::println("{}", std::float128_t{1});
}

This solution doesn't work yet because libstdc++ doesn't implement the <print> header yet. However, once it does, this will also work, because std::println also uses std::formatter.

If std::format works, why is operator<< not supported?

The proposal paper answers this question:

The streaming operators use the virtual functions num_put<>::do_put and num_get<>::do_get for output and input of arithmetic types. To fully and properly support extended floating-point types, new virtual functions would need to be added. That would be an ABI break. While an ABI break is not out of the question, it would have strong opposition. This proposal is not worth the effort that would be necessary to get an ABI break through the committee.

- P1467r9 §iostream


1) The conversion rank is greater because std::float128_t can represent more values than long double, see [conv.rank] §2.

7
On

The old school solution is libquadmath, which comes with GCC by default.

#include <iostream>
#include <quadmath.h>

int main()
{
    __float128 x = 12.3; // `__float128` should be equivalent to `std::float128_t`.
    char buf[200];
    quadmath_snprintf(buf, sizeof buf, "%Qf", x);
    std::cout << buf << '\n';
}

Add -lquadmath when linking.