21xrx.com
2025-04-14 23:01:49 Monday
文章检索 我的文章 写文章
C++高性能double到string的转换
2023-07-03 02:28:58 深夜i     --     --
C++ 高性能 double string 转换

C++作为一门广泛应用于科学计算、游戏开发和嵌入式系统领域的编程语言,其中double到string的转换是一项非常普遍且关键的操作。在一些对计算速度要求较高的应用场景中,高性能的转换算法可以显著提升程序的运行效率。本文将介绍C++实现高性能double到string的转换算法。

1.最简单的实现方法:ostringstream

C++标准库提供了ostringstream类可以将double类型转换为字符串。该类通过预分配一个字符串缓冲区暂存转换结果,在确定结果长度后将其复制到新的string对象中。

#include <sstream>
#include <string>
using namespace std;
string doubleToString(double n) {
  ostringstream os;
  os << n;
  return os.str();
}

尽管这种方法简单易行,但其在性能上的表现较差。在CPU缓存行发生不命中时,频繁的内存操作会导致数据读取速度下降,进而影响程序的性能。

2.快速实现方法:高效使用sprintf函数

C语言的sprintf函数可以将double类型转换为字符串,其实现过程和使用ostringstream类进行转换的方法类似。但是在不同的编译器中,sprintf具有不同的表现。在特定编译器下,sprintf显著快于ostringstream的实现方法。但是,因为可能存在不同的表现,所以这种方法需要进行benchmark测试,以确定其是否是当前情况下最快的算法。

#include <cstdio>
#include <string>
using namespace std;
string doubleToString(double n) {
  char buf[30];
  sprintf(buf, "%.17g", n);
  return string(buf);
}

3.最优实现方法:Dragon4算法

由于ostringstream类和sprintf函数需要确定输出字符串的长度,因此它们需要对double类型的位数进行四舍五入和舍弃前导零等处理。相对而言,Dragon4算法是一种更为优秀高效的实现方法,它可以克服sprintf和ostringstream类的这一限制。这种算法的主要思想是使用基于整数的算法,建立一个除法器来获得指数和小数。Dragon4算法是介于“最快”和“最简单”之间的算法,因此被广泛应用于许多现代编译器中。

可参考的实现代码如下:

#include <string>
#include <cstdio>
#include <cmath>
#include <limits.h>
using namespace std;
static const char *digit_chars = "0123456789";
string doubleToString(double n) {
  static char buf[32];
  char* p = buf;
  if (isnan(n)) {
    sprintf(buf, "nan");
    return string(buf);
  }
  if (isinf(n)) {
    sprintf(buf, "inf");
    return string(buf);
  }
  if (n <= 0) {
    if (n == 0) {
      sprintf(buf, "0");
      return string(buf);
    }
    *p++ = '-';
    n = -n;
  }
  int exponent;
  n = frexp(n, &exponent); //分解n的指数和尾数
  exponent += 1022;    //增加偏移量
  int whole_digits = 0;
  bool useExpNotation = false;
  if (exponent < 0 || exponent > 2047)
    useExpNotation = true;
  
  if (useExpNotation) {
    *p++ = digit_chars[static_cast<int>(n * 10)];
    *p++ = '.';
    whole_digits = 0;
  }
  else {
    char one;
    while (n > 0 || whole_digits < 17) {
      double n10 = n * 10;
      whole_digits++;
      int d = static_cast<int>(floor(n10));
      n = n10 - d;
      *p++ = digit_chars[d];
    }
    exponent += whole_digits - 1; //整个数要移动的位数
    one = *buf;
    if (whole_digits == 1 && one == '0') {
      exponent++;
      memmove(buf, buf + 1, whole_digits + 1); //将小数点移动一位
      whole_digits = 0;
    }
    *p++ = '.';
  }
  char digits[20];
  char* pdigits = digits;
  while (exponent != 0) {
    int new_exponent = exponent / 10;
    int digit = exponent - new_exponent * 10;
    exponent = new_exponent;
    *pdigits++ = digit_chars[digit];
  }
  *pdigits-- = '\0';
  size_t len = pdigits - digits;
  while (len > 0) {
    *p++ = *pdigits--;
    len--;
  }
  if (p[-1] == '.')
    p--;
  
  if (useExpNotation) {
    *p++ = 'e';
    int exp_abs = abs(exponent) + 1;
    *p++ = exponent < 0 ? '-' : '+';
    *p++ = digit_chars[exp_abs / 100];
    *p++ = digit_chars[(exp_abs / 10) % 10];
    *p++ = digit_chars[exp_abs % 10];
  }
  return string(buf, p - buf);
}

总的来说,在需要频繁进行double到string转换的项目中,可以使用Dragon4算法进行高性能的实现。当需要考虑可移植性时,使用sprintf较为稳妥;如果需要简单的实现,可以使用ostringstream类方法。

  
  

评论区