From 515f93a0b8db61be0b8dae9d91c3142f76c50988 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 20 Aug 2014 19:32:11 +0000 Subject: [PATCH] Better handling of nan/infinity. Previously, these weren't working on Windows in config strings. --- dtool/src/dtoolbase/cmath.I | 104 +++++++++++++++-------- dtool/src/dtoolbase/cmath.h | 8 +- dtool/src/dtoolbase/pstrtod.cxx | 144 ++++++++++++++++++++------------ 3 files changed, 163 insertions(+), 93 deletions(-) diff --git a/dtool/src/dtoolbase/cmath.I b/dtool/src/dtoolbase/cmath.I index 8b975bf924..b731b19b77 100644 --- a/dtool/src/dtoolbase/cmath.I +++ b/dtool/src/dtoolbase/cmath.I @@ -16,11 +16,11 @@ // see float.h #define FPU_CONTROLWORD_WRITEMASK 0xFFFFF // if you look at defn of _CW_DEFAULT, all settings fall within 0xFFFFF #define FPU_CONTROLWORD_NEW_SETTING _CW_DEFAULT -#endif +#endif //////////////////////////////////////////////////////////////////// // Function: csqrt -// Description: +// Description: //////////////////////////////////////////////////////////////////// INLINE float csqrt(float v) { @@ -29,7 +29,7 @@ csqrt(float v) { //////////////////////////////////////////////////////////////////// // Function: csin -// Description: +// Description: //////////////////////////////////////////////////////////////////// INLINE float csin(float v) { @@ -38,7 +38,7 @@ csin(float v) { //////////////////////////////////////////////////////////////////// // Function: ccos -// Description: +// Description: //////////////////////////////////////////////////////////////////// INLINE float ccos(float v) { @@ -47,7 +47,7 @@ ccos(float v) { //////////////////////////////////////////////////////////////////// // Function: ctan -// Description: +// Description: //////////////////////////////////////////////////////////////////// INLINE float ctan(float v) { return tanf(v); @@ -55,7 +55,7 @@ INLINE float ctan(float v) { //////////////////////////////////////////////////////////////////// // Function: csincos -// Description: +// Description: //////////////////////////////////////////////////////////////////// INLINE void csincos(float v, float *sin_result, float *cos_result) { @@ -80,7 +80,7 @@ csincos(float v, float *sin_result, float *cos_result) { // Function: csin_over_x // Description: Computes sin(x) / x, well-behaved as x approaches 0. //////////////////////////////////////////////////////////////////// -INLINE float +INLINE float csin_over_x(float v) { if (1.0f + v * v == 1.0f) { return 1.0f; @@ -91,7 +91,7 @@ csin_over_x(float v) { //////////////////////////////////////////////////////////////////// // Function: cabs -// Description: +// Description: //////////////////////////////////////////////////////////////////// INLINE float cabs(float v) { @@ -100,7 +100,7 @@ cabs(float v) { //////////////////////////////////////////////////////////////////// // Function: catan -// Description: +// Description: //////////////////////////////////////////////////////////////////// INLINE float catan(float v) { @@ -109,7 +109,7 @@ catan(float v) { //////////////////////////////////////////////////////////////////// // Function: catan2 -// Description: +// Description: //////////////////////////////////////////////////////////////////// INLINE float catan2(float y, float x) { @@ -118,7 +118,7 @@ catan2(float y, float x) { //////////////////////////////////////////////////////////////////// // Function: casin -// Description: +// Description: //////////////////////////////////////////////////////////////////// INLINE float casin(float v) { @@ -127,7 +127,7 @@ casin(float v) { //////////////////////////////////////////////////////////////////// // Function: cacos -// Description: +// Description: //////////////////////////////////////////////////////////////////// INLINE float cacos(float v) { @@ -147,7 +147,7 @@ cmod(float x, float y) { //////////////////////////////////////////////////////////////////// // Function: cpow -// Description: +// Description: //////////////////////////////////////////////////////////////////// INLINE float cpow(float x, float y) { @@ -157,7 +157,7 @@ cpow(float x, float y) { //////////////////////////////////////////////////////////////////// // Function: cfloor -// Description: +// Description: //////////////////////////////////////////////////////////////////// INLINE double cfloor(double f) { @@ -169,13 +169,13 @@ cfloor(double f) { _controlfp(saved_fpu_control_word,FPU_CONTROLWORD_WRITEMASK); return retval; #else - return floor(f); + return floor(f); #endif } //////////////////////////////////////////////////////////////////// // Function: cceil -// Description: +// Description: //////////////////////////////////////////////////////////////////// INLINE double cceil(double f) { @@ -187,7 +187,7 @@ cceil(double f) { _controlfp(saved_fpu_control_word,FPU_CONTROLWORD_WRITEMASK); return retval; #else - return ceil(f); + return ceil(f); #endif } @@ -202,7 +202,7 @@ cfrac(double f) { //////////////////////////////////////////////////////////////////// // Function: csqrt -// Description: +// Description: //////////////////////////////////////////////////////////////////// INLINE double csqrt(double v) { @@ -211,7 +211,7 @@ csqrt(double v) { //////////////////////////////////////////////////////////////////// // Function: csin -// Description: +// Description: //////////////////////////////////////////////////////////////////// INLINE double csin(double v) { @@ -220,7 +220,7 @@ csin(double v) { //////////////////////////////////////////////////////////////////// // Function: ccos -// Description: +// Description: //////////////////////////////////////////////////////////////////// INLINE double ccos(double v) { @@ -229,7 +229,7 @@ ccos(double v) { //////////////////////////////////////////////////////////////////// // Function: ctan -// Description: +// Description: //////////////////////////////////////////////////////////////////// INLINE double ctan(double v) { @@ -238,7 +238,7 @@ ctan(double v) { //////////////////////////////////////////////////////////////////// // Function: csincos -// Description: +// Description: //////////////////////////////////////////////////////////////////// INLINE void csincos(double v, double *sin_result, double *cos_result) { @@ -262,7 +262,7 @@ csincos(double v, double *sin_result, double *cos_result) { // Function: csin_over_x // Description: Computes sin(x) / x, well-behaved as x approaches 0. //////////////////////////////////////////////////////////////////// -INLINE double +INLINE double csin_over_x(double v) { if (1.0 + v * v == 1.0) { return 1.0; @@ -273,7 +273,7 @@ csin_over_x(double v) { //////////////////////////////////////////////////////////////////// // Function: cabs -// Description: +// Description: //////////////////////////////////////////////////////////////////// INLINE double cabs(double v) { @@ -282,7 +282,7 @@ cabs(double v) { //////////////////////////////////////////////////////////////////// // Function: catan -// Description: +// Description: //////////////////////////////////////////////////////////////////// INLINE double catan(double v) { @@ -291,7 +291,7 @@ catan(double v) { //////////////////////////////////////////////////////////////////// // Function: catan2 -// Description: +// Description: //////////////////////////////////////////////////////////////////// INLINE double catan2(double y, double x) { @@ -300,7 +300,7 @@ catan2(double y, double x) { //////////////////////////////////////////////////////////////////// // Function: casin -// Description: +// Description: //////////////////////////////////////////////////////////////////// INLINE double casin(double v) { @@ -309,7 +309,7 @@ casin(double v) { //////////////////////////////////////////////////////////////////// // Function: cacos -// Description: +// Description: //////////////////////////////////////////////////////////////////// INLINE double cacos(double v) { @@ -329,7 +329,7 @@ cmod(double x, double y) { //////////////////////////////////////////////////////////////////// // Function: cpow -// Description: +// Description: //////////////////////////////////////////////////////////////////// INLINE double cpow(double x, double y) { @@ -338,7 +338,7 @@ cpow(double x, double y) { //////////////////////////////////////////////////////////////////// // Function: cpow -// Description: +// Description: //////////////////////////////////////////////////////////////////// INLINE int cpow(int x, int y) { @@ -360,43 +360,73 @@ cpow(int x, int y) { //////////////////////////////////////////////////////////////////// // Function: cnan -// Description: +// Description: //////////////////////////////////////////////////////////////////// INLINE bool cnan(double v) { #ifndef _WIN32 - return (std::isnan(v) != 0); + return std::isnan(v); #else return (_isnan(v) != 0); #endif } +//////////////////////////////////////////////////////////////////// +// Function: cinf +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool +cinf(double v) { +#ifndef _WIN32 + return std::isinf(v); +#else + return (_isnan(v) == 0 && _finite(v) == 0); +#endif +} + //////////////////////////////////////////////////////////////////// // Function: make_nan -// Description: +// Description: //////////////////////////////////////////////////////////////////// INLINE float make_nan(float) { #ifndef _WIN32 return nanf(""); #else - return numeric_limits::quiet_NaN(); + return std::numeric_limits::quiet_NaN(); #endif } //////////////////////////////////////////////////////////////////// // Function: make_nan -// Description: +// Description: //////////////////////////////////////////////////////////////////// -INLINE double +INLINE double make_nan(double) { #ifndef _WIN32 return nan(""); #else - return numeric_limits::quiet_NaN(); + return std::numeric_limits::quiet_NaN(); #endif } +//////////////////////////////////////////////////////////////////// +// Function: make_inf +// Description: +//////////////////////////////////////////////////////////////////// +INLINE float +make_inf(float) { + return std::numeric_limits::infinity(); +} + +//////////////////////////////////////////////////////////////////// +// Function: make_inf +// Description: +//////////////////////////////////////////////////////////////////// +INLINE double +make_inf(double) { + return std::numeric_limits::infinity(); +} //////////////////////////////////////////////////////////////////// // Function: cmod diff --git a/dtool/src/dtoolbase/cmath.h b/dtool/src/dtoolbase/cmath.h index d4494f81ca..3a815b01a8 100644 --- a/dtool/src/dtoolbase/cmath.h +++ b/dtool/src/dtoolbase/cmath.h @@ -68,13 +68,17 @@ INLINE int cpow(int x, int y); // or infinity. INLINE bool cnan(double v); -// Returns NaN. +// Returns true if the number is infinity. +INLINE bool cinf(double v); + +// Return NaN and infinity, respectively. INLINE float make_nan(float); INLINE double make_nan(double); +INLINE float make_inf(float); +INLINE double make_inf(double); INLINE int cmod(int x, int y); #include "cmath.I" #endif - diff --git a/dtool/src/dtoolbase/pstrtod.cxx b/dtool/src/dtoolbase/pstrtod.cxx index 74e1d750df..2c68ade752 100644 --- a/dtool/src/dtoolbase/pstrtod.cxx +++ b/dtool/src/dtoolbase/pstrtod.cxx @@ -17,6 +17,9 @@ #include #include +#ifdef _WIN32 +#define strncasecmp _strnicmp +#endif //////////////////////////////////////////////////////////////////// // Function: pstrtod @@ -45,67 +48,100 @@ pstrtod(const char *nptr, char **endptr) { double value = 0.0; if (isalpha(*p)) { - // For special cases like "inf" and "nan", pass these up to the - // system implementation of strtod. - return strtod(nptr, endptr); - } + // Windows' implementation of strtod doesn't support "inf" or + // "nan", so check for those here. + if (strncasecmp(p, "inf", 3) == 0) { + p += 3; + if (strncasecmp(p, "inity", 5) == 0) { + p += 5; + } + value = std::numeric_limits::infinity(); + + } else if (strncasecmp(p, "nan", 3) == 0) { + p += 3; + + if (*p == 's' || *p == 'S') { + value = std::numeric_limits::signaling_NaN(); + ++p; + } else { + if (*p == 'q' || *p == 'Q') { + ++p; + } + value = std::numeric_limits::quiet_NaN(); + } + + // It is optionally possible to include a character sequence + // between parentheses after "nan", to be passed to the new + // nan() function. Since it isn't supported universally, we + // will only accept a pair of empty parentheses. + if (strncmp(p, "()", 2) == 0) { + p += 2; + } - // Start reading decimal digits to the left of the decimal point. - bool found_digits = false; - while (isdigit(*p)) { - value = (value * 10.0) + (*p - '0'); - found_digits = true; - ++p; - } - - if (*p == '.') { - ++p; - // Read decimal digits to the right of the decimal point. - double multiplicand = 0.1; - while (isdigit(*p)) { - value += (*p - '0') * multiplicand; - ++p; - found_digits = true; - multiplicand *= 0.1; - } - } - - if (!found_digits) { - // Not a valid float. - if (endptr != NULL) { - *endptr = (char *)nptr; - } - return 0.0; - } - - if (tolower(*p) == 'e') { - // There's an exponent. - ++p; - - char esign = '+'; - if (*p == '+' || *p == '-') { - esign = *p; - ++p; - } - - // Start reading decimal digits to the left of the decimal point. - double evalue = 0.0; - while (isdigit(*p)) { - evalue = (evalue * 10.0) + (*p - '0'); - ++p; - } - - if (esign == '-') { - value /= pow(evalue, 10.0); } else { - value *= pow(evalue, 10.0); + // Pass it up to the system implementation of strtod; + // perhaps it knows how to deal with this string. + return strtod(nptr, endptr); } - } + + } else { + // Start reading decimal digits to the left of the decimal point. + bool found_digits = false; + while (isdigit(*p)) { + value = (value * 10.0) + (*p - '0'); + found_digits = true; + ++p; + } + + if (*p == '.') { + ++p; + // Read decimal digits to the right of the decimal point. + double multiplicand = 0.1; + while (isdigit(*p)) { + value += (*p - '0') * multiplicand; + ++p; + found_digits = true; + multiplicand *= 0.1; + } + } + + if (!found_digits) { + // Not a valid float. + if (endptr != NULL) { + *endptr = (char *)nptr; + } + return 0.0; + } + + if (tolower(*p) == 'e') { + // There's an exponent. + ++p; + + char esign = '+'; + if (*p == '+' || *p == '-') { + esign = *p; + ++p; + } + + // Start reading decimal digits to the left of the decimal point. + double evalue = 0.0; + while (isdigit(*p)) { + evalue = (evalue * 10.0) + (*p - '0'); + ++p; + } + + if (esign == '-') { + value /= pow(evalue, 10.0); + } else { + value *= pow(evalue, 10.0); + } + } + } if (sign == '-') { value = -value; } - + if (endptr != NULL) { *endptr = (char *)p; }