Explanation of atof code from K&R - c

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

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;

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

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).

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

Related

atoi function doesn't work in for loop

I am trying to get the output of this equation :
44 - 10 + 11 / 5
it work properly as follow :
char str[] = "44 - 10 + 11 / 5";
int sum = 0 ;
sum += atoi(str); // 0 + 44
sum += atoi(str+3); // 44 + 10
sum += atoi(str+5); // 54 + 11
sum += atoi(str+7); // 65 / 5 = 13
printf("%d/n",sum); // output = 13
but it doesn't work if I put the atoi() function in the for loop:
char str[] = "44 - 10 + 11 / 5";
int sum = 0;
int i;
sum += atoi(str);
for (i = 0; i < 100; i++) {
if (!(str[i] >= 0 && str[i] <= 9)) { //if str[i] is not a number
sum += atoi(str + i);
}
}
printf("%d/n", sum); // output = 0
You are comparing a char value that contains the ASCII representation of a number, to an actual number. Change
if(!(str[i]>=0 && str[i]<=9)){
to
if(!(str[i]>='0' && str[i]<='9')){
I did not check if the rest of the code is correct, but certainly, this is one issue.
if(!(str[i]>=0 && str[i]<=9)){ //if str[i] is not a number
This is incorrect. '0' and 0 are not same. Neither is 9 and '9'. The value of '9' will be integer that represents '9' in ASCII chart.
Rest of the logic seems suspicious too, for example what happens if you have "55", code will enter if twice, once for 55 and second time for 5, isn't it?
Because your code does not do the same calculations as your previous one. In the first one you are shifting the pointer to the next number and you are adding it to the number, so your code does the following 44 + 10 + 11 + 5.
In the second case you are checking whether the character code is between 0 and 9, but the numbers are between 48 and 57 - the characters '0' and '9'. So the if statement would be always wrong. Your intention was probably this, but it won't work.
for(i = 0; i < 100 ; i++){
if(!(str[i]>='0' && str[i]<='9')){ //if str[i] is not a number
sum += atoi(str+i);
}
}
This also does not work, because here you are doing the wrong shifts of the pointer by one, resulting in the reading of the following numbers: 44, 4, 10, 0, 11, 1, 5. Because of the atoi properties. To perform the exact same code you did before you`d have to write the following code:
int skip_non_diggits(int i, char *str) {
while (str[i] < '0' || str[i] > '9') {
if (str[i] == 0)
return -1; //end of string case
i++;
}
return i;
}
int skip_diggits(int i, char *str) {
while (str[i] >= '0' && str[i] <= '9') {
if (str[i] == 0)
return -1; //end of string case
i++;
}
return i;
}
These functions could be used as follows:
int i = 0, sum = 0;
while (i != -1) {
sum += atoi(str+i);
i = skip_diggits(i, str); // positions i after the current number
i = skip_non_diggits(i, str); // positions i at the begin of the next number
}
BTW
Don't use for (i = 0; i < 100; i++) to iterate through the strings you don't know the size of. If the string is shorter in length than 100 you're derefferencing memory which probably isn't yours creating undefined behavior. Instead check the strlen method or check whether the character is not equal to \0 which in C indicates the end of the string.
This seems a bit odd. What's the goal?
Agree with previous comment.
Why are you doing atoi() on what is NOT a number?
You will go past the end of the string.
Your spaces COUNT in the example
and so on.
you don't update i when you find a number, so you will count "44" as 44 first and 4 second.
If you're trying to learn, hook up a debugger, step through the code, and see how the data changes.

K&R book exercise 4-2

I'm studying K&R book. I'm currently at chapter 4. I was reading the atof() function on page 71. Function atof(s) converts string to its double precision floating point equivalent.
The code of atof() is as following:
//atof: convert string s to double
double atof2(char s[])
{
double val, power;
int i, sign;
for (i = 0; isspace(s[i]); ++i) //skip white space
;
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;
}
My question is about variable: power. Why do we need it for?
I do understand the use of variable: "val" but i'm not sure about variable: "power". Why do we divide val by power?
Variable power is for division of number by power , to get result as float point .
Let your string be -12.83 , then first for loop will check for space and increment i as no space so ,i=0 .
sign will be -1 as s[i]=s[0]='-' .
In next two loops string's values are converted to integers and stored in val ( excluding . - figure out yourself) .
Now after both loop val will be 1283 . But last loop will iterate for 2 times and power will be changed to 100.00 (10*1.0 in first iteration and 10*10.0 in second iteration) .
Now to get value as float point val is divided by power and multiplied by sign .
So , what it will return is -1*1283/100 , thus -12.83 is your float point number .

C: Decimal Value

Can any one help me sort out one problem, i have to reverse a number without using array(int/char) for storing them.
input1 = 123
output1 = 321
input2 = 2300
output2 = 0032
I am trying to find the solution but 0 got erased while printing so i thought of octal conversion but still no solution, so i went with the decimal places and i made the 23 to 0.0032. Now my problem is how can i extract the 0032 from that part.
Is there any possible way to achieve this without using array(int/char), with that it will be easy.
#include<stdio.h>
#include<math.h>
int main()
{
int number =3200;
int temp;
while (number >0)
{
temp= number%10;
printf("%d",temp);
number = number/10;
}
return 0;
}
you could use recursion to solve this problem, without using any array in fact u could also reverse a string without using any array using recursion. This code works for both numbers and strings and it has no arrays:
char reverse(int a)
{
char c,d;
if(a=='\n')
return 0;
c=getchar();
d=reverse(c);
putchar(a);
return (c);
}
int main()
{
char c;
scanf("%c",&c);
reverse(c);
}
for a start try this.
int n, l;
char nBuf[126];
n = 1230010;
l = sprintf(nBuf, "%d", n );
while( l >= 0 )
printf("%c", nBuf[l--] );
Though if you are taking input from stdin take it as string rathar than as int or long.
Edit - for not using array
int n = 123;
while(n) {
printf("%d", n%10);
n/=10;
}
I am assuming to get a value of this sort "output2 = 0032" it is better of being a string, else formatting complications turns up with input value length and format left space with zeros etc etc.
This becomes fairly easy if you know that you can represent numbers like so:
x = a_0 + a_1 * b^1 + a_2 * b^2 + ...
a_i are the digits
b is the base
To extract the lowest digit, you can use the remainder: x % b
Dividing by the base "removes" the last digit. That way you can get the digits in order lowest to highest.
If you reverse the digits then the lowest becomes the highest. Looking at below transformation it's easy to see how to incrementally build up a number when the digits come in order highest to lowest:
x = a_0 + b * (a_1 + b * (a_2 + ...
You start of with 0, and for each digit you multiply with the base and then add the digit.
In pseudo code:
output = 0
while input != 0
digit = input % base
input = input / base ;; integer division
output = output * base + digit
end
If you want to store leading zeros, then you need to either store the digits in an array, or remember for how many steps of above loop the output remained zero:
output = 0
zeros = 0
while input != 0
digit = input % base
input = input / base ;; integer division
output = output * base + digit
if output == 0
zeros = zeros + 1
end
end
To print that you obviously need to print zeros zeros and then the number.
Live example here, relevant code:
unsigned reverse(
unsigned input,
unsigned const base,
unsigned * const zeros) {
unsigned output = 0;
unsigned still_zero = 0;
for (; input != 0; input/=base) {
output *= base;
output += input % base;
if (output == 0) {
++still_zero;
}
}
if (zeros != NULL) {
*zeros = still_zero;
}
return output;
}
void print_zeros(unsigned zeros) {
for (; zeros != 0; --zeros) {
printf("0");
}
}
Recursion allows for a simple solution. A small variation on #vishu rathore
void rev_dec(void) {
int ch = getchar();
if (isdigit(ch)) {
rev_dec();
}
if (ch >= 0) putchar(ch);
}
int main(void) {
rev_dec();
return 0;
}
input
0123456789012345678901234567890123456789
output
9876543210987654321098765432109876543210

c-changing string to int

I'm trying to change a string of chars into a number.
For example the string '5','3','9' into 539.
what I did is:
for (j = 0; j < len_of_str; j++)
num = num + ((str[j] - 48) * (10 ^ (len_of_str - j)))
printf("%d", num);
num is the number which would contain the number as a int the minus 48 is to change the value in ASCII to a number who's like the real number.
and the (10 ^ (len_of_str - j)) is the change the values to hundreds, thousands, etc...
Several issues:
First, ^ is not an exponentiation operator in C - it's a bitwise exclusive-OR operator. Instead of getting 10N, you'll get 10 XOR N, which is not what you want. C does not have an exponentiation operator (ironic for a language that defines eleventy billion operators, but there you go) - you'll need to use the library function pow instead. Or you can avoid the whole issue and do this instead:
num = 0;
for ( j = 0; j < len_of_str; j++ )
{
num *= 10;
num += str[j] - 48;
}
Second, str[j]-48 assumes ASCII encoding. To make that a bit more generic, use str[j] - '0' instead (in most encodings digit characters are sequential,
so '9' - '0' should equal 9).
Finally, is there a reason you're not using one of the built-in library functions such as atoi or strtol?
num = (int) strtol( str, NULL, 0 );
printf( "num = %d\n", num );
As pointed out by the comments above, ^ does not actually calculate a power, but instead does a bit-wise XOR (see wikipedia). For instance for 0101 ^ 0111 == 0010, as XOR will only set the bits to one for which the inputs differ in that bit.
To calculate 10 to the power something in c, use pow(double x, double y) from <math.h>. See this post for more information.
Convert a sequence of digits into an integer is a special case of the more general case of parsing a number (integer or real) into a binary integer or double value.
One approach is to describe the number using a pattern, which you can either describe iteratively, or recursively as follows,
An integer_string is composed of:
and optional '+' or '-' (sign)
follwed by a digit_sequence
a digit_sequence is composed of:
digit ('0', '1', '2', '3', ..., '9')
followed by an optional (recursive) digit_sequence
This can be written using Backus-Naur formalism as,
integer_string := { '+' | '-' } digit_sequence
digit_sequence := digit { digit_sequence }
digit := [ '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' ]
Should you desire, you can extend the above to recognize a real number,
real_number := integer_string { '.' { digit_sequence } }
{ [ 'e' | 'E' ] integer_string }
Although the above is not quite correct, as it forces a digit before the decimal (fix is left as an exercise for the reader).
Once you have the Backus-Naur formalism, it is easy to recognize the symbols that comprise the pattern, and the semantic action of the actual conversion to integer
long int
atol_self(char* str)
{
if(!str) return(0);
//accumulator for value
long int accum=0; //nothing yet
//index through the string
int ndx=0;
//handle the optional sign
int sign=1;
if ( str[ndx=0] == '+' ) { sign=1; ndx+=1; }
else if ( str[ndx=0] == '+' ) { sign=1; ndx+=1; }
for( ; str[ndx] && isdigit(str[ndx]); ) {
int digval = str[ndx] - '0';
accum = accum*10 + digval;
++ndx;
}
return(accum*sign);
}

convert number from base n to an integer

So I'm hoping to get a little guidance on this one. I have a function that takes a radix(base) and then using getchar() will get the number to convert from the given radix to an integer representation.
The only argument given is the radix number, then getchar() gets the number representation via the command line.
So if I pass
str2int 16
input a number: 3c
It should output (16^1*3) + (16^0*12) = 48 + 12 = 60.
I fully understand the math, and different ways of converting bases, but don't know how to go about coding something up. The math is always MUCH easier than the code, at least to me.
Another way to compute would be:
(702) base 15 = 15*7 + 0 = 105; 15*105 + 2 = 1577
I don't know how to express this in C only using getchar()? Is it possible to do without using the math function?
Keep getting one char at a time until not a digit or no more are needed.
unsigned shparkison(unsigned base) {
unsigned sum = 0;
int ch;
while ((ch = getchar()) != EOF) {
// one could instead look up the toupper(value) in an array "0123...ABC...Z";
// Following assumes ASCII
if (isdigit(ch)) ch -= '0';
else if (islower(ch)) ch -= 'A' - 10;
else if (isupper(ch)) ch -= 'a' - 10;
else {
break; // Not a digit
}
if (ch >= base) {
break; // Digit too high
}
unsigned sum_old = sum;
sum *= base;
sum += ch;
if (sum < sum_old) {
sum = sum_old;
break; // Overflow
}
}
ungetc(ch, stdin);
return sum;
}

Resources