Convert double to Char Array C++
Asked Answered
G

8

6

I'm trying to convert (and round) a double to a char array without converting with a std::to_string on the double first. However, I'm receiving random memory text instead. What am I doing wrong?

Here is my code:

double d = 1.0929998; 
d = std::round(d * 100) / 100;

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

Result:

s: q=×£pñ?

Intended value:

s: 1.09

Griqua answered 6/6, 2018 at 0:12 Comment(4)
What is wrong with using to_string?Macedonian
A double is not an array of char. If it was that simple there would be no need for std::to_string. There's a lot of work involved in converting a floating-point value to a text string. For a somewhat simpler problem, try converting an int value into a string. Once you've got that working you can start to think about floating-point conversions. Or you can just use the built-in library facilities. The last time I wrote a floating-point to text conversion function it took several weeks. This is not a beginner's task.Angele
If std::to_string is so abhorrent, perhaps C APIs like sprintf would do what you need?Takashi
You might find the answers to this question by someone who didn't want to use sprintf interesting. Pretty much doing some math to get the magnitude, then getting a digit at a time for the string representation by dividing repeatedly. https://mcmap.net/q/57081/-convert-a-float-to-a-stringScreech
F
8

You are translating the literal bytes of your double into chars. The double value is a binary representation (usually something like IEEE 754) while a char is a binary representation of a character (usually something based on ASCII). These two are not compatible.

Unfortunately, this means that you must do some kind of conversion process. Either the std::to_string() that you said you don't want to do, or a more complicated std::stringbuf (which will call std::to_string() under the hood anyway)

Fourflush answered 6/6, 2018 at 0:22 Comment(4)
So I'm looking at something like : double d = 1.0909998; d = std::round(d * 100) / 100; char s[4]; std::strncpy(s,std::to_string(d).c_str(), 4); For a double conversion with rounded truncation?Griqua
@Griqua I'd suggest using snprintf instead of strncpy+to_stringCantor
You could just let the STL do the rounding for you. Using a std::ostringstream, look at the std::fixed and std::setprecision() stream manipulators, eg: ostringstream oss; oss << fixed << setprecision(3) << d; string s = oss.str();Trinitarianism
Thanks, I usually try to steer clear from stringstreams though because I assume that they come with overhead. Do they? Is there a way to determine the memory/ performance cost of Standard library functions at a (cppreference or alternative docu) glance?Griqua
M
3

You are copying a double (which is a binary representation for a number) into a char array; there is no reason those bytes should correspond to digit characters.

Macedonian answered 6/6, 2018 at 0:17 Comment(0)
B
2

This isn’t tested but you could try:

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

You could then put the returned string’s characters into a char array like this:

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

And then, thanks to the comment from Remy Lebeau, you can do either this:

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

or this:

to_string(doubleValue).copy(digits, sizeof(digits))
Benempt answered 6/6, 2018 at 0:29 Comment(3)
That is a buffer overflow waiting to happen. At least use std::setprecision() to control how many digits the ostringstream outputsTrinitarianism
Your edits are still risky. std::strcpy() does not do any bounds checking. At least use std::strncpy() or std::string::copy() instead, so you can specify the max size of the char[] buffer, eg: std::strncpy(digits, to_string(doubleValue).c_str(), sizeof(digits)) or to_string(doubleValue).copy(digits, sizeof(digits))Trinitarianism
@Remy Lebeau Alright, I will change that, thanks for pointing it out.Benempt
V
2

The conversion process by your self without using to_string may be as follow:

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");
}
Vimineous answered 6/6, 2018 at 1:14 Comment(0)
C
1

If you want to convert by hand without using to_string, you're going to have to pull digits out one at a time. A double isn't stored in memory as a set of characters, so you can't just copy over the memory and hope it all works. (for more on how doubles are interpreted, see here).

Cruce answered 6/6, 2018 at 0:17 Comment(0)
M
1

Just do this first:

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

This converts a double dbl to a string str.

Then you can convert it to an array of chars like this:

char *cstr = new char[str.length()+1];
str.copy(cstr, str.length());
cstr[str.length()] = '\0';
// use cstr as needed...
delete[] cstr;

Or, simply this:

const char *cstr = str.c_str();
// use cstr as needed...
Mesopause answered 6/6, 2018 at 0:26 Comment(2)
How is this different from using std::to_string like OP mentioned in the question?Cruce
@Cruce to_string is C++11. Older compilers will not support it, unlike stringstream.Tycoon
T
0

just do this:

  #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;
  }

or this:

  #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;

or this:

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;
Teodoro answered 28/3, 2019 at 22:25 Comment(1)
It'll be useful for not just the OP but future Googlers if you explain why each set of code solves the problem and indeed the differences between each code, eg. performance, ease of use, etc.Cercus
A
0

Use itoa applied on the double with the decimal plate moved to right by two then create a copy of the result by adding the decimal point where it should be : before the last to digits :

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';
}

usage :

int main()
{
    char aux[10];
    convertToCharArray(27.77, aux);
    cout << aux;
    return 0;
}
Angularity answered 25/1, 2023 at 14:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.