# Thread: Atof with scientific notation

1. ## Atof with scientific notation

Good afternoon. I'm trying to code a program which converts a string in a double and in a string again but with scientific notation.

The exercise is from KnR:

Extend atof to handle scientific notation of the form 123.45e-6 where a floating-point number may be followed by e or E and an optionally signed exponent.

What I want to do, despite of what the exercise mentioned, is convert something like

450 to 4.50e+2 and 0.05 to 5.0e-2

that's why I thought I had to convert the double into a string again. To put the "e-2" as a string concatenated with the value.

In addiction to that, I wonder if I can write char* notation like I wrote in the code. Because I don't want each char separated by index.

conversion.h

Code:
```
void itob(int, char*, int);
char* Atof(char*);```
atof.c

Code:
```
/* atof: convert string s in scientific notation double */

#include <ctype.h>
#include <string.h>

#include "../Headers/conversion.h"

char* Atof(char* s)
{
double val, power;
int i, sign, npow, dotlocation;
char* notation, strval, strsci, strnpow;

for(i = 0; isspace(s[i]); i++)
;
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] == '.')
{
dotlocation = i;
i++;
}
for(power = 1.0, npow = 0; isdigit(s[i]); i++)
{
val = 10.0 * val + (s[i] - '0');
power *= 10.0;
npow++;
}

val = sign * val / power;
strval = val + '0';
strnpow = npow + '0';

if(npow > 0 && s[dotlocation - 1] != '0')
strsci = "e+" + strnpow + '0';

if(npow > 0 && s[dotlocation - 1] == '0')
strsci = "e-" + strnpow + '0';

notation = strcat(strval, strsci);
++*notation = '\0';
return notation;
}```
mainatof.c

Code:
```
#include <stdio.h>
#include <stdlib.h>

#include "Headers/conversion.h"
#include "Conversion/atof.c"
#include "Validation/isanumber.c"

#ifndef MAXSIZE
#define MAXSIZE 1000
#endif

#ifndef SCIENTIFIC
#define SCIENTIFIC 4
#endif

int main(void)
{
char strnum[MAXSIZE];
char* notation;
double d;

do {
printf("Please enter a double: ");
fgets(strnum, sizeof(strnum), stdin);
}while(!isanumber(strnum));

notation = Atof(strnum);
printf("The double value in scientific notation is %s\n", notation);

return 0;
}```
I have some warnings and that error saying the value wasn't assigned to a variable.

Code:
```

In file included from mainatof.c:5:0:
Conversion/atof.c: In function ‘Atof’:
Conversion/atof.c:39:13: warning: assignment makes integer from pointer without a cast [enabled by default]
Conversion/atof.c:42:13: warning: assignment makes integer from pointer without a cast [enabled by default]
Conversion/atof.c:44:4: warning: passing argument 1 of ‘strcat’ makes pointer from integer without a cast [enabled by default]
/usr/include/string.h:136:14: note: expected ‘char * __restrict__’ but argument is of type ‘char’
Conversion/atof.c:44:4: warning: passing argument 2 of ‘strcat’ makes pointer from integer without a cast [enabled by default]
/usr/include/string.h:136:14: note: expected ‘const char * __restrict__’ but argument is of type ‘char’
Conversion/atof.c:45:18: error: lvalue required as left operand of assignment```

2. You are in C.
You can not say
Code:
`strsci = "e+" + strnpow + '0';`
Use strcat for this....

3. Code:
` strsci = strcat("e+",strnpow);`
Conversion/atof.c:39:6: warning: passing argument 2 of ‘strcat’ makes pointer from integer without a cast [enabled by default]

why did I get pointer from integer if strnpow is npow (an integer) added to '0' to (turn into a string?).

4. it would make us see better if you could add more comments to the code cuz this will go a long way in dissecting the code.

5. Originally Posted by thames

why did I get pointer from integer if strnpow is npow (an integer) added to '0' to (turn into a string?).
Because npow is an integer!
Code:
`strnpow = npow + '0';`
What do you expect this to result in?
Check itoa .

Tip : Eliminate errors and warning that compiler shows top down, because the ones that are not in top might be result of the ones that are on top

6. it would make us see better if you could add more comments to the code cuz this will go a long way in dissecting the code.
Sorry for that. From now on, I'll comment the codes with certain level of complexity.

Because npow is an integer!
I thought I could do that because of itoa's code snippet:

Code:
```
do {
s[i++] = abs(n % 10) + '0';
} while( (n /= 10) > 0);```
but you made clear to me that just works for a digit and not for a number.

7. strcat is being used incorrectly. It appends the second argument to the first, so the first argument must be writable.
strcat also doesn't work on unititialized char pointers, they have to point to good memory.

8. Now I'm only testing numbers like 0.05.

Code:
```#include "../Headers/conversion.h"
#include <stdlib.h>

void itoa(int n, char* s)
{
int sign, i;
i = 0;

if( (sign = n) < 0)     /* record sign */
n = -n;   /* turn n positive */
do {
s[i++] = abs(n % 10) + '0';
} while( (n /= 10) > 0);
if(sign < 0)
{
s[i++] = '-';
}

s[i] = '\0';
}```
Code:
```
#include <ctype.h>

#include "../Headers/validation.h"

int isanumber(char* s) {

int i = 0;

while(s[i] != '\n')
{
if(isalpha((unsigned char) s[i]) || isblank( (unsigned char) s[i]) )
break;

i++;
}

if(s[i] == '\n')
return 1;
return 0;
}```
Code:
```#include <string.h>

#include "../Headers/strings.h"

void reverse(char* s, char* reversed)
{
int i, j;

for(i = strlen(s) - 1, j = 0; i >= 0; i--, j++)
reversed[j] = s[i];

reversed[j] = '\0';
}```
Code:
```

#include <ctype.h>
#include <string.h>

#include "../Headers/conversion.h"
#include "../Headers/strings.h"

#ifndef MAXSIZE
#define MAXSIZE 1000
#endif

/* atof: convert string s in scientific notation double */

char* Atof(char* s)
{
double val, power;
int i, sign, dotLocation, nPlaces;
char* notation;

/* (itoa returns the string from the end to the beginning, i.e. inverted) */
char revstrval[MAXSIZE];
char revstrnplaces[MAXSIZE];

/* the correct order of the string representation of the numbers */
char straightstrval[MAXSIZE];
char straightstrnplaces[MAXSIZE];

dotLocation = nPlaces = 0;

/* first part: get a double from the string */

/* while there is a space, keep iterating */
for(i = 0; isspace(s[i]); i++)
;

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

/* if there is a sign inside the array, just ignore it */
if(s[i] == '+' || s[i] == '-')
i++;

/* the next block converts the string to a double with the proper sign */
/* ------------------------------------------------- */

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

val = sign * val / power;

/* ------------------------------------------------- */

/* second part: write double in scientific notation */

/* count the number of places starting from the first digit after the dot */
for(i = s[dotLocation] + 1; s[i] != '\0'; i++)
nPlaces++;

/* verify if first digit is zero and make sure there is, at least, one
* place (for numbers like 0.05) */

if(val / 1 == 0 && power > 1.0)
{
val *= power;

itoa(val, revstrval);
itoa(nPlaces, revstrnplaces);

reverse(revstrval, straightstrval);
reverse(revstrnplaces, straightstrnplaces);

notation = strcat(straightstrval, "e-");
notation = strncat(notation, straightstrnplaces, MAXSIZE);
}

/* verify if first digit is different from zero and make sure there is, at least, one
* place (for numbers like 125.5) */

else {

}

return notation;
}```
Code:
```#include <stdio.h>
#include <string.h>

#include "Headers/conversion.h"
#include "Headers/validation.h"

#ifndef MAXSIZE
#define MAXSIZE 1000
#endif

int main(void)
{
char strnum[MAXSIZE];
char notation[MAXSIZE];

do {
printf("Please enter a double: ");
fgets(strnum, sizeof(strnum), stdin);
}while(!isanumber(strnum));

strcpy(notation, Atof(strnum));
printf("The double value in scientific notation is %s\n", notation);

return 0;
}```
Code:
```  gcc -g -Wall mainatof.c Conversion/atof.c Strings/reverse.c Conversion/itoa.c Validation/isanumber.c -o mainatof
./mainatof
Please enter a double: 0.05
Segmentation fault```
Code:
``` valgrind --tool=memcheck --leak-check=full ./mainatof
==5566== Memcheck, a memory error detector
==5566== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==5566== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==5566== Command: ./mainatof
==5566==
Please enter a double: 0.05
==5566== Conditional jump or move depends on uninitialised value(s)
==5566==    at 0x400A3C: Atof (atof.c:64)
==5566==    by 0x40075F: main (mainatof.c:21)
==5566==
==5566== Use of uninitialised value of size 8
==5566==    at 0x4C2D007: strcpy (mc_replace_strmem.c:438)
==5566==    by 0x400774: main (mainatof.c:21)
==5566==
==5566== Invalid read of size 1
==5566==    at 0x4C2D007: strcpy (mc_replace_strmem.c:438)
==5566==    by 0x400774: main (mainatof.c:21)
==5566==  Address 0x0 is not stack'd, malloc'd or (recently) free'd
==5566==
==5566==
==5566== Process terminating with default action of signal 11 (SIGSEGV)
==5566==  Access not within mapped region at address 0x0
==5566==    at 0x4C2D007: strcpy (mc_replace_strmem.c:438)
==5566==    by 0x400774: main (mainatof.c:21)
==5566==  If you believe this happened as a result of a stack
==5566==  overflow in your program's main thread (unlikely but
==5566==  possible), you can try to increase the size of the
==5566==  main thread stack using the --main-stacksize= flag.
==5566==  The main thread stack size used in this run was 8388608.
==5566==
==5566== HEAP SUMMARY:
==5566==     in use at exit: 0 bytes in 0 blocks
==5566==   total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==5566==
==5566== All heap blocks were freed -- no leaks are possible
==5566==
==5566== For counts of detected and suppressed errors, rerun with: -v
==5566== Use --track-origins=yes to see where uninitialised values come from
==5566== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)
Segmentation fault```

9. In your Atof():
Code:
```for(i = s[dotLocation] + 1; s[i] != '\0'; i++)
nPlaces++;```
What's the value of the expression "s[dotLocation]"?

Bye, Andreas

10. I decided, for now, to put every necessary function inside the main source because is easier to handle everything that way. Why is 0.050000000000000003 the value of val when I input 0.05 as a string to be converted into a double?

Code:
```
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>

#ifndef MAXSIZE
#define MAXSIZE 1000
#endif

void reverse(char* s, char* reversed)
{
int i, j;

for(i = strlen(s) - 1, j = 0; i >= 0; i--, j++)
reversed[j] = s[i];

reversed[j] = '\0';
}

void itoa(int n, char* s)
{
int sign, i;
i = 0;

if( (sign = n) < 0)     /* record sign */
n = -n;   /* turn n positive */
do {
s[i++] = abs(n % 10) + '0';
} while( (n /= 10) > 0);
if(sign < 0)
{
s[i++] = '-';
}

s[i] = '\0';
}

int isanumber(char* s) {

int i = 0;

while(s[i] != '\n')
{
if(isalpha((unsigned char) s[i]) || isblank( (unsigned char) s[i]) )
break;

i++;
}

if(s[i] == '\n')
return 1;
return 0;
}

char* Atof(char* s)
{
double val, power;
int i, sign, afterDot, nPlaces;
char* notation;

/* (itoa returns the string from the end to the beginning, i.e. inverted) */
char revstrval[MAXSIZE];
char revstrnplaces[MAXSIZE];

/* the correct order of the string representation of the numbers */
char straightstrval[MAXSIZE];
char straightstrnplaces[MAXSIZE];

afterDot = 0;

/* first part: get a double from the string */

/* while there is a space, keep iterating */
for(i = 0; isspace(s[i]); i++)
;

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

/* if there is a sign inside the array, just ignore it */
if(s[i] == '+' || s[i] == '-')
i++;

/* the next block converts the string to a double with the proper sign */
/* ------------------------------------------------- */

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

val = sign * val / power;

/* ------------------------------------------------- */

/* second part: write double in scientific notation */

/* count the number of places starting from the first digit after the dot */
for(i = afterDot, nPlaces = 0; s[i] != '\n'; i++, nPlaces++)
;

/* verify if first digit is zero and make sure there is, at least, one
* place (for numbers like 0.05) */

if(val / 1 == 0 && power > 1.0)
{
val *= power;

itoa(val, revstrval);
itoa(nPlaces, revstrnplaces);

reverse(revstrval, straightstrval);
reverse(revstrnplaces, straightstrnplaces);

notation = strcat(straightstrval, "e-");
notation = strncat(notation, straightstrnplaces, MAXSIZE);
}

/* verify if first digit is different from zero and make sure there is, at least, one
* place (for numbers like 125.5) */

else {

}

return notation;
}

int main(void)
{
char strnum[MAXSIZE];
char notation[MAXSIZE];

do {
printf("Please enter a double: ");
fgets(strnum, sizeof(strnum), stdin);
}while(!isanumber(strnum));

strcpy(notation, Atof(strnum));
printf("The double value in scientific notation is %s\n", notation);

return 0;
}```
Code:
```ethereal@laerehte ~/C/Conversions \$ gdb -q scinotation
Reading symbols from /home/ethereal/C/Conversions/scinotation...done.
(gdb) break 106
Breakpoint 1 at 0x400af7: file scinotation.c, line 106.
(gdb) run
Starting program: /home/ethereal/C/Conversions/scinotation
Please enter a double: 0.5

Breakpoint 1, Atof (s=0x7fffffffdee0 "0.5\n") at scinotation.c:106
106       for(i = afterDot, nPlaces = 0; s[i] != '\n'; i++, nPlaces++)
(gdb) print val
\$1 = 0.5
(gdb) cont
Continuing.

Program received signal SIGSEGV, Segmentation fault.
__strcpy_ssse3 () at ../sysdeps/x86_64/multiarch/strcpy-ssse3.S:44
44    ../sysdeps/x86_64/multiarch/strcpy-ssse3.S: No such file or directory.
(gdb) run
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/ethereal/C/Conversions/scinotation
Please enter a double: 0.05

Breakpoint 1, Atof (s=0x7fffffffdee0 "0.05\n") at scinotation.c:106
106       for(i = afterDot, nPlaces = 0; s[i] != '\n'; i++, nPlaces++)
(gdb) print val
\$2 = 0.050000000000000003
(gdb) cont
Continuing.

Program received signal SIGSEGV, Segmentation fault.
__strcpy_ssse3 () at ../sysdeps/x86_64/multiarch/strcpy-ssse3.S:44
44    ../sysdeps/x86_64/multiarch/strcpy-ssse3.S: No such file or directory.
(gdb)```

11. It's pretty damn close.

The only time you get an exact floating point number is when the number is a modal number: A real number that can be exactly represented in the number of bits a double has. I'll give you a real world example. 0.2 is a real number that does not get represented exactly in binary, instead you end up with the binary version of 0.1999999... So, basically, if it's not a modal number you are going to live with a binary approximation of the real number.

12. I solved the final problem (convert a number like 125.5 to 1.255e+2) using sprintf. However my array got a lot of junk.

edit:

I still didn't fix the output of a number like -125.5

Code:
```
gdb -q scinotation
Reading symbols from /home/ethereal/C/Conversions/scinotation...done.
(gdb) break 150
Breakpoint 1 at 0x400e0f: file scinotation.c, line 150.
(gdb) run
Starting program: /home/ethereal/C/Conversions/scinotation
Please enter a double: 125.5

Breakpoint 1, Atof (s=0x7fffffffdee0 "125.5\n") at scinotation.c:150
150          itoa(nCardinality, revncardinality);
(gdb) print straightstrval
\$1 = "1.255000", '\000' <repeats 400 times>, "t:\336\367\377\177\000\000\000\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000\003\000\000\000\000\000\000\000\300\251\377\367\377\177\000\000\207\360\226|\000\000\000\000\236B\336\367\377\177\000\000\000\000\000\000\000\000\000\000\a\000\000\000\000\000\000\000\240\326\377\377\377\177\000\000\377\377\377\377\000\000\000\000x\354\241\367\377\177\000\000\250\354\241\367\377\177\000\000p\326\377\377\377\177\000\000\000\000\000\000\000\000\000\000ظ\242\367\377\177", '\000' <repeats 18 times>, "Ȫ\377\367\377\177\000\000\300\244\377\367\377\177\000\000%Ϣ\367\377\177\000\000\370\271\242\367\377\177\000\000ظ\242\367\377\177\000\000\000\000\000\000\001\000\000\000\202\b\000\000\001", '\000' <repeats 11 times>, "\030\250\377\367\377\177\000\000\340\326\377\377\377\177\000\000\207\360\226|\000\000\000\000Ȫ\377\367\377\177\000\000\000\327\377\377\377\177\000\000\300\244\377\367\377\177\000\000#E\336\367\377\177\000\000\000\000\000\000\000\000\000\000Ȫ\377\367\377\177\000\000\001", '\000' <repeats 15 times>...
(gdb) cont
Continuing.
The double value in scientific notation is 1.255000e+2
[Inferior 1 (process 2966) exited normally]```
how's that?

13. The exercise is from KnR:

Extend atof to handle scientific notation of the form 123.45e-6 where a floating-point number may be followed by e or E and an optionally signed exponent.

What I want to do, despite of what the exercise mentioned, is convert something like

450 to 4.50e+2 and 0.05 to 5.0e-2
My two cents are that you would have a much easier time doing what you want to do if you looked at the binary representation of floating point numbers. The conversion from binary format to a decimal format should be relatively straightforward, unlike with string input. With string input you have to deal with a myriad of cases, meanwhile in binary format there are only a few formats I've remotely heard of, and IEEE standard floating point numbers are also extremely common, so it serves as a good starting point. Any decent display method probably does not do a ton of slow string manipulations.

What you are turning atof() into is nothing like atof() as well... it's supposed to ASCII to floating point. Meanwhile atof() implementations do not interpret scientific notation. I can understand where KnR is coming from, extending atof to handle this, and expecting you to return a value, but you've changed it so that we put in ASCII and we get more ASCII out. You will still learn a lot about strings, but atof() this is not.

14. what I wrote so far.

I almost got a scientific notation:

Code:
```
Please enter a double: -0.00012345
The double value in scientific notation is -12345e-8```
[code]

the code so far:

Code:
```#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <math.h>

#ifndef MAXSIZE
#define MAXSIZE 1000
#endif

void reverse(char* s, char* reversed)
{
int i, j;

for(i = strlen(s) - 1, j = 0; i >= 0; i--, j++)
reversed[j] = s[i];

reversed[j] = '\0';
}

void itoa(int n, char* s)
{
int sign, i;
i = 0;

if( (sign = n) < 0)     /* record sign */
n = -n;   /* turn n positive */
do {
s[i++] = abs(n % 10) + '0';
} while( (n /= 10) > 0);
if(sign < 0)
{
s[i++] = '-';
}

s[i] = '\0';
}

int isanumber(char* s) {

int i = 0;

while(s[i] != '\n')
{
if(isalpha((unsigned char) s[i]) || isblank( (unsigned char) s[i]) )
break;

i++;
}

if(s[i] == '\n')
return 1;
return 0;
}

char* Atof(char* s)
{
double val, power, cardinality;
int j, i, sign, afterDot, nPlaces, firstDigit, nCardinality;
char* notation;

/* (itoa returns the string from the end to the beginning, i.e. inverted) */
char revstrval[MAXSIZE];
char revstrnplaces[MAXSIZE];
char revncardinality[MAXSIZE];

/* the correct order of the string representation of the numbers */
char straightstrval[MAXSIZE];
char straightstrnplaces[MAXSIZE];
char straightncardinality[MAXSIZE];

afterDot = 0;
nCardinality = -1;

/* first part: get a double from the string */

/* while there is a space, keep iterating */
for(i = 0; isspace(s[i]); i++)
;

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

/* if there is a sign inside the array, just ignore it */
if(s[i] == '+' || s[i] == '-')
i++;

/* the next block converts the string to a double with the proper sign */
/* ------------------------------------------------- */

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

val = sign * val / power;

/* ------------------------------------------------- */

/* second part: write double in scientific notation */

/* count the number of places starting from the first digit after the dot */
for(i = afterDot, nPlaces = 0; s[i] != '\n'; i++, nPlaces++)
;

/* get the first digit */

for(i = 0; !isdigit(s[i]); i++)
;
if(isdigit(s[i]))
firstDigit = i;

/* ignore the sign */

for(i = 0; s[i] != '.'; i++)
if(s[i] == '-' || s[i] == '+')
{
i++;
break;
}

/* get the cardinality of the number */

for(j = i; s[j] != '.'; j++, nCardinality++)
;
cardinality = pow(10.0, nCardinality);

/* verify if first digit is zero and make sure there is, at least, one
* place (for numbers like 0.05) */

if(s[firstDigit] == '0' && power > 1.0)
{
val *= power;

/* get the string representation of value and of the places */

itoa(val, revstrval);
itoa(nPlaces, revstrnplaces);

reverse(revstrval, straightstrval);
reverse(revstrnplaces, straightstrnplaces);
notation = strcat(straightstrval, "e-");
notation = strncat(notation, straightstrnplaces, MAXSIZE);
}

/* verify if first digit is different from zero and make sure there is, at least, one
* place (for numbers like 125.5) */

else if(s[firstDigit] != '0' && power > 1.0)
{
val /= cardinality;

sprintf(straightstrval, "%f", val);
itoa(nCardinality, revncardinality);
reverse(revncardinality, straightncardinality);
notation = strcat(straightstrval, "e+");
notation = strncat(notation, straightncardinality, MAXSIZE);
}

return notation;
}

int main(void)
{
char strnum[MAXSIZE];
char notation[MAXSIZE];

do {
printf("Please enter a double: ");
fgets(strnum, sizeof(strnum), stdin);
}while(!isanumber(strnum));

strcpy(notation, Atof(strnum));
printf("The double value in scientific notation is %s\n", notation);

return 0;
}```

15. My two cents are that you would have a much easier time doing what you want to do if you looked at the binary representation of floating point numbers.
I will search for it.

edit:

Can you show me a good link about the algorithm process?

but atof() this is not
I see, you're completely right.

Popular pages Recent additions