lmi
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [lmi] strtod("inf") and MSVC


From: Greg Chicares
Subject: Re: [lmi] strtod("inf") and MSVC
Date: Thu, 31 May 2012 17:37:31 +0000
User-agent: Mozilla/5.0 (Windows NT 5.1; rv:10.0.2) Gecko/20120216 Thunderbird/10.0.2

On 2012-05-29 17:08Z, Vaclav Slavik wrote:
[...]
> Here's an updated and more complete version of the patch:

Committed 20120531T1516Z:
  http://svn.savannah.nongnu.org/viewvc?view=rev&root=lmi&revision=5478

I extended it to include Comeau C++, which uses the same flawed C runtime.

However, these tests in 'numeric_io_test.cpp' still don't pass:
        BOOST_TEST_EQUAL(inf_dbl, numeric_io_cast<double>("INF"));
        BOOST_TEST_EQUAL(inf_dbl, numeric_io_cast<double>("infinity"));
        BOOST_TEST_EQUAL(inf_dbl, numeric_io_cast<double>("INFINITY"));
so I'd guess that they don't pass for msvc either. You anticipated the
case-sensitivity issue:

> +        // COMPILER !! MSVC strtod() doesn't support C99 "inf[inity]" nor
> +        // "nan[(...)]" strings nor hexadecimal notation so provide our
> +        // work around for at least the first one of them which we actually
> +        // need. This workaround is, of course, incomplete as it doesn't
> +        // even support "-inf" without mentioning long and non-lower-case
> +        // versions or NaN support.
> +        if(strncmp(nptr, "inf", 3) == 0)
> +            {
> +            if(endptr)
> +                {
> +                *endptr = const_cast<char *>(nptr) + 3;

but I was surprised that converting the string "infinity" failed. I don't
remember the C standard library very well, but in this snippet I think
you used strncmp() in order to match "inf[inity]": { inf | infinity },
but the "3" on the last quoted line doesn't seem to work for the lengthier
forms.

Oh, wait--you explained that:
> +        // need. This workaround is, of course, incomplete as it doesn't
> +        // even support "-inf" without mentioning long and non-lower-case
                                                     ^^^^
> +        // versions or NaN support.
but I thought you meant 'long double'...so I figured I might as well just
fix that...and one thing led to another, and now I have a patch that converts
all infinities for como, and I'd guess for msvc too; but does it look right,
and is there a less awful way to write it that isn't slower?

Index: numeric_io_traits.hpp
===================================================================
--- numeric_io_traits.hpp       (revision 5478)
+++ numeric_io_traits.hpp       (working copy)
@@ -31,7 +31,7 @@
 #include <algorithm>                    // std::max()
 #include <cmath>                        // C99 functions fabsl(), log10l(), 
strtold()
 #include <cstdlib>                      // std::strto*()
-#include <cstring>                      // std::strncmp()
+#include <cstring>                      // std::strlen(), std::strcmp()
 #include <limits>
 #include <stdexcept>
 #include <string>
@@ -343,22 +343,47 @@
     static T strtoT(char const* nptr, char** endptr)
         {
 #if defined LMI_MSC || defined LMI_COMO_WITH_MINGW
-        // COMPILER !! MSVC strtod() doesn't support C99 "inf[inity]" nor
-        // "nan[(...)]" strings nor hexadecimal notation so provide our
-        // work around for at least the first one of them which we actually
-        // need. This workaround is, of course, incomplete as it doesn't
-        // even support "-inf" without mentioning long and non-lower-case
-        // versions or NaN support.
-        if(0 == std::strncmp(nptr, "inf", 3))
+        // COMPILER !! MSVC strtof() doesn't support C99 "inf[inity]".
+        // Work around that, but don't worry about "nan".
+        //
+        // Pointer to which strtoT()'s 'endptr' argument refers.
+        char* rendptr;
+        T z = strtof(nptr, &rendptr);
+        if('\0' == *rendptr)
             {
             if(endptr)
                 {
-                *endptr = const_cast<char *>(nptr) + 3;
+                *endptr = rendptr;
                 }
-            return std::numeric_limits<T>::infinity();
+            return z;
             }
-#endif // defined LMI_MSC || defined LMI_COMO_WITH_MINGW
+        else if
+            (  0 == std::strcmp(nptr, "inf")
+            || 0 == std::strcmp(nptr, "INF")
+            || 0 == std::strcmp(nptr, "infinity")
+            || 0 == std::strcmp(nptr, "INFINITY")
+            || 0 == std::strcmp(nptr,"-inf")
+            || 0 == std::strcmp(nptr,"-INF")
+            || 0 == std::strcmp(nptr,"-infinity")
+            || 0 == std::strcmp(nptr,"-INFINITY")
+            )
+            {
+            if(endptr)
+                {
+                *endptr = const_cast<char *>(nptr) + std::strlen(nptr);
+                }
+            return '-' == *nptr
+                ? -std::numeric_limits<T>::infinity()
+                : std::numeric_limits<T>::infinity()
+                ;
+            }
+        else
+            {
+            throw std::invalid_argument("Numeric conversion failed.");
+            }
+#else  // !(defined LMI_MSC || defined LMI_COMO_WITH_MINGW)
         return strtof(nptr, endptr);
+#endif // !(defined LMI_MSC || defined LMI_COMO_WITH_MINGW)
         }
 };

@@ -371,22 +396,47 @@
     static T strtoT(char const* nptr, char** endptr)
         {
 #if defined LMI_MSC || defined LMI_COMO_WITH_MINGW
-        // COMPILER !! MSVC strtod() doesn't support C99 "inf[inity]" nor
-        // "nan[(...)]" strings nor hexadecimal notation so provide our
-        // work around for at least the first one of them which we actually
-        // need. This workaround is, of course, incomplete as it doesn't
-        // even support "-inf" without mentioning long and non-lower-case
-        // versions or NaN support.
-        if(0 == std::strncmp(nptr, "inf", 3))
+        // COMPILER !! MSVC strtod() doesn't support C99 "inf[inity]".
+        // Work around that, but don't worry about "nan".
+        //
+        // Pointer to which strtoT()'s 'endptr' argument refers.
+        char* rendptr;
+        T z = std::strtod(nptr, &rendptr);
+        if('\0' == *rendptr)
             {
             if(endptr)
                 {
-                *endptr = const_cast<char *>(nptr) + 3;
+                *endptr = rendptr;
                 }
-            return std::numeric_limits<T>::infinity();
+            return z;
             }
-#endif // defined LMI_MSC || defined LMI_COMO_WITH_MINGW
+        else if
+            (  0 == std::strcmp(nptr, "inf")
+            || 0 == std::strcmp(nptr, "INF")
+            || 0 == std::strcmp(nptr, "infinity")
+            || 0 == std::strcmp(nptr, "INFINITY")
+            || 0 == std::strcmp(nptr,"-inf")
+            || 0 == std::strcmp(nptr,"-INF")
+            || 0 == std::strcmp(nptr,"-infinity")
+            || 0 == std::strcmp(nptr,"-INFINITY")
+            )
+            {
+            if(endptr)
+                {
+                *endptr = const_cast<char *>(nptr) + std::strlen(nptr);
+                }
+            return '-' == *nptr
+                ? -std::numeric_limits<T>::infinity()
+                : std::numeric_limits<T>::infinity()
+                ;
+            }
+        else
+            {
+            throw std::invalid_argument("Numeric conversion failed.");
+            }
+#else  // !(defined LMI_MSC || defined LMI_COMO_WITH_MINGW)
         return std::strtod(nptr, endptr);
+#endif // !(defined LMI_MSC || defined LMI_COMO_WITH_MINGW)
         }
 };




reply via email to

[Prev in Thread] Current Thread [Next in Thread]