将双精度浮点数转换为字符数组C++

6
我将尝试将一个双精度浮点数转换为字符数组并进行四舍五入,但是我收到了随机的内存文本。我做错了什么?
这是我的代码:
double d = 1.0929998; 
d = std::round(d * 100) / 100;

char s[sizeof(d)];
std::memcpy(s,&d,sizeof(d));

结果:

s: q=×£pñ?

预期值:

s:1.09


使用 to_string 有什么问题吗? - Scott Hunter
6
一个 double 不是一个 char 数组。如果这很简单,就不需要使用 std::to_string 了。将浮点数值转换为文本字符串涉及到大量的工作。对于一个比较简单的问题,可以尝试将一个 int 值转换为字符串。一旦你成功实现了这个,就可以开始考虑浮点数转换了。或者你可以直接使用内置库设施。上次我编写浮点数到文本转换函数花费了几周时间。这不是初学者的任务。 - Pete Becker
1
如果 std::to_string 如此令人厌恶,也许像 sprintf 这样的 C API 可以满足您的需求? - ShadowRanger
你可能会觉得通过某个不想使用 sprintf 方法的人找到这个问题的答案很有趣。基本上是通过进行一些数学运算来获取数字的大小,然后通过重复除法来逐位获取字符串表示形式中的数字。https://dev59.com/K3E95IYBdhLWcg3wdtqj - Ryan
8个回答

8
您正在将您的 double 字节直接转换为 char。其中,double 值是二进制表示(通常类似于 IEEE 754),而 char 是字符的二进制表示(通常基于 ASCII)。这两者不兼容。

不幸的是,这意味着您必须进行某种转换过程。要么使用您不想使用的 std::to_string(),要么使用更复杂的 std::stringbuf(它在底层会调用 std::to_string())。


所以我正在研究这样的内容:double d = 1.0909998; d = std::round(d * 100) / 100; char s[4]; std::strncpy(s,std::to_string(d).c_str(), 4);用于进行四舍五入的双精度转换? - Delrog
1
@Delrog 我建议使用 snprintf 而不是 strncpy+to_string - user253751
5
你可以让STL为你做舍入。使用std::ostringstream,看一下std::fixedstd::setprecision()流操作器,例如:ostringstream oss; oss << fixed << setprecision(3) << d; string s = oss.str(); - Remy Lebeau
谢谢,我通常会避免使用stringstream,因为我认为它们会带来额外的开销。它们真的会吗?有没有一种方法可以在(cppreference或其他文档)上快速确定标准库函数的内存/性能成本? - Delrog

3
你正在将一个 double(它是一个数字的二进制表示)复制到一个 char 数组中;这些字节没有理由对应于数字字符。

2

这个没有经过测试,但你可以尝试:

std::string to_string(double x)
{
    std::ostringstream ss;
    ss << x;
    return ss.str();
}

您可以将返回的字符串字符放入char数组中,如下所示:

char arrayName[] = returnedString.toCharArray();

std::string str = to_string(doubleValue);
char digits[str.length()];

感谢Remy Lebeau的评论,您可以执行以下操作:

std::strncpy(digits, to_string(doubleValue).c_str(), sizeof(digits))

或者这样:
to_string(doubleValue).copy(digits, sizeof(digits))

那是一个等待发生缓冲区溢出的问题。至少使用 std::setprecision() 来控制 ostringstream 输出的数字位数。 - Remy Lebeau
您的编辑仍然存在风险。std::strcpy()没有进行任何边界检查。请至少使用std::strncpy()或者std::string::copy()代替,这样您就可以指定char[]缓冲区的最大大小,例如:std::strncpy(digits, to_string(doubleValue).c_str(), sizeof(digits))或者to_string(doubleValue).copy(digits, sizeof(digits)) - Remy Lebeau
@Remy Lebeau 好的,我会修改的,谢谢你指出来。 - 0xCursor

2

如果不使用to_string,您可以按照以下步骤进行转换:

char* to_char_array(double num_double, int decimal_place)
{
    int num_int = std::round(num_double * pow(10, decimal_place));
    int sign = num_int < 0 ? 1 : 0;
    num_int = abs(num_int);

    if (num_int == 0)
    {
        char* s = (char*)malloc(decimal_place + 3);
        s[0] = '0';
        s[1] = '.';
        for (int i = 2; i < decimal_place + 2; i++)
            s[i] = '0';
        s[decimal_place + 2] = '\0';
        return s;
    }

    int digit_count = 1;
    int n = num_int;
    if (n >= 100000000) { digit_count += 8; n /= 100000000; }
    if (n >= 10000) { digit_count += 4; n /= 10000; }
    if (n >= 100) { digit_count += 2; n /= 100; }
    if (n >= 10) { digit_count++; }

    int size = digit_count + 1 + (decimal_place > 0 ? 1 : 0) + sign;
    char* s = (char*)malloc(size);

    for (int i = 0, integer = num_int; integer != 0; integer /= 10) {
        s[size - 2 - i++] = integer % 10 + 48;
        if (decimal_place > 0 && i == decimal_place)
            s[size - 2 - i++] = '.';
    }
    s[size - 1] = '\0';
    if (sign)
        s[0] = '-';
    return s;
}

void main()
{
    double d = -12.268904;
    char* s = to_char_array(d, 3);
    cout << "double: " << d << " - char array: " << s << endl;
    system("pause");
}

1

首先执行这个操作

std::ostringstream strs;
strs << dbl;
std::string str = strs.str();

这将把双精度数 dbl 转换为字符串 str
然后你可以像这样将其转换为字符数组:
char *cstr = new char[str.length()+1];
str.copy(cstr, str.length());
cstr[str.length()] = '\0';
// use cstr as needed...
delete[] cstr;

或者,简单地说:

const char *cstr = str.c_str();
// use cstr as needed...

这与在问题中提到的使用std::to_string有何不同? - scohe001
@scohe001 to_string是C++11的函数。与stringstream不同,旧版本编译器将无法支持它。 - Sebastian D'Agostino

1

如果你想手动转换而不使用to_string,那么你需要一次一个地提取数字。双精度浮点数并不是以一组字符的形式存储在内存中的,因此你不能只是复制内存并希望它能正常工作。(想了解更多关于双精度浮点数如何被解释,请参见这里)。


0
使用itoa函数将小数点向右移动两位应用于双精度浮点数,然后通过在最后两个数字之前添加小数点来创建结果的副本:
void convertToCharArray(double value, char *rezult)
{
  int k;

  char aux[10];

  itoa(value*100, aux, 10);
  int n = strlen(aux);
  k = 0;
  for(int i=0;i<n;i++)
  {
    if(i == n-1 - 1)
    {
      rezult[k++] = '.';
    }
    rezult[k++] = aux[i];
  }
  rezult[k++] = '\0';
}

用法:

int main()
{
    char aux[10];
    convertToCharArray(27.77, aux);
    cout << aux;
    return 0;
}

0

只需要这样做:

  #include <iostream>
  #include <string>
  #include <sstream>

  using namespace std;

  int main()
  {
      double a=23.25;
      char b[12];
      stringstream ss;

      ss << a;
      ss >> b;
      cout << b << endl;

      return 0;
  }

或者这样:

  #include <iostream>
  #include <string>
  #include <sstream>
  #include <stdio.h>


  using namespace std;

  int main()
  {


  double a=23.25;
  char b[12];
  stringstream ss  ;
  ss<<a;
  sprintf(b,ss.str().c_str());
  cout<<b<<endl;
  return 0;

或者这样:

here don't forget to
 #include<bits/stdc++.h>
  
  double a=23.25;
  char b[12];
  stringstream ss  ;
  ss<<a;
  strcpy(b,ss.str().c_str());
  cout<<b<<endl;
  return 0;


如果您解释每组代码为什么解决了问题,以及每个代码之间的区别(例如性能、易用性等),那么这将不仅对OP有用,而且对未来的谷歌搜索者也有用。 - Studocwho

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接