C++17字符转换

将整型如int或浮点型double转换为字符串或反之在C++中有什么方法吗? 可以使用sprintf函数或者sscanf,也可以使用std::to_string,还可以使用 boost::lexical_cast。printf函数族的算法在过去几十年没有大的改进,to_string和printf执行时间差不多,lexical_cast最慢。 2018年,google的一位工程师提出了ryu算法和ryu printf ,能在不同平台上将printf速度提升4-24倍。微软的标准库中charconv使用了ryu算法。charconv的form_chars和to_chars实现很不容易,微软的杰出开发stephan t. lavavej花了一整年的时间。如何使用呢?参加例子中对from_chars和to_chars的使用。

#include <charconv>
#include <iostream>
#include <array>
int main()
{
  {
    std::array<char, 1000> str{"1.21111111111111111888e-111"};
    double                 result;
    if (auto [p, ec] = std::from_chars(str.data(), str.data() + str.size(), result, std::chars_format::scientific);
        ec == std::errc()) {
      std::cout << result;
      std::cout << std::endl;
    }
  }
  //--------------
  {
    std::array<char, 1000> str;
    double                 result = 0.10000000000000001;
    if (auto [p, ec] = std::to_chars(str.data(), str.data() + str.size(), result); ec == std::errc()) {
      std::cout << std::string_view(str.data(), p - str.data());
      std::cout << std::endl;
    }
    result = 0.1000000000000000055523435123322155555;
    if (auto [p, ec] = std::to_chars(str.data(), str.data() + str.size(), result); ec == std::errc()) {
      std::cout << std::string_view(str.data(), p - str.data());
      std::cout << std::endl;
    }
  }
}

上述例子的to_chars将打印出0.1,这是因为0.1的二进制浮点表示和0.1000000000000000055523435123322155555以及0.10000000000000001相同,to_chars会使用最少的十进制有效数字字符串来表示一个相同二进制的浮点数,参见The Shortest Decimal String That Round-Trips

然而from_chars和to_chars的输入参数都是起始指针和结束指针, charconvutils对from_chars和to_chars做了封装,可以传入range如char数组或者容器,使用起来更方便一些。引自charconvutils的例子:

#include "charconvutils.hpp"
#include <array>

int main() {
  std::array<char, 7> a{ '8', '6', '7', '5', '3', '0', '9' };
  unsigned int value;

  if (auto [ptr, ec] = charconvutils::from_chars(a, value); ec != std::errc{}) {
    // CONVERSION FAILED
  }

  std::array<char, 8> buffer{}; // initialize array with '\0's
  unsigned int input{8675309};
  
  if (auto [ptr, ec] = charconvutils::to_chars(buffer, input); result.ec != std::errc{}) {
    // CONVERSION FAILED
  }
}

很好用吧,赶快试试吧。

Posted 2019-04-25