I don't understand this code.

J

JS

#include <ctype.h>
double atof(char s[]){

double val, power;
int i, sign;

for (i = 0; isspace(s); i++)
;
sign = (s == '-') ? -1 : 1;
if (s == '+' || s == '-')
i++;

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

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

for (power = 1.0; isdigit(s); i++){
val = 10.0 * val + (s - '0');
power *= 10.0;
}
return sign * val / power;
}



The first for loop never terminates and I guess nothing is executed in its
body.

How should (s - '0') be understood? Have never seen a char subtracted
from another char before.



JS
 
L

Larry Brasfield

JS said:
#include <ctype.h>
double atof(char s[]){

double val, power;
int i, sign;

for (i = 0; isspace(s); i++)
;
sign = (s == '-') ? -1 : 1;
if (s == '+' || s == '-')
i++;

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

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

for (power = 1.0; isdigit(s); i++){
val = 10.0 * val + (s - '0');
power *= 10.0;
}
return sign * val / power;
}



The first for loop never terminates and I guess nothing is executed in its
body.


It looks to me as if it should terminate in
some fashion, either by finding a non-space
or producing an address fault. (I assume
that the data memory is not all whitespace.)

Your interpretation of the empty statement
is correct.
How should (s - '0') be understood? Have never seen a char subtracted
from another char before.


That is the usual way of converting a sequential
set of numeral character codes into their numeric
equivalents. '0' maps to 0, ..., '9' maps to 9.
 
H

Howard

JS said:
#include <ctype.h>
double atof(char s[]){

double val, power;
int i, sign;

for (i = 0; isspace(s); i++)
;
sign = (s == '-') ? -1 : 1;
if (s == '+' || s == '-')
i++;

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

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

for (power = 1.0; isdigit(s); i++){
val = 10.0 * val + (s - '0');
power *= 10.0;
}
return sign * val / power;
}



The first for loop never terminates and I guess nothing is executed in its
body.


That loop moves the starting point of the parsing code up past any leading
spaces. There is not protection there if the array passed contains only
spaces and no null terminator at the end, so I assume it's a requirement
that the function be provided a valid null-terminated string. If it never
terminates, then you've given it invalid data.
How should (s - '0') be understood? Have never seen a char subtracted
from another char before.


That's a common method if getting the values 0 through 9 from the characters
'0' through '9'. It only works if your character set contains the
characters '0' through '9' in order (which they all do, from everything I've
seen, but I don't know if it's a requirement). If you subtract '0' from '0,
you get 0. If you subtract '0' from '3', you get 3. Etc.

The code is taking a string containing a representation of a floating-point
number, and converting into an actual float value.

-Howard
 
E

evaned

There are a couple common idioms in the code you cite, and you have a
question about each of them. Let's look at them in turn:
for (i = 0; isspace(s); i++) ;


This "fast forwards" over spaces at the beginning of the number. This
way the function will still work for things like " 1.234". The later
parts expect the index i to be loooking at a number, so this puts it at
the beginning.

Remember that the expressions inside a for loop can have side effects.
In this case, the index i is changing. I'll rewrite the for loop as a
while loop that might be a little more clear:

i = 0
while( isspace(s) )
i++;

So what this does is, starting with the beginning of the string,
increment the index i until s is not a space.

How should (s - '0') be understood? Have never seen a char

subtracted
from another char before.

This is by far the most common way to convert a digit storted in a
character (e.g. the character '4', as opposed to the character with
ASCII code 4) to a number. In ASCII and probably most other character
sets (though not all, so this isn't strictly portable), all the digits
are together and in order. For instance, I'm pretty sure '0' has ASCII
code 48. If so, '1' has ASCII code 49, '2' has ASCII code 50, etc. (If
you're wondering, 48 is 30 hex, so the digits are from 30 to 39 hex.
Letters start at 41 and 61 hex, or 65 and 97 dec.)

When you subtract two characters you get the difference in their ASCII
values. For instance, '2' - '1' translates to 49 - 48, which is 1.
You'll notice because of the numbers' placement, all the subtractions
work out "right". '9' - '4' is indeed 5 for example. (Note too that
that is the integer 5, not '5'.) And so any digit minus '0' is that
digit as an integer.

In the atof function, s holds a digit that needs to be converted to
an integer so that something sane can be done with it.

A quick run through of the for loop on lines 13-14 might be as follows:
(this part of the function is converting everything to the left of the
decimal place to a number; indeed, if you were to look at the function
atoi, you would probably see the same code up to that point except with
val as an int instead of a double)

- The string s contains "135", val starts out as 0 (the first
expression in the for loop), and i starts as 0 (the for loop explained
above doesn't iterate at all; the expression is false right from the
start so i++ is never run)
- The first iteration, val is multiplied by 10 to get 0, then to that
is added s - '0' = s[0] - '0' = '1' - '0' = 1, so val = 1 after the
first iteration
- The second iteration, val is multiplied by 10 to get 10, then to that
is added s - '0' = s[1] - '0' = '2' - '0' = 1, so val = 10+2 = 12
after the second iteration
- The third iteration, val is multiplied by 10 to get 120, then to that
is added s - '0' = s[2] - '0' = '3' - '0' = 1, so val = 120+3 = 123
after the third iteration
- At this point, i = 4 and s[4] = '\0' (the null character that marks
the end of every C string), so isdigit(s) is false and the loop
terminates.

Does the function make more sense now?
 
P

Pete Becker

In ASCII and probably most other character
sets (though not all, so this isn't strictly portable), all the digits
are together and in order.

That's required, in both C and C++.
 
E

evaned

Oh, I didn't know that. Thanks! You learn something every day! That's
part of why I read these boards... ;-)
 
R

Richard Cavell

#include <ctype.h>
double atof(char s[]){

double val, power;
int i, sign;

for (i = 0; isspace(s); i++)
;
sign = (s == '-') ? -1 : 1;
if (s == '+' || s == '-')
i++;

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

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

for (power = 1.0; isdigit(s); i++){
val = 10.0 * val + (s - '0');
power *= 10.0;
}
return sign * val / power;
}



The first for loop never terminates and I guess nothing is executed in its
body.


It has the function of incrementing i to be an offset to the first
non-whitespace character.
How should (s - '0') be understood? Have never seen a char subtracted
from another char before.


Characters are represented by numbers, so that the character '0'
translates into a particular number. In most if not all character sets,
the characters that represent numbers are sequential. So whatever
number '0' translates into, plus 1, will translate back into '1' and so on.
 
S

Sven Heyll

Let me comment on the code:
#include <ctype.h>
double atof(char s[]){

double val, power;
int i, sign;

Eat whitespaces...
for (i = 0; isspace(s); i++)
;


Is the first char a "-"? in that case set sign to -1
if it is a '+' not a minus do nothing but in either case: eat the char
(+ or -)...
sign = (s == '-') ? -1 : 1;
if (s == '+' || s == '-')
i++;


Start processing the digits until a non digit char occures.
During process the current digit is found by substracing the charachter
code wich symbolizes the 0, and is added to the variable that
accumulates all digits. Of course this variable must be multiplied by 10
before the digit is added, we are decimal format...
for (val = 0.0; isdigit(s); i++)
val = 10.0 * val + (s - '0');


Ok all digits eaten.

Now check for a '.' if there is one an exponent is expected. The '.'
char is eaten of course.
if (s == '.')
i++;


Why does the following work? Very simple:
There are three actually two possibilities:
either the string contained
a '.'
a number
or something else
IT COULD NOT have contained a number as all numbers were eaten.
That leaves two possibilities to consider:
there was a '.' char(1)
there was something else (2)
Only a '.' char would have been eaten(case 1) not any other(necessaryly
non-digit) char(case 2).
It follows directly that the next character is a digit ONLY if a '.'
char was eaten above.

So the following loop can safely rely on that, and it also does so by
(blindly) running as long as the current character is a digit.

It accumulates the digits into the variable val in the same fashion as
the first digit eating loop does. It also multiplies another
variable(power) by 10 each loop, in order to be able to take the digits
position after decimal point into account.
for (power = 1.0; isdigit(s); i++){
val = 10.0 * val + (s - '0');
power *= 10.0;
}


The parts arer composed and returned...
return sign * val / power;
}



The first for loop never terminates and I guess nothing is executed in its false
body.
false also!! i++ belongs the the "body" i++ means "eat a
character!" (in this case whitespace characters)
How should (s - '0') be understood? Have never seen a char subtracted
from another char before.

numbers are represented as a dense sequence in the character symbol
table. To get 1 from a char containing '1' we can therefor substract
the symbol table index of '1' from the index at '0':

char cx = '1';
int x = cx - '0';

Hope I could ghelp you.

Greetings,
Sven Heyll
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top