lmi
[Top][All Lists]
Advanced

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

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


From: Vaclav Slavik
Subject: Re: [lmi] strtod("inf") and MSVC
Date: Tue, 29 May 2012 19:08:19 +0200
User-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:12.0) Gecko/20120428 Thunderbird/12.0.1

Hi,

On 2011-06-27 01:37, Vadim Zeitlin wrote:
>  LMI relies on strtod() accepting string "inf" as a valid input but this
> is, AFAIR[*], C99 extension and probably because of this, MSVC doesn't
> implement it and returns an error in this case, even in its latest version:
> quoting http://msdn.microsoft.com/en-us/library/kxsfc1ab%28v=VS.100%29.aspx
> 
>       strtod expects nptr to point to a string of the following form:
> 
>       [whitespace] [sign] [digits] [.digits] [ {d | D | e | E}[sign]digits]
> 
> This prevents creating new census from working with MSVC right now as it
> fails to parse "inf"s in the sample data files. For now I've applied the
> smallest possible patch to make the program work for me with MSVC:
> 
> --- a/numeric_io_traits.hpp
> +++ b/numeric_io_traits.hpp
> @@ -341,7 +341,25 @@ template<> struct numeric_conversion_traits<double>
>      static int digits(T t) {return floating_point_decimals(t);}
>      static char const* fmt() {return "%#.*f";}
>      static T strtoT(char const* nptr, char** endptr)
> -        {return std::strtod(nptr, endptr);}
> +        {
> +#ifdef LMI_MSC
> +            // 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;
> +                    }
> +                return std::numeric_limits<T>::infinity();
> +                }
> +#endif // LMI_MSC
> +            return std::strtod(nptr, endptr);
> +        }
>  };
> 
>  #if !defined LMI_COMPILER_PROVIDES_STRTOLD
> 
> But I could extend/prettify it a little if you thought it would be useful
> to have. Would it?

Here's an updated and more complete version of the patch:


diff --git a/numeric_io_cast.hpp b/numeric_io_cast.hpp
index f7cbc4a..255c8fb 100644
--- a/numeric_io_cast.hpp
+++ b/numeric_io_cast.hpp
@@ -309,6 +309,12 @@ struct numeric_converter<std::string, From>
             }
         else
             {
+#if defined _MSC_VER
+            // COMPILER !! MSVC formats infinity into a string as "1.#INF", not
+            // "inf" as gcc does and C99/C++11 mandates. Translate it manually.
+            if(0 == std::strcmp(buffer, "1.#INF"))
+                return "inf";
+#endif // defined _MSC_VER
             return numeric_conversion_traits<From>::simplify(To(buffer));
             }
         }
diff --git a/numeric_io_traits.hpp b/numeric_io_traits.hpp
index 499b4bf..4c7636e 100644
--- a/numeric_io_traits.hpp
+++ b/numeric_io_traits.hpp
@@ -80,6 +80,15 @@ template<typename T>
 inline int floating_point_decimals(T t)
 {
     BOOST_STATIC_ASSERT(boost::is_float<T>::value);
+#if defined _MSC_VER
+    // COMPILER !! Not only does Visual C++ write infinity as "1.#INF" rather
+    // than "inf", it respects decimals specification, "shortening" it into
+    // "1." if we return 0 here.
+    if(is_infinite(t))
+        {
+        return 4;
+        }
+#endif // defined _MSC_VER
     // Avoid taking the logarithm of zero or infinity.
     if(0 == t || is_infinite(t))
         {
@@ -331,7 +340,25 @@ template<> struct numeric_conversion_traits<float>
     static int digits(T t) {return floating_point_decimals(t);}
     static char const* fmt() {return "%#.*f";}
     static T strtoT(char const* nptr, char** endptr)
-        {return strtof(nptr, endptr);}
+        {
+#if defined _MSC_VER
+        // 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;
+                }
+            return std::numeric_limits<T>::infinity();
+            }
+#endif // defined _MSC_VER
+        return strtof(nptr, endptr);
+        }
 };
 
 template<> struct numeric_conversion_traits<double>
@@ -341,7 +368,25 @@ template<> struct numeric_conversion_traits<double>
     static int digits(T t) {return floating_point_decimals(t);}
     static char const* fmt() {return "%#.*f";}
     static T strtoT(char const* nptr, char** endptr)
-        {return std::strtod(nptr, endptr);}
+        {
+#if defined _MSC_VER
+        // 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;
+                }
+            return std::numeric_limits<T>::infinity();
+            }
+#endif // defined _MSC_VER
+        return std::strtod(nptr, endptr);
+        }
 };
 
 #if !defined LMI_COMPILER_PROVIDES_STRTOLD



Regards,
Vaclav



reply via email to

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