英文:
Can't use operator<< with std::float128_t; how do I print it?
问题
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** |
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?
英文:
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<br>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?
答案1
得分: 14
operator<<
(std::float128_t
) 是可选的
<stdfloat>
中对于扩展浮点类型的 operator<<
重载都不保证存在。对于 std::float128_t
来说,这通常不成立。
在 x86_64 上:
long double
通常是一个 80 位浮点类型,而std::float128_t
是一个四倍精度 IEEE-754 浮点类型。
这意味着 std::float128_t
拥有比 long double
更大的转换等级1)。
因此,operator<<
(std::float128_t
) 是可选的:
否则,如果扩展浮点类型的浮点转换等级小于或等于 long double 的等级,则格式化转换的执行方式就如下代码片段所示:
bool failed = use_facet<num_put<charT, ostreambuf_iterator<charT, traits>>>(getloc()).put(*this, *this, fill(), static_cast<long double>(val)).failed();
否则,调用操作函数的支持情况由实现定义的语义条件下支持。
GCC 不要求支持它,当打印扩展浮点类型时,您应考虑其他替代方案。
替代方案
#include <iostream>
#include <stdfloat>
#include <format>
int main() {
std::cout << std::format("{}", std::float128_t{1}) << '\n';
}
这个解决方案目前可行,并且保证可行。
扩展浮点类型的 std::formatter
是使用 std::to_chars
实现的,该函数必须支持所有算术类型。
#include <print>
#include <stdfloat>
int main() {
std::println("{}", std::float128_t{1});
}
这个解决方案目前还不可行,因为 libstdc++ 尚未实现 <print>
头文件。
然而,一旦实现,这也将可行,因为 std::println
也使用了 std::formatter
。
如果 std::format
可行,为什么不支持 operator<<
?
提案文件回答了这个问题:
流操作符使用虚拟函数
num_put<>::do_put
和num_get<>::do_get
来输出和输入算术类型。要完全和正确地支持扩展浮点类型,需要添加新的虚拟函数。这将导致 ABI 打破。虽然 ABI 打破并非不可能,但将会遭到强烈反对。这个提案不值得付出通过委员会的 ABI 打破所需的努力。
1) 转换等级更大,因为 std::float128_t
可以表示比 long double
更多的值,参见 [conv.rank] §2。
英文:
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, andstd::float128_t
is a quadruple precision IEEE-754 floating-point type.
This means that std::float128_t
has a greater conversion rank<sup>1)</sup> 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:
> cpp
> 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.
<sup>1)</sup> The conversion rank is greater because std::float128_t
can represent more values than long double
, see [conv.rank] §2.
答案2
得分: 2
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.
英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论