Better handling of nan/infinity. Previously, these weren't working on Windows in config strings.

This commit is contained in:
rdb 2014-08-20 19:32:11 +00:00
parent cd79de19b2
commit 515f93a0b8
3 changed files with 163 additions and 93 deletions

View File

@ -365,12 +365,25 @@ cpow(int x, int y) {
INLINE bool INLINE bool
cnan(double v) { cnan(double v) {
#ifndef _WIN32 #ifndef _WIN32
return (std::isnan(v) != 0); return std::isnan(v);
#else #else
return (_isnan(v) != 0); return (_isnan(v) != 0);
#endif #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 // Function: make_nan
// Description: // Description:
@ -380,7 +393,7 @@ make_nan(float) {
#ifndef _WIN32 #ifndef _WIN32
return nanf(""); return nanf("");
#else #else
return numeric_limits<float>::quiet_NaN(); return std::numeric_limits<float>::quiet_NaN();
#endif #endif
} }
@ -393,10 +406,27 @@ make_nan(double) {
#ifndef _WIN32 #ifndef _WIN32
return nan(""); return nan("");
#else #else
return numeric_limits<double>::quiet_NaN(); return std::numeric_limits<double>::quiet_NaN();
#endif #endif
} }
////////////////////////////////////////////////////////////////////
// Function: make_inf
// Description:
////////////////////////////////////////////////////////////////////
INLINE float
make_inf(float) {
return std::numeric_limits<float>::infinity();
}
////////////////////////////////////////////////////////////////////
// Function: make_inf
// Description:
////////////////////////////////////////////////////////////////////
INLINE double
make_inf(double) {
return std::numeric_limits<double>::infinity();
}
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: cmod // Function: cmod

View File

@ -68,13 +68,17 @@ INLINE int cpow(int x, int y);
// or infinity. // or infinity.
INLINE bool cnan(double v); 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 float make_nan(float);
INLINE double make_nan(double); INLINE double make_nan(double);
INLINE float make_inf(float);
INLINE double make_inf(double);
INLINE int cmod(int x, int y); INLINE int cmod(int x, int y);
#include "cmath.I" #include "cmath.I"
#endif #endif

View File

@ -17,6 +17,9 @@
#include <ctype.h> #include <ctype.h>
#include <math.h> #include <math.h>
#ifdef _WIN32
#define strncasecmp _strnicmp
#endif
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: pstrtod // Function: pstrtod
@ -45,60 +48,93 @@ pstrtod(const char *nptr, char **endptr) {
double value = 0.0; double value = 0.0;
if (isalpha(*p)) { if (isalpha(*p)) {
// For special cases like "inf" and "nan", pass these up to the // Windows' implementation of strtod doesn't support "inf" or
// system implementation of strtod. // "nan", so check for those here.
return strtod(nptr, endptr); if (strncasecmp(p, "inf", 3) == 0) {
} p += 3;
if (strncasecmp(p, "inity", 5) == 0) {
p += 5;
}
value = std::numeric_limits<double>::infinity();
// Start reading decimal digits to the left of the decimal point. } else if (strncasecmp(p, "nan", 3) == 0) {
bool found_digits = false; p += 3;
while (isdigit(*p)) {
value = (value * 10.0) + (*p - '0');
found_digits = true;
++p;
}
if (*p == '.') { if (*p == 's' || *p == 'S') {
++p; value = std::numeric_limits<double>::signaling_NaN();
// Read decimal digits to the right of the decimal point. ++p;
double multiplicand = 0.1; } else {
while (isdigit(*p)) { if (*p == 'q' || *p == 'Q') {
value += (*p - '0') * multiplicand; ++p;
++p; }
found_digits = true; value = std::numeric_limits<double>::quiet_NaN();
multiplicand *= 0.1; }
}
}
if (!found_digits) { // It is optionally possible to include a character sequence
// Not a valid float. // between parentheses after "nan", to be passed to the new
if (endptr != NULL) { // nan() function. Since it isn't supported universally, we
*endptr = (char *)nptr; // will only accept a pair of empty parentheses.
} if (strncmp(p, "()", 2) == 0) {
return 0.0; p += 2;
} }
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 { } 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);
}
} }
} }