21xrx.com
2024-12-22 18:26:03 Sunday
登录
文章检索 我的文章 写文章
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类方法。

  
  

评论区

{{item['qq_nickname']}}
()
回复
回复