开发者

Number Parser program: why strtol/strtod fails for exponential and hex numbers

开发者 https://www.devze.com 2023-04-06 16:27 出处:网络
I have written a small code, which breaks any string (exported as environment variable) into it\'s whole number and fractional part.

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
0

精彩评论

暂无评论...
验证码 换一张
取 消