【问题标题】:Full precision display of floating point numbers in C++?C++中浮点数的全精度显示?
【发布时间】:2013-11-05 18:30:29
【问题描述】:

我已经阅读了几个关于在 C++ 中显示浮点数的主题,但我找不到令人满意的答案。

我的问题是: 如何在 C++ 中以科学格式(尾数/指数)显示浮点数的所有有效数字?

问题是所有数字在基数 10 中的有效位数都不相同。

例如,double 具有 15 到 17 位有效小数位精度,但 std::numeric_limits<double>::digits10 返回 15,因此,对于某些数字,我将失去 2 个额外的小数位精度。

【问题讨论】:

  • 那么,使用precision(std::numeric_limits<double>::digits10 + 2)?当然,如果最后两位数字是“通过您的计算生成的随机垃圾(例如1.00000000000005 + 100 - 100 [假设编译器没有将 +100 - 100 删除为无意义,例如因为这些是当时未知的变量])。它应该注意的是,例如 glibc 的 printf 将产生“垃圾”,如果您将 double value = 1.234E+300 打印为 printf("%f", value); - 将有大约 280 位“随机性”)
  • @Mats Petersson 垃圾在旁观者的眼中。如果打印 2^-n 的所有 n 位,则没有一个是垃圾。
  • @RickRegan:所以你认为 1234000000000001278847239713129823712387918732982173971923... 是比 1234000000000000000000000000000000000000000... [我在工作中正在处理的 printf 是在做后者,但当然当我使用 glibc 作为参考时,我必须考虑到差异]。我不知道cout 在这种情况下做了什么。
  • @MatsPetersson:通过计算产生的“随机垃圾”主要出现在digits10以外的数字中,这通常是错误的。这些数字是在从十进制到浮点的转换中可能不会保留的数字,它们只是受浮点中最小可能舍入影响的数字。一旦执行了不止一次的计算,错误可能会以多种方式复合,产生的复合错误范围从零到无穷大,视情况而定。
  • @MatsPetersson:此外,对于那些了解浮点并仔细使用它的人来说,数字并不是没有意义的。是的,我们使用所有可用信息。除此之外,为了能够再次读取原始数字而需要写入的位数通常超过digits10。因此,您必须有更多的数字。

标签: c++ floating-point iostream floating-point-precision iomanip


【解决方案1】:

std::numeric_limits<double>::digits10提供可以安全恢复的十进制位数,即在往返十进制中保留的十进制位数->double->十进制。假设更多的十进制数字是正确的并没有帮助。如果你想促进往返double->decimal->double,你会使用std::numeric_limits<double>::max_digits10

如果您想要使用std::numeric_limits<double>::digits 的确切值,但它会以一种有趣的方式显示从十进制值转换而来的数字,因为它们通常是四舍五入的值。这也是为什么max_digits10 在呈现数字以供人类消费时没有用的原因:最后几个数字通常不是人类读者所期望的。

【讨论】:

    【解决方案2】:

    在 C++20 中,您将能够使用 std::format 来执行此操作:

    std::cout << std::format("{}", M_PI);
    

    输出(假设 IEEE754 double):

    3.141592653589793
    

    默认浮点格式是具有往返保证的最短十进制表示。与使用来自std::numeric_limitsmax_digits10 的精度相比,此方法的优势在于它不会打印不必要的数字。例如:

    std::cout << std::setprecision(
      std::numeric_limits<double>::max_digits10) << 0.3;
    

    同时打印0.29999999999999999

    std::cout << std::format("{}", 0.3);
    

    打印0.3 (godbolt)。

    同时你可以使用the {fmt} librarystd::format是基于。 {fmt} 还提供了print 函数,使这更容易和更高效(godbolt):

    fmt::print("{}", M_PI);
    

    免责声明:我是 {fmt} 和 C++20 std::format 的作者。

    【讨论】:

      【解决方案3】:

      你看过std::max_digits10吗?

      来自cppreference

      std::numeric_limits&lt;T&gt;::max_digits10 的值是 以 10 为基数唯一表示所有不同的数字 T 类型的值,例如对于 序列化/反序列化为文本。这个常数对于 所有浮点类型。

      这意味着(以及我如何使用它)是文本输出可以复制/粘贴到另一个程序中,并且数字将代表相同的数字

      现在,我必须说我的工作马格式始终是右对齐的%23.16E,并且我对最后几位数字使用了工程判断。我喜欢它,因为对于符号、指数和十六位数字来说已经足够了。

      -----------------------
      -1.1234567812345678E+12
      

      现在,请注意 digits 的精度和 decimal digits 的精度不一定是同一件事。

      【讨论】:

      • +1 用于推荐打印双精度数字的良好默认值。我自己使用% 23.16e,如有必要,留出打印标志的空间。
      • @njuffa 我认为我们的格式相同。在我的情况下,我“右对齐”%23.16Evia 标志(而不是格式声明中的 via 空格),以便为可选符号留出空间。这是一个很好的格式!
      猜你喜欢
      • 2014-03-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-18
      • 1970-01-01
      • 2010-12-19
      • 1970-01-01
      • 2011-04-18
      相关资源
      最近更新 更多