3

I have the following code:

int check_for_non_number(char *input, int lineNum)
{
    errno = 0;
    char *endptr;
    printf("%s\n",input);
    double xnum = strtod(input, &endptr);
    // IF endptr FOUND A NON-VALID ENTRY AND THAT ENTRY IS NOT THE NEW LINE CHARACTER THEN ITS AN ERROR
    if((*endptr) && (*endptr != '\n'))
    {
        return 1;
    }
    // We still want to process this number, even if it is represented as inf, so inform the user in the terminal and then return 0
    else if (errno == ERANGE)
    {
        printf("\nOPERAND IS OUT OF RANGE ON LINE %d\n",lineNum);
        return 0;
    }
    // ELSE IF endptr FOUND A NON-VALID ENTRY AND THAT ENTRY IS THE NEW LINE CHARACTER THEN RETURN 2 TO CHECK IF IT SHOULD BE A NEW LINE
    else if((*endptr) && (*endptr == '\n'))
    {
        return 2;
    }
    else
    {
        return 0;
    }
}

the value of input came from strtok() and was passed into the check_for_non_number function...

The problem is when strtok reads "inf" from the text file, my function returns 1, implying the first argument was true... For clarity, the "inf" in the text file is located in the middle of the line in the text file so there is text before and after it and strtok is being used before and after it.

If anyone could shed some light on why strtod() is not handling "inf" as an input to that would be greatly appreciated!

prazuber
  • 1,352
  • 10
  • 26
sdepot
  • 41
  • 6
  • When I pass `"inf"` to your function, it returns 0, as I would expect. I'm inclined to guess, therefore, that you are in fact passing something different. Present a [mcve] that demonstrates your problem. – John Bollinger Dec 12 '17 at 16:30
  • 1
    Try `printf("{%s}\n",input);` (note the braces) to get a clearer picture of what the function was passed. – dbush Dec 12 '17 at 16:32
  • 1
    What's your platform? Maybe the `strtod` implementation on your platform does not implement converting of `"INF"`. – Jabberwocky Dec 12 '17 at 16:34
  • I am running with windows but I am compiling in Sublime Text 3 using gcc – sdepot Dec 12 '17 at 16:38
  • [This works for me](https://www.ideone.com/QdJ4k0) <<--- click here.. – Jabberwocky Dec 12 '17 at 16:38
  • 2
    Aside: More prudent to code `errno = 0;` just before `strtod()` rather than before `printf()` – chux - Reinstate Monica Dec 12 '17 at 16:48
  • @MichaelWalz When I print out xnum in my code the same way that you did it, it returns 0.000000 – sdepot Dec 12 '17 at 16:49
  • 1
    Right after `double xnum = strtod(input, &endptr);`, add `printf("<%s> <%s> %.20e %d\n", input, endptr, xnum, errno);` and report results to provide more info. – chux - Reinstate Monica Dec 12 '17 at 16:53
  • @chux ` 0.00000000000000000000e+000 0` – sdepot Dec 12 '17 at 16:54
  • @dbush `{inf}` is what is printed to the terminal with your suggestion – sdepot Dec 12 '17 at 16:56
  • Some systems use a different encoding for infinity with text than `"inf"` and some do not support it at all. Try printing infinity to see what and if your system supports that. e.g. `printf("%.e\n", exp(1000));` Perhaps it is `"+inf"`. If it does print something infinity-ish, try `strtod()` on it. – chux - Reinstate Monica Dec 12 '17 at 16:56
  • @chux the code wont compile with that exp(1000) it says implicit declaration of function 'exp' and incompatible implicit declaration of built-in function 'exp' – sdepot Dec 12 '17 at 16:59
  • `exp()` needs `#include ` or try `printf("%.e\n", 1.0e300 * 1.0e300);` – chux - Reinstate Monica Dec 12 '17 at 17:00
  • @chux Ah, of course... ok it prints `.e` when I run that – sdepot Dec 12 '17 at 17:02
  • Re-read as I edited the comment and first forgot the `%`, Now it is `printf("%.e\n"...` – chux - Reinstate Monica Dec 12 '17 at 17:03
  • @chux `1e+000` after adding % – sdepot Dec 12 '17 at 17:05
  • Hmmm `1e+000` is a non-compliant C output (only 2 zeros should be in the exponent.) So what compiler, version and OS are you using? – chux - Reinstate Monica Dec 12 '17 at 17:07
  • @chux my computer is running windows 10, and I am writing the code in Sublime Text 3, and I am using the command: `gcc human_readable_to_bin.c -o human_readable_to_bin.exe -L. -lsoftfloat & human_readable_to_bin.exe` to create and run the executable – sdepot Dec 12 '17 at 17:42
  • Right before `return 1;`, add `printf("*end = %c (%d), diff=%d\n", *end, *end, *end - input);` and tell us what you get. – dbush Dec 12 '17 at 18:00
  • @dbush when I run exactly what you put I get an error about end so I assume you meant endptr... So I changed the `*end` to `*endptr` and I get an error about `invalid operands to binary - (have 'int' and 'char *') ` at the point `*endptr - input` pointing to the minus sign – sdepot Dec 12 '17 at 18:06
  • My mistake, that should be `endptr - input`. – dbush Dec 12 '17 at 18:07
  • @dbush ok just changed it and it compiled and produced: `*end = i (105), diff=0` – sdepot Dec 12 '17 at 18:08
  • @sdepot As much as your code is trying to convert a textual _infinity_ to numeric _infinity_, generating numeric _infinity_ and printing that also seems to be a problem - and one that I think needs to be solved first - if possible - it should be easier to solve. – chux - Reinstate Monica Dec 12 '17 at 18:13
  • So it seems "inf" is not recognized as a valid string for infinity. According to section 7.22.1.3 of the standard, "inf" is a valid sequence for `strtod`. It looks like you have a non-conforming compiler. – dbush Dec 12 '17 at 18:13
  • @dbush A compliant compiler need not support infinity. – chux - Reinstate Monica Dec 12 '17 at 18:13
  • @dbush so it sounds like I'm SOL at handling inf/infinity then with my current setup – sdepot Dec 12 '17 at 18:14
  • @sdepot What happens with `double x = 10; for (int i=0; i<12; i++) { x *= x; printf("%e\n", x); };`? I get `1.000000e+02 1.000000e+04 1.000000e+08 1.000000e+16 1.000000e+32 1.000000e+64 1.000000e+128 1.000000e+256 inf inf inf inf`. – chux - Reinstate Monica Dec 12 '17 at 18:20
  • @chux 1.000000e+002 1.000000e+004 1.000000e+008 1.000000e+016 1.000000e+032 1.000000e+064 1.000000e+128 1.000000e+256 1.#INF00e+000 1.#INF00e+000 1.#INF00e+000 1.#INF00e+000 – sdepot Dec 12 '17 at 18:28
  • If it helps with any of this... When I set this up I installed MinGW w the packages: `-mingw-developer-toolkit -mingw32-base -mingw32-gcc-g++ -mingw32-gcc-objc -msys-base` then added my PATH to include: `• \bin • \msys\1.0\bin` then in to connect the libraries that Im using in my code I ran the command: “mingw32-make” in the directory the libraries were in then use `gcc human_readable_to_bin.c -o human_readable_to_bin.exe -L. -lsoftfloat & human_readable_to_bin.exe` to build/run code in Sublime – sdepot Dec 12 '17 at 18:48
  • So does `strtod("1.#INF00e+000", &endptr)` work as hoped? – chux - Reinstate Monica Dec 12 '17 at 19:12
  • @chux No that causes it to print the following: `*end = # (35), diff=-2226968` – sdepot Dec 12 '17 at 20:49
  • ALL.... I implemented a workaround inside of the `check_for_non_number()` that checks if `input` is any variation of infinity that `strtod()` SHOULD be able to handle and returns either 3 or 4 (inf or -inf, respectively) without calling `strtod()` and then where I return from the function call I check if the return value is 3 or 4 and I set the value of the operand to `"1e9999"` or `"-1e9999"`, respectively... which for whatever reason is handled properly when `strtod()` gets called with the operand further along in the code... THANK YOU for all of the responses!!! – sdepot Dec 12 '17 at 20:52
  • 1
    See [strtod does not implement the C99](https://connect.microsoft.com/VisualStudio/feedback/details/794104/strtod-does-not-implement-the-c99-c-11-standard), [strtod has changed from 4.5.2 to 4.7.0](http://mingw-users.1079350.n2.nabble.com/strtod-has-changed-from-4-5-2-to-4-7-0-td7579794.html), etc. – chux - Reinstate Monica Dec 12 '17 at 20:55

1 Answers1

1

The C11 standard requires strtod() to recognize INF:

The expected form of the subject sequence is an optional plus or minus sign, then one of the following:

  • a nonempty sequence of decimal digits optionally containing a decimal-point character, then an optional exponent part as defined in 6.4.4.2;
  • a 0x or 0X, then a nonempty sequence of hexadecimal digits optionally containing a decimal-point character, then an optional binary exponent part as defined in 6.4.4.2;
  • INF or INFINITY, ignoring case
  • NAN or NAN(n-char-sequenceopt), ignoring case in the NAN part, where:

     n-char-sequence:
             digit
             nondigit
             n-char-sequence digit
             n-char-sequence nondigit
    

The subject sequence is defined as the longest initial subsequence of the input string, starting with the first non-white-space character, that is of the expected form. The subject sequence contains no characters if the input string is not of the expected form.

If you can demonstrate (by calling your function with strings such as "inf") that it fails to convert that to an infinity, then you have a bug in the strtod() provided by your implementation — or it conforms to C90 but not C99 or later. The C90 specification for strtod() did not mention the hex formats (those were added to the language in C99), nor the infinities or NaN handling. From C99 onwards, support for those notations is required.

If you're stuck with a C90 runtime library, you'll have to upgrade to a newer version with the support, or implement your own variant of strtod(), or co-opt an open source implementation. Be wary of how much extra support code an open source implementation also requires for handling locales properly, etc.

Note that the Microsoft Visual Studio 2017 runtime appears to support the C11 specification of strtod(), as does the 2015 version (which is the earliest available on the Microsoft Docs web site). If you're using a significantly older version of MSVS, an upgrade is in order.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • The OP's system is not compliant, namely his C runtime library does not implement `printf` nor `strtod` correcty. Most likely an old Microsoft C library. Is there a way to fix this? – chqrlie Mar 13 '19 at 06:50
  • @chqrlie: the only way to 'fix' it is to write your own `strtod()` or download an open source version of it that doesn't require too much other supporting code (locale support could be a problem, for example). Or upgrade to a newer system. – Jonathan Leffler Mar 13 '19 at 06:52
  • The stream of comments after the question are too long-winded (there are just too many of them), so it isn't conducive to reading the whole lot. – Jonathan Leffler Mar 13 '19 at 06:53
  • Yes, that's my question: is there a way to upgrade the Microsoft C runtime library and support headers? – chqrlie Mar 13 '19 at 06:54
  • To my surprise, no one mentioned in the comments the obvious root cause for the lack of conformity. your answer is correct: *the implementation is not Standard conformant*, re-implementing `printf` and `strtod` is not for the faint of heart, even from open source library code. Upgrading the runtime environment seems a better option, if possible. – chqrlie Mar 13 '19 at 06:59