3

I understand what we are doing, before we converted a string to an int, now we are converting a string to a double. I don't understand the logic behind this code though. Could someone clarify this a little for me? Best regards.

#include <ctype.h>
#include <stdio.h>

//atof: convert string s to double

double atof(char s[])
{
    double val, power;
    int i, sign;

    for (i = 0; isspace(s[i]); i++) //skip whitespace
        ;
    sign = (s[i] == '-') ? -1 : 1;
    if (s[i] == '+' || s[i] == '-')
        i++;
    for (val = 0.0; isdigit(s[i]); i++)
        val = 10.0 * val + (s[i] - '0');
    if (s[i] == '.')
        i++;
    for (power = 1.0; isdigit(s[i]); i++) {
        val = 10.0 * val + (s[i] - '0');
        power *= 10.0;
    }
    return sign * val / power;
}
int main()
{
    char s[] = "78.93"; //output is 78.930000
    printf("atof equals %f\n", atof(s));
    return 0;
}
kits
  • 609
  • 6
  • 20
  • 6
    Do you expect a line by line explanation or is there a specific piece of piece of code that confuses you? – try-catch-finally Dec 28 '14 at 00:41
  • 1
    Can you break it down a bit? Then phrase this question in a way that'll help someone else one day. – Lightness Races in Orbit Dec 28 '14 at 00:49
  • Note: this `atof()` differs from the standard library `atof()`. This one does not handle exponentials, hexadecimal formats (See `printf()` `"%a")`, sub-normals, infinites, fails near the extremes of the `double `range, is unnecessarily imprecise with its `val / power`. Other than that, it is an OK `aotf()` function. – chux - Reinstate Monica Dec 28 '14 at 07:41

4 Answers4

7

This part is pretty easy, just skips to the first non-whitespace character:

for (i = 0; isspace(s[i]); i++) //skip whitespace
    ;

Now we check if the first non-whitespace character is a - to set it negative, then skip over the character whether its a - or a +:

sign = (s[i] == '-') ? -1 : 1;
if (s[i] == '+' || s[i] == '-')
    i++;

Now it starts to get tricky. Let's use an example of 1234.5678. First we're going to handle the part before the decimal. Its handled by looking at each digit, adding it to val, then if the next digit is not a decimal, multiply val up to the point by 10 to left shift it and add the next digit. For example with 1234.5678, we first see digit 1, add it to val for a val of 1. The next digit is 2, so we multiple current val (1) by 10 to get 10 then add 2 to get 12. The next digit is 3, so we multiply the current val (12) by 10 to get 120, then add 3 to get 123. The next digit is 4, so we multiple the current val (123) by 10 to get 1230, then add 4 to get 1234. Then the '.' is not a digit, so we've finished the left side of the number.

for (val = 0.0; isdigit(s[i]); i++)
    val = 10.0 * val + (s[i] - '0');

This part just moves past the dot.

if (s[i] == '.')
    i++;

Now we do the same with the right side of the decimal as we did with the left, but we also track how many digits are past the decimal (with the power variable). In the example of 1234.5678, the first digit we see is 5. So we multiply the current val (1234) by 10 and add 5 for (12345). We also increase our power to 10.0. This continues until we get a val of 123456789 and a power of 10000.0.

for (power = 1.0; isdigit(s[i]); i++) {
    val = 10.0 * val + (s[i] - '0');
    power *= 10.0;
}

Finally, we divide by the power to get the decimal place in the correct spot (123456789 / 10000.0):

return sign * val / power;
Dtor
  • 590
  • 2
  • 9
3
double atof(char s[])
{
    double val, power;
    int i, sign;

    // if there is any leading 'white space', step index past it
    // keep stepping index until other than white space encountered
    for (i = 0; isspace(s[i]); i++)
        ;

    // if there is a '-' char 
    // then indicate value is negative 
    // else assume value is positive
    // format is: result = (condition)? true value : false value
    sign = (s[i] == '-') ? -1 : 1; 

    // if there is a sign byte, step index past it
    if (s[i] == '+' || s[i] == '-')
        i++;

    // initialize the result 'val'
    // then loop through following characters
    for (val = 0.0; isdigit(s[i]); i++)
        // digits are in the range 0x30 through 0x39
        // make them integers by subtracting 0x30 ('0')
        // and update the result 'val'
        // remembering that each successive digit pushes the current result 'val'
        // to 10 times the old value then add the new 'converted' digit
        val = 10.0 * val + (s[i] - '0');
        // this ends the 'for' code block

     // when execution gets here, encountered something other than a digit
    // when a '.' encountered, step the index past it
    if (s[i] == '.')
        i++;

    // the 'power' value is indicating how much to divide the resulting
    // 'val' by to place the decimal point (if there was a decimal point)
    // into the correct position
    // if other than a digit encountered, exit loop
    for (power = 1.0; isdigit(s[i]); i++)
    {
        val = 10.0 * val + (s[i] - '0'); // see above comment about a similar line of code
        power *= 10.0;
    } // end for

    // calculate the actual value by allowing for any sign (+ or -)
    // then dividing that result by 'power' to properly place the decimal point
    return sign * val / power;
}  // end function: atof
user3629249
  • 16,402
  • 1
  • 16
  • 17
1

Skip the white space; handle a leading sign; compute the integer part (in Val); skip the decimal; handle the fractional part (by updating Val as if there were no decimal point, but also power to account for it).

Scott Hunter
  • 48,888
  • 12
  • 60
  • 101
0

This code consists of 3 loops

the first loop keep reading 'spaces' until something readable has been detected (a sign or a number)

the second loop calculate the value of the left part of the floating point (the value of xxx in -xxx.545)

the last loop uses the value of the previous loop and continue with the right part of the 'point' while calculating the number 'power' which is the 10 to the power of number of elements after the '.' now that we have a sign and value of both left and right parts of the floating point number

now in simple example: let -12.345

sign = -1
val = 12345
power = 1000 ( 10 to the power of numbers after the '.')
result is -1 * 12345 / 1000 = -12.345
Maher
  • 294
  • 1
  • 13