【问题标题】:iomanip set at most 4 digits after decimal pointiomanip 设置小数点后最多 4 位
【发布时间】:2018-10-01 03:57:16
【问题描述】:

我想使用 iomanip 打印以筛选一些小数点后最多 4 位的数字。

我了解到,在默认模式下,setprecision 不仅计算小数点后的数字,还计算整数部分中的数字。这段代码

#include <iostream>
#include <iomanip>

int main () {
    double numbers[] = {3.141516, 1.01, 200.78901, 0.12345};
    int len = sizeof(numbers) / sizeof(numbers[0]);

    std::cout << std::setprecision(4);
    for (int i = 0; i < len; ++i) {
        std::cout << numbers[i] << '\n';
    }
    return 0;
}

输出:

3.142
1.01
200.8
0.1235

但我想要的是:(小数点后最多 4 位不带尾随零)

3.1415
1.01
200.789
0.1235

iomanip 有能力做到这一点吗?不使用其他技巧(如round)?

编辑

好像我说得不够清楚。我的问题是iomanip 具体

我只想知道iomanip 是否能够做我所描述的事情,因为iomanip 据说是输入/输出操纵器的标准库。发布的问题是

iomanip 能做到这一点吗?

更像是“是否支持”而不是“给我任何解决方案”。

我再次搜索了它,查找了 iomanip 参考资料,希望找到一种简洁紧凑的方式来格式化最多为 n 数字的浮点数,尽可能少地使用不必要的库。

而且似乎没有标准的方法来实现这一点。

【问题讨论】:

标签: c++


【解决方案1】:

获得 OP 所需输出的一个(丑陋)选项是用所需的最大精度表示数字,然后删除不需要的零:

#include <iostream>
#include <iomanip>
#include <sstream>
#include <vector>

int main()
{
    std::vector<double> numbers {
        3.141516, 1.01, 200.78901, 0.12345, 9.99999
    };

    for (auto x : numbers)
    {
        // "print" the number
        std::stringstream ss;
        ss << std::fixed << std::setprecision(4) << x;

        // remove the unwanted zeroes
        std::string result{ss.str()};
        while (result.back() == '0')
            result.pop_back();

        // remove the separator if needed   
        if (result.back() == '.')
            result.pop_back();

        std::cout << result  << '\n';
    }

    std::cout << "\nJustified:\n";
    for (auto x : numbers)
    {
        // "print" the number
        std::stringstream ss;
        ss << std::fixed << std::setprecision(4) << std::setw(15) << x;

        // remove the unwanted zeroes
        std::string result{ss.str()};
        auto it = result.rbegin();
        while (*it == '0')
            *it++ = ' ';

        // remove the separator if needed   
        if (*it == '.')
            *it = ' ';

        std::cout << result  << '\n';
    }
}

现场示例:https://ideone.com/8zP17O

【讨论】:

    【解决方案2】:

    所以:

    • std::fixedfield 模式下,std::setprecision 设置有效数字的最大数量,而不是小数位;
    • 如果您转到std::fixed,则表示准确小数位数;
    • C++ 不提供任何类似于%.Nf printf 格式字符串的功能! (最多 N 个小数位)

    我刚刚拼凑了一个小小的假 I/O 操纵器,它可以得到我们都想要的行为。它的性能不是非常好,但它可以完成工作:

    #include <iostream>
    #include <iomanip>
    #include <string>
    #include <sstream>
    #include <cstdio>
    
    struct sprintf_wrapper
    {
        sprintf_wrapper(std::string fmt_str)
            : _fmt_str(std::move(fmt_str))
        {}
    
        struct proxy
        {
            proxy(const sprintf_wrapper& wrapper, std::ostream& os)
                : _wrapper(wrapper)
                , _os(os)
            {}
    
            std::ostream& Resolve(const double value) const
            {
                // First find out how many characters we're going to need
                const int len = std::snprintf(nullptr, 0, _wrapper._fmt_str.c_str(), value);
                if (len < 0)
                {
                    _os.setstate(std::ios::failbit);
                    return _os;
                }
    
                // Then allocate a buffer
                std::string result;
                result.resize(len);
    
                // Actually serialise the value according to the format string
                if (std::sprintf(result.data(), _wrapper._fmt_str.c_str(), value) < 0)
                {
                    _os.setstate(std::ios::failbit);
                    return _os;
                }
    
                // Stream it out
                _os.write(result.data(), result.size());
                return _os;
            }
    
            friend std::ostream& operator<<(const proxy& obj, const double val)
            {
                return obj.Resolve(val);
            }
    
        private:
            const sprintf_wrapper& _wrapper;
            std::ostream& _os;
        };
    
        friend proxy operator<<(std::ostream& os, const sprintf_wrapper& obj)
        {
            return proxy(obj, os);
        }
    
    private:
        std::string _fmt_str;
    };
    
    inline auto sprintf_f(size_t n, const bool showpos = false)
    {
        std::stringstream fmt;
        fmt << '%';
        if (showpos) fmt << '+';
        fmt << '.' << n << 'f';
    
        return sprintf_wrapper(fmt.str());
    }
    
    int main()
    {
        std::cout << sprintf_f(2) << 4.123456789 << '\n';
        std::cout << sprintf_f(3) << 4.123456789 << '\n';
        std::cout << sprintf_f(4) << 4.123456789 << '\n';
        std::cout << sprintf_f(5) << 4.123456789 << '\n';
        std::cout << sprintf_f(6) << 4.123456789 << '\n';
    }
    

    (live demo)

    【讨论】:

      【解决方案3】:

      使用std::fixedstd::setprecision 的组合。

      #include <iomanip>
      #include <iostream>
      
      int main() {
          double d = 311.3456;
          std::cout << std::fixed;
          std::cout << std::setprecision(4);
          std::cout << d << std::endl;
      }
      

      【讨论】:

      • std::fixed 将打印尾随零,但我不希望那样。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-01-12
      • 1970-01-01
      • 1970-01-01
      • 2019-12-22
      • 1970-01-01
      • 2017-05-04
      相关资源
      最近更新 更多