1

I want to convert a section of a char array to a double. For example I have:

char in_string[] = "4014.84954";

Say I want to convert the first 40 to a double with value 40.0. My code so far:

#include <stdio.h>
#include <stdlib.h> 

int main(int arg) {
  char in_string[] = "4014.84954";
  int i = 0;
  for(i = 0; i <= sizeof(in_string); i++) {
    printf("%c\n", in_string[i]);
    printf("%f\n", atof(&in_string[i]));
  }
}

In each loop atof it converts the char array from the starting pointer I supply all the way to the end of the array. The output is:

4
4014.849540
0
14.849540
1
14.849540
4
4.849540
.
0.849540
8
84954.000000   etc...

How can I convert just a portion of a char array to a double? This must by modular because my real input_string is much more complicated, but I will ensure that the char is a number 0-9.

Kyle Weller
  • 2,533
  • 9
  • 35
  • 45

6 Answers6

2

The following should work assuming:

I will ensure that the char is a number 0-9.

double toDouble(const char* s, int start, int stop) {
    unsigned long long int m = 1;
    double ret = 0;
    for (int i = stop; i >= start; i--) {
        ret += (s[i] - '0') * m;
        m *= 10;
    }
    return ret;
}

For example for the string 23487 the function will do this calculations:

ret = 0
ret += 7 * 1
ret += 8 * 10
ret += 4 * 100
ret += 3 * 1000
ret += 2 * 10000
ret = 23487
Shoe
  • 74,840
  • 36
  • 166
  • 272
  • It's best to avoid `pow` when you can because it's so slow. – GraphicsMuncher Mar 23 '13 at 06:29
  • @GraphicsMuncher, says who? – Shoe Mar 23 '13 at 06:30
  • The real problem with this approach is that it's prone to rounding errors (because of `pow()` and because of the underlying format being binary and not decimal) and it therefore has limited use. – Alexey Frunze Mar 23 '13 at 06:36
  • @AlexeyFrunze, the OP ensured there will be no `.` sign in the string, therefore there's no real need for decimals. This is probably the fastest way right because it's built for the OP needs (*has limited use*). Also I don't see any roundings. Can you point them out? – Shoe Mar 23 '13 at 06:40
  • @Jueecy `pow()` *may* return inexact integer powers of 10. It'd be more reliable to multiply by 10,100,1000,etc directly. – Alexey Frunze Mar 23 '13 at 06:45
  • @Jueecy `pow` takes longer because it handles floating point powers and bases. Making a pow function for integer powers only will run faster. See [exponentiation by squaring](http://stackoverflow.com/questions/101439/the-most-efficient-way-to-implement-an-integer-based-power-function-powint-int). – GraphicsMuncher Mar 23 '13 at 06:48
  • @GraphicsMuncher, no more `pow`. – Shoe Mar 23 '13 at 06:50
  • Now the routine is limited to the range of ints. Dunno if that should be called out explicitly or a longer integer (or floating-point) type should be used for `m`. – Alexey Frunze Mar 23 '13 at 06:50
  • @AlexeyFrunze, actually, since `long int` = `int` in most platforms, the real deal is using `unsigned`. – Shoe Mar 23 '13 at 06:54
  • Why not `[unsigned] long long`? Actually, why not multiply `ret` by 10 on every iteration? – Alexey Frunze Mar 23 '13 at 06:56
  • Also, don't you need to subtract `'0'` from `s[i]` first? – Alexey Frunze Mar 23 '13 at 06:58
  • @Jueecy I really like your idea most so far, but it's not seeming to work for me, have you tested it? Using my example `in_string` and start 0 and stop 1 I call the function as `printf("VALUE = %f\n",toDouble(in_string, 0, 1));` I printf which char it is evaluating and I print `ret` as it goes and I see the following output: `char=0 ret before = 0.000000 ret after = 48.000000 char=4 ret before = 48.000000 ret after = 568.000000 VALUE = 568.000000` – Kyle Weller Mar 23 '13 at 06:59
  • @Jueecy Yep that does it thanks! I was struggling to keep up with the updates because they were coming in so fast. Thanks for the quick changes. – Kyle Weller Mar 23 '13 at 07:03
  • @AlexeyFrunze, I'm not sure but `unsigned long int` (4 bytes) is optimal and it's the best you can get anyway because you are still limited to the 4 bytes of the `double` type in return. Therefore those 4 more bytes wouldn't be used anyway. (I've run some tests) – Shoe Mar 23 '13 at 07:10
1

You can copy the desired amount of the string you want to another char array, null terminate it, and then convert it to a double. EG, if you want 2 digits, copy the 2 digits you want into a char array of length 3, ensuring the 3rd character is the null terminator.

Or if you don't want to make another char array, you can back up the (n+1)th char of the char array, replace it with a null terminator (ie 0x00), call atof, and then replace the null terminator with the backed up value. This will make atof stop parsing where you placed your null terminator.

GraphicsMuncher
  • 4,583
  • 4
  • 35
  • 50
1

What about that, insert NULL at the right position and then revert it back to the original letter? This means you will manipulate the char array but you will revert it back to the original at the end.

Shoe
  • 74,840
  • 36
  • 166
  • 272
Mahmoud Fayez
  • 3,398
  • 2
  • 19
  • 36
1

Just use sscanf. Use the format "ld" and check for return value is one.

Ed Heal
  • 59,252
  • 17
  • 87
  • 127
0

You can create a function that will make the work in a temporary string (on the stack) and return the resulting double:

double atofn (char *src, int n) {
  char tmp[50]; // big enough to fit any double

  strncpy (tmp, src, n);
  tmp[n] = 0;
  return atof(tmp);
}
Déjà vu
  • 28,223
  • 6
  • 72
  • 100
0

How much simpler could it get than sscanf?

#include <assert.h>
#include <stdio.h>

int main(void) {
    double foo;
    assert(sscanf("4014.84954", "%02lf", &foo) == 1);
    printf("Processed the first two bytes of input and got: %lf\n", foo);

    assert(sscanf("4014.84954" + 2, "%05lf", &foo) == 1);
    printf("Processed the next five bytes of input and got: %lf\n", foo);

    assert(sscanf("4014.84954" + 7, "%lf", &foo) == 1);
    printf("Processed the rest of the input and got: %lf\n", foo);
    return 0;
}
autistic
  • 1
  • 3
  • 35
  • 80