【问题标题】:Does std::scientific always result in normalized scientific notation for floating-point numbers?std::scientific 是否总是导致浮点数的标准化科学记数法?
【发布时间】:2018-11-08 01:39:15
【问题描述】:

科学记数法定义了应如何使用符号、数字和指数来显示数字,但它并没有说明可视化是标准化的。

举例:-2.34e-2(标准化科学记数法)与-0.234e-1(科学记数法)相同

我可以依赖以下代码始终产生标准化结果吗? 编辑:除了答案中指出的 NAN 和 INF。

template<typename T>
static std::string toScientificNotation(T number, unsigned significantDigits)
{
    if (significantDigits > 0) {
        significantDigits--;
    }
    std::stringstream ss;
    ss.precision(significantDigits);
    ss << std::scientific << number;
    return ss.str();
}

如果是,请在 C++ 文档/标准中列出一个部分,说明它不是平台/实现定义的。由于 0 的值也以不同的方式表示,我担心某些非常小的数字(非规范化?!)可能会以不同的方式可视化。在我的编译器平台上,它目前适用于 std::numeric_limits::min()、denorm_min()。

注意:我使用它来查找数字的数量级,而不会弄乱浮点数分析的所有古怪细节。我希望标准库为我做这件事:-)

【问题讨论】:

  • 如果您想要数量级,为什么不使用简单的std::log10
  • 因为这会产生错误的结果。一个例子:999.0f 的大小为 2。当用于公制前缀表示法时,这是错误的,因为您需要考虑有效数字。 3 个有效数字的大小为 2,但 2 个有效数字的大小为 3,因为该值四舍五入为 1000。简而言之:999.0f 有 2 个 sig 数字 ->“1.0k”,999.0f 有 3 个 sig 数字 ->“999”
  • “错误的结果” - 好吧,你应该在你的问题中添加你想要的东西。您可以随时使用对数/舍入和乘法来获得您想要的结果。

标签: c++ floating-point scientific-notation


【解决方案1】:

我可以依赖以下代码始终产生标准化结果吗?

没有任何保证,不。更好的说法是:标准并没有像您希望的那样强加保证。

std::scientific 仅在以下相关部分引用:

  1. [floatfield.manip]:2

    ios_base& scientific(ios_base& str);  
    

    效果:调用str.setf(ios_­base​::​scientific, ios_­base​::​floatfield)
    返回str

  2. Table 101 — fmtflags effects

    | Element    | Effect(s) if set                                       |
    | ...        | ...                                                    |
    | scientific | generates floating-point output in scientific notation |
    | ...        | ...                                                    |
    

【讨论】:

  • ͏+͏1͏͏͏͏͏͏͏͏͏͏͏͏͏͏͏͏͏͏͏͏͏͏͏͏͏͏͏͏͏͏͏。我确实发现 ISO 没有定义科学记数法是令人惊讶的。
  • @Bathsheba 也许您确实有一份 ISO C++ 文档的正确副本,引用了另一个定义“科学记数法”的 ISO?
  • 非常感谢!您是否知道以度量前缀表示法(m、µ、G 等)可视化浮点数的高级方法?我的代码经过了很好的单元测试,并且有一种可靠的方法来确定数字的数量级也很有帮助。 SO和其他地方的大量代码完全是错误的。它们仅涵盖最明显的场景。我需要一个防弹解决方案,不能相信标准或 boost 中没有任何内容:-(
  • @FrozenTarzan 我不知道。如果存在,您将需要一个特定的工具,或者构建您自己的工具。如果您下定决心并选择后者并发现自己陷入困境,因此您在 SO 上提出问题,请在此处联系我;)
  • @YSC 我会对您对已接受答案的评论非常感兴趣,这样我就可以睡得更好,保持我的代码这样 ;-)
【解决方案2】:

是的,除了零、无穷大和 NaN。

C++标准是指格式化的C标准,需要标准化的科学记数法。

  • [floatfield.manip]/2

    ios_base& scientific(ios_base& str);
    

    效果:调用str.setf(ios_­base​::​scientific, ios_­base​::​floatfield)

    返回:str

  • [ostream.inserters.arithmetic]/1(部分)

    operator<<(float val);
    operator<<(double val);
    operator<<(long double val);
    

    效果:num_­get&lt;&gt;num_­put&lt;&gt; 类处理依赖于区域设置的数字格式和解析。 这些插入器函数使用注入的 locale 值来执行数字格式。当val 的类型为...、doublelong double、...时,格式转换就像执行了以下代码片段一样:

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

    val 的类型为float 时,格式转换就像执行以下代码片段一样:

    bool failed = use_facet<
      num_put<charT, ostreambuf_iterator<charT, traits>>
        >(getloc()).put(*this, *this, fill(),
          static_cast<double>(val)).failed();
    
  • [facet.num.put.virtuals]/1:5.1(部分)

    • 第一阶段:

      阶段 1 的第一个操作是确定转换说明符。描述此确定的表使用以下局部变量

      fmtflags flags = str.flags();
      fmtflags floatfield = (flags & (ios_base::floatfield));
      

      对于浮点类型的转换,该函数确定浮点转换说明符,如表 70 所示。

      表 70 - 浮点转换

      | State                                            | stdio equivalent |
      | ------------------------------------------------ | ---------------- |
      | floatfield == ios_­base​::​scientific && !uppercase | %e               |
      | floatfield == ios_­base​::​scientific               | %E               |
      

      第 1 阶段结束时的表示由char组成,将通过调用printf(s, val) 打印,其中s 是上面确定的转换说明符。 p>

  • C11 n1570 [7.21.6.1]:8.4

    • e,E

      代表浮点数的double 参数在 样式 [−]d.ddde±dd其中有一位数字(如果 参数非零)在小数点字符之前和 后面的数字等于精度;如果精度缺失,则取6;如果精度为零且未指定 # 标志,则无小数点 字符出现。该值四舍五入到适当的位数。 E 转换说明符生成一个带有 E 而不是 e 的数字 引入指数。指数始终包含至少两位数字, 并且只需要尽可能多的数字来表示指数。 如果 值为零,指数为零。

      表示无穷大或 NaN 的 double 参数以 fF 转换说明符的样式进行转换。

【讨论】:

  • 哇,谢谢!这个答案非常详细,正是我想要的! :-)
  • 这是一个很好的答案。我完全错过了那张 70 桌,干得好!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-25
  • 1970-01-01
  • 2021-04-21
  • 2011-10-04
  • 2012-03-15
相关资源
最近更新 更多