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

Sebastian D'Agostino
  • 1,575
  • 2
  • 27
  • 44
Delrog
  • 735
  • 2
  • 11
  • 27
  • What is wrong with using `to_string`? – Scott Hunter Jun 06 '18 at 00:18
  • 6
    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. – Pete Becker Jun 06 '18 at 00:18
  • 1
    If `std::to_string` is so abhorrent, perhaps C APIs like `sprintf` would do what you need? – ShadowRanger Jun 06 '18 at 00:26
  • 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://stackoverflow.com/questions/2302969/convert-a-float-to-a-string – Ryan Jun 06 '18 at 00:44

8 Answers8

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)

tdk001
  • 1,014
  • 1
  • 9
  • 16
  • 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? – Delrog Jun 06 '18 at 00:35
  • 1
    @Delrog I'd suggest using `snprintf` instead of `strncpy`+`to_string` – user253751 Jun 06 '18 at 00:36
  • 5
    You could just let the STL do the rounding for you. Using a [`std::ostringstream`](http://en.cppreference.com/w/cpp/io/basic_ostringstream), look at the [`std::fixed`](http://en.cppreference.com/w/cpp/io/manip/fixed) and [`std::setprecision()`](http://en.cppreference.com/w/cpp/io/manip/setprecision) stream manipulators, eg: `ostringstream oss; oss << fixed << setprecision(3) << d; string s = oss.str();` – Remy Lebeau Jun 06 '18 at 00:38
  • 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? – Delrog Jun 06 '18 at 00:57
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.

Scott Hunter
  • 48,888
  • 12
  • 60
  • 101
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))
0xCursor
  • 2,242
  • 4
  • 15
  • 33
  • That is a buffer overflow waiting to happen. At least use `std::setprecision()` to control how many digits the `ostringstream` outputs – Remy Lebeau Jun 06 '18 at 00:42
  • 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))` – Remy Lebeau Jun 06 '18 at 00:46
  • @Remy Lebeau Alright, I will change that, thanks for pointing it out. – 0xCursor Jun 06 '18 at 00:50
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");
}
Hossein Golshani
  • 1,847
  • 5
  • 16
  • 27
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).

scohe001
  • 15,110
  • 2
  • 31
  • 51
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...
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Sebastian D'Agostino
  • 1,575
  • 2
  • 27
  • 44
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;
  • 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. – Studocwho Mar 28 '19 at 22:54
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;
}