Panda3D
pstrtod.cxx
1 // Filename: pstrtod.cxx
2 // Created by: drose (13Jun09)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "pstrtod.h"
16 
17 #include <ctype.h>
18 #include <math.h>
19 #include <limits>
20 
21 #ifdef _WIN32
22 #define strncasecmp _strnicmp
23 #endif
24 
25 ////////////////////////////////////////////////////////////////////
26 // Function: pstrtod
27 // Description: This function re-implements strtod, to avoid the
28 // problems that occur when the LC_NUMERIC locale gets
29 // set to anything other than "C". Regardless of the
30 // user's locale, we need to be able to parse
31 // floating-point numbers internally understanding a "."
32 // as the decimal point.
33 ////////////////////////////////////////////////////////////////////
34 double
35 pstrtod(const char *nptr, char **endptr) {
36  // First, skip whitespace.
37  const char *p = nptr;
38  while (isspace(*p)) {
39  ++p;
40  }
41 
42  // Skip an optional leading sign.
43  char sign = '+';
44  if (*p == '+' || *p == '-') {
45  sign = *p;
46  ++p;
47  }
48 
49  double value = 0.0;
50 
51  if (isalpha(*p)) {
52  // Windows' implementation of strtod doesn't support "inf" or
53  // "nan", so check for those here.
54  if (strncasecmp(p, "inf", 3) == 0) {
55  p += 3;
56  if (strncasecmp(p, "inity", 5) == 0) {
57  p += 5;
58  }
59  value = std::numeric_limits<double>::infinity();
60 
61  } else if (strncasecmp(p, "nan", 3) == 0) {
62  p += 3;
63 
64  if (*p == 's' || *p == 'S') {
65  value = std::numeric_limits<double>::signaling_NaN();
66  ++p;
67  } else {
68  if (*p == 'q' || *p == 'Q') {
69  ++p;
70  }
71  value = std::numeric_limits<double>::quiet_NaN();
72  }
73 
74  // It is optionally possible to include a character sequence
75  // between parentheses after "nan", to be passed to the new
76  // nan() function. Since it isn't supported universally, we
77  // will only accept a pair of empty parentheses.
78  if (strncmp(p, "()", 2) == 0) {
79  p += 2;
80  }
81 
82  } else {
83  // Pass it up to the system implementation of strtod;
84  // perhaps it knows how to deal with this string.
85  return strtod(nptr, endptr);
86  }
87 
88  } else {
89  // Start reading decimal digits to the left of the decimal point.
90  bool found_digits = false;
91  while (isdigit(*p)) {
92  value = (value * 10.0) + (*p - '0');
93  found_digits = true;
94  ++p;
95  }
96 
97  if (*p == '.') {
98  ++p;
99  // Read decimal digits to the right of the decimal point.
100  double multiplicand = 0.1;
101  while (isdigit(*p)) {
102  value += (*p - '0') * multiplicand;
103  ++p;
104  found_digits = true;
105  multiplicand *= 0.1;
106  }
107  }
108 
109  if (!found_digits) {
110  // Not a valid float.
111  if (endptr != NULL) {
112  *endptr = (char *)nptr;
113  }
114  return 0.0;
115  }
116 
117  if (tolower(*p) == 'e') {
118  // There's an exponent.
119  ++p;
120 
121  char esign = '+';
122  if (*p == '+' || *p == '-') {
123  esign = *p;
124  ++p;
125  }
126 
127  // Start reading decimal digits to the left of the decimal point.
128  double evalue = 0.0;
129  while (isdigit(*p)) {
130  evalue = (evalue * 10.0) + (*p - '0');
131  ++p;
132  }
133 
134  if (esign == '-') {
135  value /= pow(10.0, evalue);
136  } else {
137  value *= pow(10.0, evalue);
138  }
139  }
140  }
141 
142  if (sign == '-') {
143  value = -value;
144  }
145 
146  if (endptr != NULL) {
147  *endptr = (char *)p;
148  }
149  return value;
150 }
151 
152 
153 ////////////////////////////////////////////////////////////////////
154 // Function: patof
155 // Description: This function re-implements atof, to avoid the
156 // problems that occur when the LC_NUMERIC locale gets
157 // set to anything other than "C". Regardless of the
158 // user's locale, we need to be able to parse
159 // floating-point numbers internally understanding a "."
160 // as the decimal point.
161 ////////////////////////////////////////////////////////////////////
162 double
163 patof(const char *str) {
164  return pstrtod(str, (char **)NULL);
165 }