Number Parser program: why strtol/strtod fails for exponential and hex numbers
I have written a small code, which breaks any string (exported as environment variable) into it's whole number and fractional part.
Example: export ENV_NUM=3.45
The program will print: whole number: 3 Fractional: 0.45
Additionally this program also scans if the exported number is out-of-range or invalid number in which case, it exits printing an error message.
To simplify things for reader let me explain the logic i have used:
I have scanned the number and the number is broken into 2 parts using strtok
whereever it finds a decimal (.), and then assigns the first token to integral part and the next token to fractional part.
Problem faced: Now the problem开发者_如何学运维 with this program is: it gives erroneous results if the exported number is a hex number or an exponential number.
Can you let me know what is the problem?
#include <stdlib.h>
#include <stdio.h>
#include <limits.h>
#include <errno.h>
#include <string.h>
static time_t wholeNumber;
static float fractional;
void numberParser(void);
int main(int argc, char **argv)
{
numberParser( );
printf("Whole number: %12d, Fractional: %5.8f\n", wholeNumber, fractional);
return 0;
}
void numberParser( void )
{
char * charPtr, * numberFormatErr;
charPtr = getenv("ENV_NUM");
if ( charPtr == NULL )
return;
double envVal = strtod(charPtr, &numberFormatErr);
/* This part checks if the string is a valid number and not negative */
if ( (numberFormatErr == charPtr) || (*numberFormatErr != '\0') ) {
printf("exited: ENV_NUM is not a number\n");
exit(1);
}
else if ( envVal < 0 ) {
printf("exited: ENV_NUM a negative number\n");
exit(1);
}
/* This part breaks the string into integral and float part */
char * tokens = strtok(charPtr, ".");
int count = 0;
errno = 0;
while ( tokens != NULL ) {
//printf("Token scanned: %s\n",tokens);
long d = strtol(tokens, NULL, 10);
//printf("token to long: %5d\n",d);
if ( errno == ERANGE && d == LONG_MAX ) {
printf("exited: ENV_NUM not in valid range.");
exit(1);
}
if ( count == 0 ) {
( wholeNumber = d );
}
tokens = strtok(NULL, " ");
count++;
}
fractional = (envVal) - (double)(wholeNumber);
}
Here is the output:
Correct output for normal numbers
[time_related]$ ./a.out
Whole number: 3, Fractional: 0.56000000
Wrong output for hex: [time_related]$ export ENV_NUM=0x21
[time_related]$ ./a.out
Whole number: 0, Fractional: 33.00000000 (should be 33, 0)
Wrong output for exponential: [time_related]$ export ENV_NUM=3e3
[time_related]$ ./a.out
Whole number: 3, Fractional: 2997.00000000 (should be 3000, 0)
I am surprised that strtod parses hex numbers, as I looked at two references and they don't mention such capability, but why not.
Note that 0x21 = 33, not 35.
You made intermediary print, use it: for 0x21, envVal is 33, strtol fails to parse beyond the x, so it returns 0, and fractional is envVal - 0 = 33
Idem for 3e3: it isn't an long number, so strtol returns 3, and fractional is 3000-3
You should add a check on the result of strtol, like you did for strtod...
#include <stdio.h>
#include <stdlib.h>
double numberParser(char *str){
double val;
long lval;
char *p;
val=strtod(str, &p);//exponential number OK!, 0x prefix from C99
if(*p == '\0')
return val;
else if(str[0] == '0' && (str[1] == 'x' || str[1] == 'X')){
lval = strtol(str, &p, 16);//strtol(str, &p, 0) better
if(*p == '\0')
return (double)lval;
}
return -1.0;//error
}
int main(int argc, char **argv){
double wk;
int i;
i= wk = numberParser("3.45");
printf("Whole number: %12d, Fractional: %5.8f\n", i, wk - i);
i= wk = numberParser("0x21");
printf("Whole number: %12d, Fractional: %5.8f\n", i, wk - i);
i= wk = numberParser("3e3");
printf("Whole number: %12d, Fractional: %5.8f\n", i, wk - i);
return 0;
}
output:
Whole number: 3, Fractional: 0.45000000
Whole number: 33, Fractional: 0.00000000
Whole number: 3000, Fractional: 0.00000000
精彩评论