Convert string to double and check for errors

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;







up vote
3
down vote

favorite












The following program converts a string to a double, and checks for errors. It uses the strtod() function to do the conversion, and follows the example given for the strtol() function from the man page to do the error checking.



As an example, the program is executed as follows:



$ ./a.out 123.45


I would really appreciate any comments regarding the code, specifically whether there are any issues to be address or whether the code can be made more efficient or improved in any way.



#include <stdlib.h>
#include <float.h>
#include <stdio.h>
#include <errno.h>

int main(int argc, char *argv)







share|improve this question



























    up vote
    3
    down vote

    favorite












    The following program converts a string to a double, and checks for errors. It uses the strtod() function to do the conversion, and follows the example given for the strtol() function from the man page to do the error checking.



    As an example, the program is executed as follows:



    $ ./a.out 123.45


    I would really appreciate any comments regarding the code, specifically whether there are any issues to be address or whether the code can be made more efficient or improved in any way.



    #include <stdlib.h>
    #include <float.h>
    #include <stdio.h>
    #include <errno.h>

    int main(int argc, char *argv)







    share|improve this question























      up vote
      3
      down vote

      favorite









      up vote
      3
      down vote

      favorite











      The following program converts a string to a double, and checks for errors. It uses the strtod() function to do the conversion, and follows the example given for the strtol() function from the man page to do the error checking.



      As an example, the program is executed as follows:



      $ ./a.out 123.45


      I would really appreciate any comments regarding the code, specifically whether there are any issues to be address or whether the code can be made more efficient or improved in any way.



      #include <stdlib.h>
      #include <float.h>
      #include <stdio.h>
      #include <errno.h>

      int main(int argc, char *argv)







      share|improve this question













      The following program converts a string to a double, and checks for errors. It uses the strtod() function to do the conversion, and follows the example given for the strtol() function from the man page to do the error checking.



      As an example, the program is executed as follows:



      $ ./a.out 123.45


      I would really appreciate any comments regarding the code, specifically whether there are any issues to be address or whether the code can be made more efficient or improved in any way.



      #include <stdlib.h>
      #include <float.h>
      #include <stdio.h>
      #include <errno.h>

      int main(int argc, char *argv)









      share|improve this question












      share|improve this question




      share|improve this question








      edited Jul 19 at 2:33









      200_success

      123k14143399




      123k14143399









      asked Jul 18 at 20:04









      Domenic

      161




      161




















          1 Answer
          1






          active

          oldest

          votes

















          up vote
          4
          down vote













          Good use of errno, but can be improved.



          The below is decent code for detecting overflow, yet would benefit with improvements.



          On overflow, the return value is HUGE_VAL, not necessarily DBL_MAX. HUGE_VAL may be a large finite or an infinity.



          if ((errno == ERANGE && (val == DBL_MAX || val == -DBL_MAX)) || ...) 
          perror("strtod");
          exit(EXIT_FAILURE);

          // replace with
          if ((errno == ERANGE && (val == HUGE_VAL || val == -HUGE_VAL)) || ...) {


          Underflow may also raise ERANGE. Look at the code that attempts to discern that and review the spec.



          if (... || (errno != 0 && val == 0.0)) 
          perror("strtod");
          exit(EXIT_FAILURE);




          If the result underflows ..., the functions return a value whose magnitude is no greater than the smallest normalized positive number in the return type; whether errno acquires the value ERANGE is implementation-defined.




          OP's code incorrectly assumes 0.0 on underflow.



          With so many options with 1) if errno is set and 2) what value is return, portable code nneds to fix a mess.



          Portable code would use the following, yet that gives up useful conversions by some of values in the sub-normal range DBL_TRUE_MIN to DBL_MIN.



          if (fabs(val) < DBL_MIN) 
          if (val > 0.0) val = DBL_MIN;
          else if (val < 0.0) val = -DBL_MIN;
          // val is now, DBL_MIN, -DBL_MIN, 0.0, or -0.0

          // pedantic code would also reset errno = 0 if it changed due to `strtod()`



          A somewhat less portable handing, yet retains sub-normal values - just accept any wee value.



          if (fabs(val) < DBL_MIN) 
          // reset errno = 0 if it changed due to `strtod()`



          No conversion



          Good test for no conversion, yet some strtod() set errno in the no conversion case. To simplify errno processing in the previous block, I'd recommend to do the endptr == str before looking at errno.



          Use exponential notation for output



          "%f" may print all values of magnitude less than 0.0000005 as 0.000000. This is not informative. With large values like DBL_MAX, perhaps hundreds of digits are printed of which past the first 20 or so are rarely of interest.



          Instead:



          #include <float.h>

          printf("strtod() returned %.*gn", DBL_DECIMAL_DIG, DBL_val);
          // or
          printf("strtod() returned %.*en", DBL_DECIMAL_DIG - 1, val);


          Fancier code would assess the length of the argv[1] string for exponential notation and number of digits and then print out in a like-wise fashion.



          Tolerate trailing white-space



          OP code's alerts with input like "123 ", but not with " 123". Suggest silence on trailing white-space.



          // add
          while (issapce((unsigned char) *endptr))
          endptr++;


          if (*endptr != '') /* Not necessarily an error... */
          printf("Further characters after number: %sn", endptr);


          Such non-numeric output may be best done on stderr rather than stdout.




          Suggested alternative.



          Useful as a basis for some ideas.



          // Return status for my_strtod().
          // Higher values are more problematic.
          typedef enum
          my_strtod_OK,
          my_strtod_Underflow,
          my_strtod_Overflow,
          my_strtod_ExtraJunk,
          my_strtod_NoConvertableText,
          my_strtod_N
          my_strtod_T;

          // Convert a pointer to a string to double and saves its value in *dest.
          // Return conversion status.
          //
          // `errno` is temporarily cleared by this function. Its value on return:
          // 1) Should strtod(*s, ...) set errno, then that is its value.
          // 2) Otherwise errno is restored to its original value.
          my_strtod_T my_strtod(double *dest, const char *s)
          char *endptr;

          int errno_original = errno;
          errno = 0;
          *dest = strtod(s, &endptr);
          int errno_my_strtod = errno;
          if (errno == 0)
          errno = errno_original;


          if (s == endptr)
          return my_strtod_NoConvertableText;


          while (isspace((unsigned char ) *endptr))
          endptr++;

          if (*endptr)
          return my_strtod_ExtraJunk;


          if (errno_my_strtod == ERANGE && fabs(*dest) == HUGE_VAL)
          return my_strtod_Overflow;


          if (errno_my_strtod == ERANGE && fabs(*dest) <= DBL_MIN)
          return my_strtod_Underflow;


          // Note: at this point, errno may be set
          return my_strtod_OK;



          Selected C specs concerning errno:



          /*
          * errno ... has type int ... the value of which is set to a positive error
          * number by several library functions. C11dr §7.5 2
          *
          * errno ... is never set to zero by any library function. The value of errno
          * may be set to nonzero by a library function call whether or not there is an
          * error, provided the use of errno is not documented in the description of
          * the function ... C11dr §7.5 3
          *
          * Of course, a library function can save the value of errno on entry and
          * then set it to zero, as long as the original value is restored if errno’s
          * value is still zero just before the return. C11dr footnote 202
          */





          share|improve this answer























          • Thank you very much for reviewing my code. Those are all great suggestions. I really appreciate it. Cheers!
            – Domenic
            Jul 19 at 22:20










          • Oh wow, I definitely like your suggested alternative code, where my_strtod returns the status for each of the possibilities. When testing for underflow, though, won't the test fail in some cases since errno may not necessary be set to ERANGE in some implementations, as you indicated earlier?
            – Domenic
            Jul 19 at 23:31











          • @Domenic won't the test fail --> depends on what is "failure" and the coding goal in this case. In practice, conversion to a |value| less than DBL_MIN is simply a tolerable non-failure result - regardless of errno. Portable code could use if (fabs(val) < DBL_MIN) if (val > 0.0) val = DBL_MIN; else if (val < 0.0) val = -DBL_MIN; return my_strtod_Underflow;
            – chux
            Jul 20 at 1:05











          • @Domenic Too be clear, in the cases of fabs(y) <= DBL_MIN, there is too much left to implementation defined behavior for super portable code. This all gets very messy once one considers that strod() is subject to different results based on rounding modes (5 of them). Do the best you can and document your expectations.
            – chux
            Jul 20 at 1:08







          • 1




            I've now asked about this over on SO.
            – Toby Speight
            Jul 20 at 13:11










          Your Answer




          StackExchange.ifUsing("editor", function ()
          return StackExchange.using("mathjaxEditing", function ()
          StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
          StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
          );
          );
          , "mathjax-editing");

          StackExchange.ifUsing("editor", function ()
          StackExchange.using("externalEditor", function ()
          StackExchange.using("snippets", function ()
          StackExchange.snippets.init();
          );
          );
          , "code-snippets");

          StackExchange.ready(function()
          var channelOptions =
          tags: "".split(" "),
          id: "196"
          ;
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function()
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled)
          StackExchange.using("snippets", function()
          createEditor();
          );

          else
          createEditor();

          );

          function createEditor()
          StackExchange.prepareEditor(
          heartbeatType: 'answer',
          convertImagesToLinks: false,
          noModals: false,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: null,
          bindNavPrevention: true,
          postfix: "",
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          );



          );








           

          draft saved


          draft discarded


















          StackExchange.ready(
          function ()
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f199775%2fconvert-string-to-double-and-check-for-errors%23new-answer', 'question_page');

          );

          Post as a guest






























          1 Answer
          1






          active

          oldest

          votes








          1 Answer
          1






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes








          up vote
          4
          down vote













          Good use of errno, but can be improved.



          The below is decent code for detecting overflow, yet would benefit with improvements.



          On overflow, the return value is HUGE_VAL, not necessarily DBL_MAX. HUGE_VAL may be a large finite or an infinity.



          if ((errno == ERANGE && (val == DBL_MAX || val == -DBL_MAX)) || ...) 
          perror("strtod");
          exit(EXIT_FAILURE);

          // replace with
          if ((errno == ERANGE && (val == HUGE_VAL || val == -HUGE_VAL)) || ...) {


          Underflow may also raise ERANGE. Look at the code that attempts to discern that and review the spec.



          if (... || (errno != 0 && val == 0.0)) 
          perror("strtod");
          exit(EXIT_FAILURE);




          If the result underflows ..., the functions return a value whose magnitude is no greater than the smallest normalized positive number in the return type; whether errno acquires the value ERANGE is implementation-defined.




          OP's code incorrectly assumes 0.0 on underflow.



          With so many options with 1) if errno is set and 2) what value is return, portable code nneds to fix a mess.



          Portable code would use the following, yet that gives up useful conversions by some of values in the sub-normal range DBL_TRUE_MIN to DBL_MIN.



          if (fabs(val) < DBL_MIN) 
          if (val > 0.0) val = DBL_MIN;
          else if (val < 0.0) val = -DBL_MIN;
          // val is now, DBL_MIN, -DBL_MIN, 0.0, or -0.0

          // pedantic code would also reset errno = 0 if it changed due to `strtod()`



          A somewhat less portable handing, yet retains sub-normal values - just accept any wee value.



          if (fabs(val) < DBL_MIN) 
          // reset errno = 0 if it changed due to `strtod()`



          No conversion



          Good test for no conversion, yet some strtod() set errno in the no conversion case. To simplify errno processing in the previous block, I'd recommend to do the endptr == str before looking at errno.



          Use exponential notation for output



          "%f" may print all values of magnitude less than 0.0000005 as 0.000000. This is not informative. With large values like DBL_MAX, perhaps hundreds of digits are printed of which past the first 20 or so are rarely of interest.



          Instead:



          #include <float.h>

          printf("strtod() returned %.*gn", DBL_DECIMAL_DIG, DBL_val);
          // or
          printf("strtod() returned %.*en", DBL_DECIMAL_DIG - 1, val);


          Fancier code would assess the length of the argv[1] string for exponential notation and number of digits and then print out in a like-wise fashion.



          Tolerate trailing white-space



          OP code's alerts with input like "123 ", but not with " 123". Suggest silence on trailing white-space.



          // add
          while (issapce((unsigned char) *endptr))
          endptr++;


          if (*endptr != '') /* Not necessarily an error... */
          printf("Further characters after number: %sn", endptr);


          Such non-numeric output may be best done on stderr rather than stdout.




          Suggested alternative.



          Useful as a basis for some ideas.



          // Return status for my_strtod().
          // Higher values are more problematic.
          typedef enum
          my_strtod_OK,
          my_strtod_Underflow,
          my_strtod_Overflow,
          my_strtod_ExtraJunk,
          my_strtod_NoConvertableText,
          my_strtod_N
          my_strtod_T;

          // Convert a pointer to a string to double and saves its value in *dest.
          // Return conversion status.
          //
          // `errno` is temporarily cleared by this function. Its value on return:
          // 1) Should strtod(*s, ...) set errno, then that is its value.
          // 2) Otherwise errno is restored to its original value.
          my_strtod_T my_strtod(double *dest, const char *s)
          char *endptr;

          int errno_original = errno;
          errno = 0;
          *dest = strtod(s, &endptr);
          int errno_my_strtod = errno;
          if (errno == 0)
          errno = errno_original;


          if (s == endptr)
          return my_strtod_NoConvertableText;


          while (isspace((unsigned char ) *endptr))
          endptr++;

          if (*endptr)
          return my_strtod_ExtraJunk;


          if (errno_my_strtod == ERANGE && fabs(*dest) == HUGE_VAL)
          return my_strtod_Overflow;


          if (errno_my_strtod == ERANGE && fabs(*dest) <= DBL_MIN)
          return my_strtod_Underflow;


          // Note: at this point, errno may be set
          return my_strtod_OK;



          Selected C specs concerning errno:



          /*
          * errno ... has type int ... the value of which is set to a positive error
          * number by several library functions. C11dr §7.5 2
          *
          * errno ... is never set to zero by any library function. The value of errno
          * may be set to nonzero by a library function call whether or not there is an
          * error, provided the use of errno is not documented in the description of
          * the function ... C11dr §7.5 3
          *
          * Of course, a library function can save the value of errno on entry and
          * then set it to zero, as long as the original value is restored if errno’s
          * value is still zero just before the return. C11dr footnote 202
          */





          share|improve this answer























          • Thank you very much for reviewing my code. Those are all great suggestions. I really appreciate it. Cheers!
            – Domenic
            Jul 19 at 22:20










          • Oh wow, I definitely like your suggested alternative code, where my_strtod returns the status for each of the possibilities. When testing for underflow, though, won't the test fail in some cases since errno may not necessary be set to ERANGE in some implementations, as you indicated earlier?
            – Domenic
            Jul 19 at 23:31











          • @Domenic won't the test fail --> depends on what is "failure" and the coding goal in this case. In practice, conversion to a |value| less than DBL_MIN is simply a tolerable non-failure result - regardless of errno. Portable code could use if (fabs(val) < DBL_MIN) if (val > 0.0) val = DBL_MIN; else if (val < 0.0) val = -DBL_MIN; return my_strtod_Underflow;
            – chux
            Jul 20 at 1:05











          • @Domenic Too be clear, in the cases of fabs(y) <= DBL_MIN, there is too much left to implementation defined behavior for super portable code. This all gets very messy once one considers that strod() is subject to different results based on rounding modes (5 of them). Do the best you can and document your expectations.
            – chux
            Jul 20 at 1:08







          • 1




            I've now asked about this over on SO.
            – Toby Speight
            Jul 20 at 13:11














          up vote
          4
          down vote













          Good use of errno, but can be improved.



          The below is decent code for detecting overflow, yet would benefit with improvements.



          On overflow, the return value is HUGE_VAL, not necessarily DBL_MAX. HUGE_VAL may be a large finite or an infinity.



          if ((errno == ERANGE && (val == DBL_MAX || val == -DBL_MAX)) || ...) 
          perror("strtod");
          exit(EXIT_FAILURE);

          // replace with
          if ((errno == ERANGE && (val == HUGE_VAL || val == -HUGE_VAL)) || ...) {


          Underflow may also raise ERANGE. Look at the code that attempts to discern that and review the spec.



          if (... || (errno != 0 && val == 0.0)) 
          perror("strtod");
          exit(EXIT_FAILURE);




          If the result underflows ..., the functions return a value whose magnitude is no greater than the smallest normalized positive number in the return type; whether errno acquires the value ERANGE is implementation-defined.




          OP's code incorrectly assumes 0.0 on underflow.



          With so many options with 1) if errno is set and 2) what value is return, portable code nneds to fix a mess.



          Portable code would use the following, yet that gives up useful conversions by some of values in the sub-normal range DBL_TRUE_MIN to DBL_MIN.



          if (fabs(val) < DBL_MIN) 
          if (val > 0.0) val = DBL_MIN;
          else if (val < 0.0) val = -DBL_MIN;
          // val is now, DBL_MIN, -DBL_MIN, 0.0, or -0.0

          // pedantic code would also reset errno = 0 if it changed due to `strtod()`



          A somewhat less portable handing, yet retains sub-normal values - just accept any wee value.



          if (fabs(val) < DBL_MIN) 
          // reset errno = 0 if it changed due to `strtod()`



          No conversion



          Good test for no conversion, yet some strtod() set errno in the no conversion case. To simplify errno processing in the previous block, I'd recommend to do the endptr == str before looking at errno.



          Use exponential notation for output



          "%f" may print all values of magnitude less than 0.0000005 as 0.000000. This is not informative. With large values like DBL_MAX, perhaps hundreds of digits are printed of which past the first 20 or so are rarely of interest.



          Instead:



          #include <float.h>

          printf("strtod() returned %.*gn", DBL_DECIMAL_DIG, DBL_val);
          // or
          printf("strtod() returned %.*en", DBL_DECIMAL_DIG - 1, val);


          Fancier code would assess the length of the argv[1] string for exponential notation and number of digits and then print out in a like-wise fashion.



          Tolerate trailing white-space



          OP code's alerts with input like "123 ", but not with " 123". Suggest silence on trailing white-space.



          // add
          while (issapce((unsigned char) *endptr))
          endptr++;


          if (*endptr != '') /* Not necessarily an error... */
          printf("Further characters after number: %sn", endptr);


          Such non-numeric output may be best done on stderr rather than stdout.




          Suggested alternative.



          Useful as a basis for some ideas.



          // Return status for my_strtod().
          // Higher values are more problematic.
          typedef enum
          my_strtod_OK,
          my_strtod_Underflow,
          my_strtod_Overflow,
          my_strtod_ExtraJunk,
          my_strtod_NoConvertableText,
          my_strtod_N
          my_strtod_T;

          // Convert a pointer to a string to double and saves its value in *dest.
          // Return conversion status.
          //
          // `errno` is temporarily cleared by this function. Its value on return:
          // 1) Should strtod(*s, ...) set errno, then that is its value.
          // 2) Otherwise errno is restored to its original value.
          my_strtod_T my_strtod(double *dest, const char *s)
          char *endptr;

          int errno_original = errno;
          errno = 0;
          *dest = strtod(s, &endptr);
          int errno_my_strtod = errno;
          if (errno == 0)
          errno = errno_original;


          if (s == endptr)
          return my_strtod_NoConvertableText;


          while (isspace((unsigned char ) *endptr))
          endptr++;

          if (*endptr)
          return my_strtod_ExtraJunk;


          if (errno_my_strtod == ERANGE && fabs(*dest) == HUGE_VAL)
          return my_strtod_Overflow;


          if (errno_my_strtod == ERANGE && fabs(*dest) <= DBL_MIN)
          return my_strtod_Underflow;


          // Note: at this point, errno may be set
          return my_strtod_OK;



          Selected C specs concerning errno:



          /*
          * errno ... has type int ... the value of which is set to a positive error
          * number by several library functions. C11dr §7.5 2
          *
          * errno ... is never set to zero by any library function. The value of errno
          * may be set to nonzero by a library function call whether or not there is an
          * error, provided the use of errno is not documented in the description of
          * the function ... C11dr §7.5 3
          *
          * Of course, a library function can save the value of errno on entry and
          * then set it to zero, as long as the original value is restored if errno’s
          * value is still zero just before the return. C11dr footnote 202
          */





          share|improve this answer























          • Thank you very much for reviewing my code. Those are all great suggestions. I really appreciate it. Cheers!
            – Domenic
            Jul 19 at 22:20










          • Oh wow, I definitely like your suggested alternative code, where my_strtod returns the status for each of the possibilities. When testing for underflow, though, won't the test fail in some cases since errno may not necessary be set to ERANGE in some implementations, as you indicated earlier?
            – Domenic
            Jul 19 at 23:31











          • @Domenic won't the test fail --> depends on what is "failure" and the coding goal in this case. In practice, conversion to a |value| less than DBL_MIN is simply a tolerable non-failure result - regardless of errno. Portable code could use if (fabs(val) < DBL_MIN) if (val > 0.0) val = DBL_MIN; else if (val < 0.0) val = -DBL_MIN; return my_strtod_Underflow;
            – chux
            Jul 20 at 1:05











          • @Domenic Too be clear, in the cases of fabs(y) <= DBL_MIN, there is too much left to implementation defined behavior for super portable code. This all gets very messy once one considers that strod() is subject to different results based on rounding modes (5 of them). Do the best you can and document your expectations.
            – chux
            Jul 20 at 1:08







          • 1




            I've now asked about this over on SO.
            – Toby Speight
            Jul 20 at 13:11












          up vote
          4
          down vote










          up vote
          4
          down vote









          Good use of errno, but can be improved.



          The below is decent code for detecting overflow, yet would benefit with improvements.



          On overflow, the return value is HUGE_VAL, not necessarily DBL_MAX. HUGE_VAL may be a large finite or an infinity.



          if ((errno == ERANGE && (val == DBL_MAX || val == -DBL_MAX)) || ...) 
          perror("strtod");
          exit(EXIT_FAILURE);

          // replace with
          if ((errno == ERANGE && (val == HUGE_VAL || val == -HUGE_VAL)) || ...) {


          Underflow may also raise ERANGE. Look at the code that attempts to discern that and review the spec.



          if (... || (errno != 0 && val == 0.0)) 
          perror("strtod");
          exit(EXIT_FAILURE);




          If the result underflows ..., the functions return a value whose magnitude is no greater than the smallest normalized positive number in the return type; whether errno acquires the value ERANGE is implementation-defined.




          OP's code incorrectly assumes 0.0 on underflow.



          With so many options with 1) if errno is set and 2) what value is return, portable code nneds to fix a mess.



          Portable code would use the following, yet that gives up useful conversions by some of values in the sub-normal range DBL_TRUE_MIN to DBL_MIN.



          if (fabs(val) < DBL_MIN) 
          if (val > 0.0) val = DBL_MIN;
          else if (val < 0.0) val = -DBL_MIN;
          // val is now, DBL_MIN, -DBL_MIN, 0.0, or -0.0

          // pedantic code would also reset errno = 0 if it changed due to `strtod()`



          A somewhat less portable handing, yet retains sub-normal values - just accept any wee value.



          if (fabs(val) < DBL_MIN) 
          // reset errno = 0 if it changed due to `strtod()`



          No conversion



          Good test for no conversion, yet some strtod() set errno in the no conversion case. To simplify errno processing in the previous block, I'd recommend to do the endptr == str before looking at errno.



          Use exponential notation for output



          "%f" may print all values of magnitude less than 0.0000005 as 0.000000. This is not informative. With large values like DBL_MAX, perhaps hundreds of digits are printed of which past the first 20 or so are rarely of interest.



          Instead:



          #include <float.h>

          printf("strtod() returned %.*gn", DBL_DECIMAL_DIG, DBL_val);
          // or
          printf("strtod() returned %.*en", DBL_DECIMAL_DIG - 1, val);


          Fancier code would assess the length of the argv[1] string for exponential notation and number of digits and then print out in a like-wise fashion.



          Tolerate trailing white-space



          OP code's alerts with input like "123 ", but not with " 123". Suggest silence on trailing white-space.



          // add
          while (issapce((unsigned char) *endptr))
          endptr++;


          if (*endptr != '') /* Not necessarily an error... */
          printf("Further characters after number: %sn", endptr);


          Such non-numeric output may be best done on stderr rather than stdout.




          Suggested alternative.



          Useful as a basis for some ideas.



          // Return status for my_strtod().
          // Higher values are more problematic.
          typedef enum
          my_strtod_OK,
          my_strtod_Underflow,
          my_strtod_Overflow,
          my_strtod_ExtraJunk,
          my_strtod_NoConvertableText,
          my_strtod_N
          my_strtod_T;

          // Convert a pointer to a string to double and saves its value in *dest.
          // Return conversion status.
          //
          // `errno` is temporarily cleared by this function. Its value on return:
          // 1) Should strtod(*s, ...) set errno, then that is its value.
          // 2) Otherwise errno is restored to its original value.
          my_strtod_T my_strtod(double *dest, const char *s)
          char *endptr;

          int errno_original = errno;
          errno = 0;
          *dest = strtod(s, &endptr);
          int errno_my_strtod = errno;
          if (errno == 0)
          errno = errno_original;


          if (s == endptr)
          return my_strtod_NoConvertableText;


          while (isspace((unsigned char ) *endptr))
          endptr++;

          if (*endptr)
          return my_strtod_ExtraJunk;


          if (errno_my_strtod == ERANGE && fabs(*dest) == HUGE_VAL)
          return my_strtod_Overflow;


          if (errno_my_strtod == ERANGE && fabs(*dest) <= DBL_MIN)
          return my_strtod_Underflow;


          // Note: at this point, errno may be set
          return my_strtod_OK;



          Selected C specs concerning errno:



          /*
          * errno ... has type int ... the value of which is set to a positive error
          * number by several library functions. C11dr §7.5 2
          *
          * errno ... is never set to zero by any library function. The value of errno
          * may be set to nonzero by a library function call whether or not there is an
          * error, provided the use of errno is not documented in the description of
          * the function ... C11dr §7.5 3
          *
          * Of course, a library function can save the value of errno on entry and
          * then set it to zero, as long as the original value is restored if errno’s
          * value is still zero just before the return. C11dr footnote 202
          */





          share|improve this answer















          Good use of errno, but can be improved.



          The below is decent code for detecting overflow, yet would benefit with improvements.



          On overflow, the return value is HUGE_VAL, not necessarily DBL_MAX. HUGE_VAL may be a large finite or an infinity.



          if ((errno == ERANGE && (val == DBL_MAX || val == -DBL_MAX)) || ...) 
          perror("strtod");
          exit(EXIT_FAILURE);

          // replace with
          if ((errno == ERANGE && (val == HUGE_VAL || val == -HUGE_VAL)) || ...) {


          Underflow may also raise ERANGE. Look at the code that attempts to discern that and review the spec.



          if (... || (errno != 0 && val == 0.0)) 
          perror("strtod");
          exit(EXIT_FAILURE);




          If the result underflows ..., the functions return a value whose magnitude is no greater than the smallest normalized positive number in the return type; whether errno acquires the value ERANGE is implementation-defined.




          OP's code incorrectly assumes 0.0 on underflow.



          With so many options with 1) if errno is set and 2) what value is return, portable code nneds to fix a mess.



          Portable code would use the following, yet that gives up useful conversions by some of values in the sub-normal range DBL_TRUE_MIN to DBL_MIN.



          if (fabs(val) < DBL_MIN) 
          if (val > 0.0) val = DBL_MIN;
          else if (val < 0.0) val = -DBL_MIN;
          // val is now, DBL_MIN, -DBL_MIN, 0.0, or -0.0

          // pedantic code would also reset errno = 0 if it changed due to `strtod()`



          A somewhat less portable handing, yet retains sub-normal values - just accept any wee value.



          if (fabs(val) < DBL_MIN) 
          // reset errno = 0 if it changed due to `strtod()`



          No conversion



          Good test for no conversion, yet some strtod() set errno in the no conversion case. To simplify errno processing in the previous block, I'd recommend to do the endptr == str before looking at errno.



          Use exponential notation for output



          "%f" may print all values of magnitude less than 0.0000005 as 0.000000. This is not informative. With large values like DBL_MAX, perhaps hundreds of digits are printed of which past the first 20 or so are rarely of interest.



          Instead:



          #include <float.h>

          printf("strtod() returned %.*gn", DBL_DECIMAL_DIG, DBL_val);
          // or
          printf("strtod() returned %.*en", DBL_DECIMAL_DIG - 1, val);


          Fancier code would assess the length of the argv[1] string for exponential notation and number of digits and then print out in a like-wise fashion.



          Tolerate trailing white-space



          OP code's alerts with input like "123 ", but not with " 123". Suggest silence on trailing white-space.



          // add
          while (issapce((unsigned char) *endptr))
          endptr++;


          if (*endptr != '') /* Not necessarily an error... */
          printf("Further characters after number: %sn", endptr);


          Such non-numeric output may be best done on stderr rather than stdout.




          Suggested alternative.



          Useful as a basis for some ideas.



          // Return status for my_strtod().
          // Higher values are more problematic.
          typedef enum
          my_strtod_OK,
          my_strtod_Underflow,
          my_strtod_Overflow,
          my_strtod_ExtraJunk,
          my_strtod_NoConvertableText,
          my_strtod_N
          my_strtod_T;

          // Convert a pointer to a string to double and saves its value in *dest.
          // Return conversion status.
          //
          // `errno` is temporarily cleared by this function. Its value on return:
          // 1) Should strtod(*s, ...) set errno, then that is its value.
          // 2) Otherwise errno is restored to its original value.
          my_strtod_T my_strtod(double *dest, const char *s)
          char *endptr;

          int errno_original = errno;
          errno = 0;
          *dest = strtod(s, &endptr);
          int errno_my_strtod = errno;
          if (errno == 0)
          errno = errno_original;


          if (s == endptr)
          return my_strtod_NoConvertableText;


          while (isspace((unsigned char ) *endptr))
          endptr++;

          if (*endptr)
          return my_strtod_ExtraJunk;


          if (errno_my_strtod == ERANGE && fabs(*dest) == HUGE_VAL)
          return my_strtod_Overflow;


          if (errno_my_strtod == ERANGE && fabs(*dest) <= DBL_MIN)
          return my_strtod_Underflow;


          // Note: at this point, errno may be set
          return my_strtod_OK;



          Selected C specs concerning errno:



          /*
          * errno ... has type int ... the value of which is set to a positive error
          * number by several library functions. C11dr §7.5 2
          *
          * errno ... is never set to zero by any library function. The value of errno
          * may be set to nonzero by a library function call whether or not there is an
          * error, provided the use of errno is not documented in the description of
          * the function ... C11dr §7.5 3
          *
          * Of course, a library function can save the value of errno on entry and
          * then set it to zero, as long as the original value is restored if errno’s
          * value is still zero just before the return. C11dr footnote 202
          */






          share|improve this answer















          share|improve this answer



          share|improve this answer








          edited Jul 19 at 16:06


























          answered Jul 18 at 21:39









          chux

          11.4k11238




          11.4k11238











          • Thank you very much for reviewing my code. Those are all great suggestions. I really appreciate it. Cheers!
            – Domenic
            Jul 19 at 22:20










          • Oh wow, I definitely like your suggested alternative code, where my_strtod returns the status for each of the possibilities. When testing for underflow, though, won't the test fail in some cases since errno may not necessary be set to ERANGE in some implementations, as you indicated earlier?
            – Domenic
            Jul 19 at 23:31











          • @Domenic won't the test fail --> depends on what is "failure" and the coding goal in this case. In practice, conversion to a |value| less than DBL_MIN is simply a tolerable non-failure result - regardless of errno. Portable code could use if (fabs(val) < DBL_MIN) if (val > 0.0) val = DBL_MIN; else if (val < 0.0) val = -DBL_MIN; return my_strtod_Underflow;
            – chux
            Jul 20 at 1:05











          • @Domenic Too be clear, in the cases of fabs(y) <= DBL_MIN, there is too much left to implementation defined behavior for super portable code. This all gets very messy once one considers that strod() is subject to different results based on rounding modes (5 of them). Do the best you can and document your expectations.
            – chux
            Jul 20 at 1:08







          • 1




            I've now asked about this over on SO.
            – Toby Speight
            Jul 20 at 13:11
















          • Thank you very much for reviewing my code. Those are all great suggestions. I really appreciate it. Cheers!
            – Domenic
            Jul 19 at 22:20










          • Oh wow, I definitely like your suggested alternative code, where my_strtod returns the status for each of the possibilities. When testing for underflow, though, won't the test fail in some cases since errno may not necessary be set to ERANGE in some implementations, as you indicated earlier?
            – Domenic
            Jul 19 at 23:31











          • @Domenic won't the test fail --> depends on what is "failure" and the coding goal in this case. In practice, conversion to a |value| less than DBL_MIN is simply a tolerable non-failure result - regardless of errno. Portable code could use if (fabs(val) < DBL_MIN) if (val > 0.0) val = DBL_MIN; else if (val < 0.0) val = -DBL_MIN; return my_strtod_Underflow;
            – chux
            Jul 20 at 1:05











          • @Domenic Too be clear, in the cases of fabs(y) <= DBL_MIN, there is too much left to implementation defined behavior for super portable code. This all gets very messy once one considers that strod() is subject to different results based on rounding modes (5 of them). Do the best you can and document your expectations.
            – chux
            Jul 20 at 1:08







          • 1




            I've now asked about this over on SO.
            – Toby Speight
            Jul 20 at 13:11















          Thank you very much for reviewing my code. Those are all great suggestions. I really appreciate it. Cheers!
          – Domenic
          Jul 19 at 22:20




          Thank you very much for reviewing my code. Those are all great suggestions. I really appreciate it. Cheers!
          – Domenic
          Jul 19 at 22:20












          Oh wow, I definitely like your suggested alternative code, where my_strtod returns the status for each of the possibilities. When testing for underflow, though, won't the test fail in some cases since errno may not necessary be set to ERANGE in some implementations, as you indicated earlier?
          – Domenic
          Jul 19 at 23:31





          Oh wow, I definitely like your suggested alternative code, where my_strtod returns the status for each of the possibilities. When testing for underflow, though, won't the test fail in some cases since errno may not necessary be set to ERANGE in some implementations, as you indicated earlier?
          – Domenic
          Jul 19 at 23:31













          @Domenic won't the test fail --> depends on what is "failure" and the coding goal in this case. In practice, conversion to a |value| less than DBL_MIN is simply a tolerable non-failure result - regardless of errno. Portable code could use if (fabs(val) < DBL_MIN) if (val > 0.0) val = DBL_MIN; else if (val < 0.0) val = -DBL_MIN; return my_strtod_Underflow;
          – chux
          Jul 20 at 1:05





          @Domenic won't the test fail --> depends on what is "failure" and the coding goal in this case. In practice, conversion to a |value| less than DBL_MIN is simply a tolerable non-failure result - regardless of errno. Portable code could use if (fabs(val) < DBL_MIN) if (val > 0.0) val = DBL_MIN; else if (val < 0.0) val = -DBL_MIN; return my_strtod_Underflow;
          – chux
          Jul 20 at 1:05













          @Domenic Too be clear, in the cases of fabs(y) <= DBL_MIN, there is too much left to implementation defined behavior for super portable code. This all gets very messy once one considers that strod() is subject to different results based on rounding modes (5 of them). Do the best you can and document your expectations.
          – chux
          Jul 20 at 1:08





          @Domenic Too be clear, in the cases of fabs(y) <= DBL_MIN, there is too much left to implementation defined behavior for super portable code. This all gets very messy once one considers that strod() is subject to different results based on rounding modes (5 of them). Do the best you can and document your expectations.
          – chux
          Jul 20 at 1:08





          1




          1




          I've now asked about this over on SO.
          – Toby Speight
          Jul 20 at 13:11




          I've now asked about this over on SO.
          – Toby Speight
          Jul 20 at 13:11












           

          draft saved


          draft discarded


























           


          draft saved


          draft discarded














          StackExchange.ready(
          function ()
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f199775%2fconvert-string-to-double-and-check-for-errors%23new-answer', 'question_page');

          );

          Post as a guest













































































          Popular posts from this blog

          Python Lists

          Aion

          JavaScript Array Iteration Methods