【问题标题】:Convert float to string with precision & number of decimal digits specified?将浮点数转换为指定精度和小数位数的字符串?
【发布时间】:2015-05-25 21:00:46
【问题描述】:

如何在 C++ 中将浮点数转换为字符串,同时指定精度和小数位数?

例如:3.14159265359 -> "3.14"

【问题讨论】:

  • 为什么不创建一个具有指定精度的临时浮点数,然后将其转换为字符串?
  • @Gilad “临时浮点”没有“指定精度”,在某些情况下这种方法会失败。这个问题基本上就像在问“'%.2f'格式的等价物是什么”?
  • 如果这只是 IO 的需要,请参阅 stackoverflow.com/questions/14432043/c-float-formatting

标签: c++ string floating-point


【解决方案1】:

典型的方法是使用stringstream:

#include <iomanip>
#include <sstream>

double pi = 3.14159265359;
std::stringstream stream;
stream << std::fixed << std::setprecision(2) << pi;
std::string s = stream.str();

fixed

使用固定浮点表示法

str 流的floatfield 格式标志设置为fixed

floatfield 设置为fixed 时,浮点值使用定点表示法写入:该值由精度字段指定的小数部分中完全相同的位数表示 em> (precision) 并且没有指数部分。

setprecision


对于技术目的的转换,例如将数据存储在 XML 或 JSON 文件中,C++17 定义了to_chars 系列函数。

假设一个兼容的编译器(我们在撰写本文时缺乏), 可以考虑这样的事情:

#include <array>
#include <charconv>

double pi = 3.14159265359;
std::array<char, 128> buffer;
auto [ptr, ec] = std::to_chars(buffer.data(), buffer.data() + buffer.size(), pi,
                               std::chars_format::fixed, 2);
if (ec == std::errc{}) {
    std::string s(buffer.data(), ptr);
    // ....
}
else {
    // error handling
}

【讨论】:

    【解决方案2】:

    做这种事情的习惯方法是“打印到字符串”。在 C++ 中,这意味着使用 std::stringstream 类似:

    std::stringstream ss;
    ss << std::fixed << std::setprecision(2) << number;
    std::string mystring = ss.str();
    

    【讨论】:

      【解决方案3】:

      另一个选项是snprintf:

      double pi = 3.1415926;
      
      std::string s(16, '\0');
      auto written = std::snprintf(&s[0], s.size(), "%.2f", pi);
      s.resize(written);
      

      Demo。应添加错误处理,即检查written &lt; 0

      【讨论】:

      • 为什么snprintf 在这种情况下会更好?请解释一下......它肯定不会更快,产生更少的代码[我已经编写了一个 printf 实现,它相当紧凑,代码不到 2000 行,但宏扩展达到大约 2500 行]
      • @MatsPetersson 我坚信它会更快。这就是我首先做出这个答案的原因。
      • @MatsPetersson 我已经执行了测量(GCC 4.8.1,-O2),表明 snprintf 确实花费了更少的时间,因子 17/22。我会尽快上传代码。
      • @MatsPetersson 我的基准测试可能存在缺陷,但在 1000000 次迭代(Clang 3.7.0、libstdc++、-O2)时,stringstream 版本的耗时是 snprintf 版本的两倍。
      • 请记住,仅从 C++11 标准开始,直接写入 std::string 拥有的缓冲区是安全的。
      【解决方案4】:

      在这里,我提供了一个反例,您希望在将浮点数转换为字符串时避免。

      float num=99.463;
      float tmp1=round(num*1000);
      float tmp2=tmp1/1000;
      cout << tmp1 << " " << tmp2 << " " << to_string(tmp2) << endl;
      

      你得到

      99463 99.463 99.462997
      

      注意:num 变量可以是接近 99.463 的任何值,您将得到相同的打印输出。关键是要避免方便的 c++11“to_string”函数。我花了一段时间才摆脱这个陷阱。最好的方法是 stringstream 和 sprintf 方法(C 语言)。 C++11 或更高版本应提供第二个参数作为要显示的浮点数之后的位数。现在默认值为 6。我设置这个是为了让其他人不会在这个问题上浪费时间。

      我写了我的第一个版本,如果您发现任何需要修复的错误,请告诉我。您可以使用 iomanipulator 控制确切的行为。我的功能是显示小数点后的位数。

      string ftos(float f, int nd) {
         ostringstream ostr;
         int tens = stoi("1" + string(nd, '0'));
         ostr << round(f*tens)/tens;
         return ostr.str();
      }
      

      【讨论】:

        【解决方案5】:

        你可以使用 C++20 std::format:

        #include <format>
        
        int main() {
          std::string s = std::format("{:.2f}", 3.14159265359); // s == "3.14"
        }
        

        或者the {fmt} library中的fmt::format函数,std::format基于(godbolt):

        #include <fmt/core.h>
        
        int main() {
          std::string s = fmt::format("{:.2f}", 3.14159265359); // s == "3.14"
        }
        

        2 是一个精度。

        它不仅比使用 iostreams 或 sprintf 更短,而且还比使用 significantly faster 更短,并且不受语言环境的影响。

        【讨论】:

          【解决方案6】:

          这里是一个只使用标准的解决方案。但是请注意,这只会向下取整。

              float number = 3.14159;
              std::string num_text = std::to_string(number);
              std::string rounded = num_text.substr(0, num_text.find(".")+3);
          

          对于rounded,它产生:

          3.14
          

          代码将整个浮点数转换为字符串,但在“.”之后将所有字符切割 2 个字符

          【讨论】:

          • 唯一的问题是这样你实际上并没有四舍五入。
          • @ChrCury78 正如我的回答中所述,这只四舍五入。如果你想要一个正常的四舍五入,只需在你的值后面的数字上加 5。例如 number + 0.005 在我的例子中。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-11-15
          • 2020-02-03
          • 1970-01-01
          • 1970-01-01
          • 2020-08-29
          相关资源
          最近更新 更多