I'm sorry, I wrote that program just to make it clearer what the
Quote:
problem I was worried about was. Should have tried compiling it before
posting.
>
I went through the code I actually use to test this problem, and
discovered that sprintf appends a NULL to the end of the char* it is
given. That's why the 3rd exponent character never messed up my smaller
numbers.
sprintf uses the standard string convention of appending '\0' - see my
comment below...
Quote:
>
Illustration -
value in string: -1.0000000000000000+e123
value in string: -1.2345678900000000+e10NULL
>
Which means that, given a number which serializes to a 24-character
long string, my buffer must have overflowed. Am I right?
Yes, and by doing so you have entered the realm of undefined behaviour
Quote:
>
/* Start of code ==============================*/
/* Remember to compile with -lm */
>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
>
#define CONTRAST(x) (printf ("Original : "), \
printf (fmtString, (x)), \
printf ("\nStored : "), \
sprintf (string, fmtString, (x)), \
printf (string), \
printf ("\nRetrieved : "), \
\
printf (fmtString, atof(string)), \
printf ("\n"))
You are still using atof() - consider using sscanf instead.
Quote:
>
#define STRING_LEN 24
/* 1 sign + 1 decimal + 1 decimal point
* + 16 fractionals + 5 exponent = 24 */
BANG! If you use all of these values to the maximum you will run into
problems. The strXXX functions expect valid strings which must have the
null termination character '\0' appended, therefore you must allow space for
this too. Simply add 1 to your STRING_LEN to allow for this. Incidentally
a good book or tutorial should explain this - if it doesnt, get a better
one!
Quote:
>
int main (int argc, char **argv) {
double one, two, three, four;
char string[STRING_LEN];
char *fmtString;
int i;
>
one = -1 * pow (10, 304);
two = -1 * pow (10, 20);
three = -1 * pow (10, 304);
four = -1 * pow (10, 309); /* inf */
>
fmtString = "% .16e";
You can do this initialisation when defined fmtString with either:
char *fmtString = "%.16e";
char fmtString[] = "%.16e";
Quote:
>
/* --- start testing ------------------------------*/
>
/* Fill up all 3 exponent characters. */
CONTRAST (one);
printf("Simulated write() : ");
for (i = 0; i < 24; i++) {
printf ("%c", string[i]);
}
printf ("\n\n");
If you do as I suggested above with the null termination character, you can
replace this for loop simply with
printf("%s\n\n", string);
Quote:
>
/* This has only 2 exponent characters.
* Will the third exp character from
* before appear here? */
CONTRAST (two);
printf("Simulated write() : ");
for (i = 0; i < 24; i++) {
printf ("%c", string[i]);
}
printf ("\n\n");
>
/* Fill up all 3 exponent characters. */
CONTRAST (three);
printf("Simulated write() : ");
for (i = 0; i < 24; i++) {
printf ("%c", string[i]);
}
printf ("\n\n");
>
/* This shows up on my pc as "-inf" with
* printf. Simulated write() shows me that
* the string actually contains garbage
* characters from the previous operation. */
CONTRAST (four);
printf("Simulated write() : ");
for (i = 0; i < 24; i++) {
printf ("%c", string[i]);
}
printf ("\n\n");
>
/* NOTE: Just discovered that the sim write
* prints 1 less character than the string's
* length for the previous operation. Does
* sprintf append a NULL to the string? If
* so, 'string' is overflowing! */
YES!
Quote:
>
if (string[4] == 0) {
printf ("**NOTE: NULL appended after \"-inf\"! -- sprintf?\n");
}
>
return 0;
}
>
I have changed your code a little to that below. Everything seems to work
apart from when using sscanf to read back
-1.#INF000000000000e+000
The return value is -1.00000000000 as it sees the # and thinks that this is
the end of the number. But otherwise everything seems to work.
Allan
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
/* 1 sign + 1 decimal + 1 decimal point
* + 16 fractionals + 5 exponent + '\0' = 25 */
#define STRING_LEN 25
void contrast(double x)
{
char string[STRING_LEN];
double a;
printf ("Original\t: %.16e\n", x);
printf ("Stored\t\t: %.16e\n", x);
sprintf(string, "%.16e", x);
printf ("Converted\t: %s\n", string);
if (sscanf (string, "%le", &a) != 1)
{
printf("Failure reading double value, setting to 0\n");
a = 0;
}
printf ("Retrieved\t: %.16e\n\n", a);
}
int main (void)
{
double one = -1 * pow (10, 304),
two = -1 * pow (10, 20),
three = -1 * pow (10, 304),
four =-1 * pow (10, 309);
contrast(one);
contrast(two);
contrast(three);
contrast(four);
return 0;
}