【问题标题】:how to prevent numbers from showing up in scientific notations如何防止数字出现在科学记数法中
【发布时间】:2016-05-17 10:30:44
【问题描述】:

我们有一个 StreamBuffer 类,我们没有在其中实现 std::fixed 操作,我试图防止数字出现在科学计数法中。使用我下面的代码,一些数字以科学计数法显示。我们希望避免进行任何分配,因此出于性能原因,我们实现了StreamBuffer 类。

下面是代码:

T value = 0;

template<typename U> void process(U& buf, DataOption holder) const {
    if (holder == DataOption::TYPES) {
        switch (type_) {
        case teck::PROC_FLOAT:
            buf << "{\"float\":" << value << "}";
            break;
        case teck::PROC_DOUBLE:
            buf << "{\"double\":" << value << "}";
            break;
        default:
            buf << "{\"" << type_ << "\":" << value << "}";
        }
    }
}

这就是它的调用方式:

void HolderProcess::dump(std::ostream& os, DataOption holder) const 
{
    process<std::ostream>(os, holder);
}

void HolderProcess::dump(StreamBuffer& buffer, DataOption holder) const
{
    process<StreamBuffer>(buffer, holder);
}

我尝试使用如下所示的方式,但出现错误,据我了解我们无法在 StreamBuffer 类上使用 std::fixed

case teck::PROC_DOUBLE:
    buf << "{\"double\":" << std::fixed << value << "}";

std::fixed 的替代品是什么,我可以在这里使用它根本不做任何分配。我正在考虑将数字转换为字符串,然后在其上应用std::fixed,但这也会进行一些分配,我想避免这种情况。

执行此操作的最佳方法是什么,既能提高性能又不进行任何分配?我有一个下面的解决方案,但它会在使用字符串时进行一些分配。我可以从上面的代码中调用下面的方法。

template <typename T> string str(T number)
   {
       std::ostringstream ss;
       ss << std::fixed << number;
       return ss.str();
   }

还有其他优化高效的方法吗?

【问题讨论】:

  • 你遇到了什么错误?
  • StreamBuffer 是否继承自 std::streambuf?因为fixed 是在格式化的I/O 级别实现的,远高于streambuf
  • 它不是从 streambuf 继承的,我也无法更改该类来做到这一点,因为它已经被使用了很长时间,它会破坏其他使用它的地方,所以试图看看一些替代和有效的方法
  • 你还没有真正明确你想要什么。什么数字以指数形式出现?如果你输入 1.23e+20,你真的想要一个固定的数字吗?
  • 是的,这就是我的意思.. 很抱歉造成混乱.. 有些数字是指数形式的,我想为这些数字设置一个固定的数字。在上述每个案例块中,价值都以指数形式出现,我希望它得到修复。

标签: c++ scientific-notation


【解决方案1】:

StreamBuffer 类必须继承自 std::ios_base(或它的一些派生类,例如 std::ostream)才能实现您的预​​期行为。 std::fixed 只能用于作为 STL 的一部分提供的衍生实现。

此外,如果您可以访问 std::ios_base,您还可以使用std::ios_base::precision

如果您遇到无法更新类的情况,那么最常用和最传统的方法是缩放浮点数。为了减少重复,请查看已回答的问题here。例如,对于 3 级精度,我会将所有“值”实例替换为:

// case teck::PROC_FLOAT:
static_cast<float>( static_cast<int>(value*1000) ) / 1000
// case techk::PROC_DOUBLE:
static_cast<double>( static_cast<long long>(value*1000) ) / 1000

更好地理解提问者的要求。我已经意识到上述内容不适用于指数。为了解决这个问题,我建议执行以下操作:

case teck::PROC_FLOAT:
        std::stringstream ss;

        ss << std::fixed << value;
        buf << "{\"float\":" << ss.str() << "}";
        break;

然而,这肯定会分配更多的内存。


【讨论】:

  • 我不认为我们的 StreamBuffer 类继承自 std::ios_basestd::ostream。我主要使用 Java,所以对 C++ 没有太多想法,但它肯定不会继承那些。
  • 所以,有你的问题。如果您只是将 StreamBuffer 更改为从其中任何一个继承,您应该没问题。
  • 你还在吗?我现在根本无法更改这门课。由于很长一段时间都是这样,而且工作正常,所以我需要保持这样,让科学记数法在没有这样的情况下工作。有没有其他方法可以在这里使用或在 StreamBuffer 类中添加一些方法来做到这一点?
  • 我明白了。我在答案中添加了第三个选项,请看一下。
  • 我尝试在正常的主要方法中使用科学计数法提供数字,但它没有转换为没有科学计数法的数字?如果可能的话,您能否提供一个我的问题的示例,以便我了解这将如何工作?
【解决方案2】:

只需使用 snprintf:

#include <cstdio>
#include <limits>
#include <iostream>

int main() {
    double value = 0.1234567890123456789;
    const int Precision = std::numeric_limits<double>::digits10;
    const std::size_t StringBufferSize = Precision + 3; // sign, dot and terminating zero.
    char str[StringBufferSize];
    std::snprintf(str, StringBufferSize - 1, "%.*f", Precision, value);
    str[StringBufferSize - 1] = 0;
    // buf << "{\"double\":" << str << "}";
    std::cout << str << '\n';
}

【讨论】:

  • 感谢您的建议。我只需要为我的开关块中的double 案例执行此操作?或者floatlongint 也是如此。在我的问题中,我只展示了两个 case 块,但我会有更多类似 longintfloatdouble。此外,如果我只打印值,我看不到它是以科学记数法的形式打印的。
  • 您可以将它用于任何适合双精度的类型,但您也可以考虑为不同类型使用不同的格式(查看 printf-family 格式字符串的文档)
  • 我用指数试过这个,但它不工作。知道有什么问题吗?例如尝试使用此值1.23e+006
【解决方案3】:

在我看来你应该试试CppFormat。有一些示例说明如何使用它来格式化here

【讨论】:

    猜你喜欢
    • 2022-10-10
    • 1970-01-01
    • 2016-02-21
    • 1970-01-01
    • 1970-01-01
    • 2023-03-07
    • 1970-01-01
    • 1970-01-01
    • 2022-01-19
    相关资源
    最近更新 更多