无法使用operator<<来处理std::float128_t;我该如何打印它?

huangapple go评论85阅读模式
英文:

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 &lt;iostream&gt;
#include &lt;stdfloat&gt;

int main() {
    std::cout &lt;&lt; std::float128_t{1} &lt;&lt; &#39;\n&#39;;
}

This gives me the following error:

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

The listed ambiguous overloads are:

  • operator&lt;&lt;(long)
  • operator&lt;&lt;(unsigned long)
  • operator&lt;&lt;(bool)
  • ...

Surprisingly, operator&lt;&lt;(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_putnum_get<>::do_get 来输出和输入算术类型。要完全和正确地支持扩展浮点类型,需要添加新的虚拟函数。这将导致 ABI 打破。虽然 ABI 打破并非不可能,但将会遭到强烈反对。这个提案不值得付出通过委员会的 ABI 打破所需的努力。


1) 转换等级更大,因为 std::float128_t 可以表示比 long double 更多的值,参见 [conv.rank] §2

英文:

operator&lt;&lt;(std::float128_t) is optional

None of the operator&lt;&lt; overloads for extended floating-point types from &lt;stdfloat&gt; 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 rank<sup>1)</sup> than long double.
As a result, operator&lt;&lt;(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
&gt; bool failed = use_facet&lt;num_put&lt;charT, ostreambuf_iterator&lt;charT, traits&gt;&gt;&gt;(getloc()).put(*this, *this, fill(), static_cast&lt;long double&gt;(val)).failed();
&gt;

> 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&lt;&lt; when printing extended floating point types.

Alternatives

#include &lt;iostream&gt;
#include &lt;stdfloat&gt;
#include &lt;format&gt;

int main() {
    std::cout &lt;&lt; std::format(&quot;{}&quot;, std::float128_t{1}) &lt;&lt; &#39;\n&#39;;
}

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 &lt;print&gt;
#include &lt;stdfloat&gt;

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

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

If std::format works, why is operator&lt;&lt; not supported?

The proposal paper answers this question:

> The streaming operators use the virtual functions num_put&lt;&gt;::do_put and num_get&lt;&gt;::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


<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 &lt;iostream&gt;
#include &lt;quadmath.h&gt;

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

Add -lquadmath when linking.

huangapple
  • 本文由 发表于 2023年6月18日 18:44:39
  • 转载请务必保留本文链接:https://go.coder-hub.com/76500128.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定