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?
operator<<(std::float128_t)
is optionalNone 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, andstd::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.
#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
.
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
andnum_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.
1) The conversion rank is greater because std::float128_t
can represent more values than long double
, see [conv.rank] §2.