1 /* melder_atof.cpp
2 *
3 * Copyright (C) 2003-2008,2011,2015-2019 Paul Boersma
4 *
5 * This code is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or (at
8 * your option) any later version.
9 *
10 * This code is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this work. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "melder.h"
20
21 /**
22 Assume that the next thing that follows is a numeric string,
23 and find the end of it.
24 Return null on error.
25 */
26 template <typename T>
findEndOfNumericString(const T * string)27 static const T *findEndOfNumericString (const T *string) noexcept {
28 const T *p = & string [0];
29 /*
30 Leading white space is OK.
31 */
32 while (Melder_isAsciiHorizontalOrVerticalSpace (*p))
33 p ++;
34 /*
35 Next we accept an optional leading plus or minus.
36 */
37 if (*p == '+' || *p == '-')
38 p ++;
39 /*
40 The next character has to be a decimal digit.
41 So we don't allow things like ".5".
42 */
43 if (! Melder_isAsciiDecimalNumber (*p))
44 return nullptr; // string is not numeric
45 p ++;
46 /*
47 Then we accept any number of decimal digits.
48 */
49 while (Melder_isAsciiDecimalNumber (*p))
50 p ++;
51 /*
52 Next we accept an optional decimal point.
53 */
54 if (*p == '.') {
55 p ++;
56 /*
57 We accept any number of (even zero) decimal digits after the decimal point.
58 */
59 while (Melder_isAsciiDecimalNumber (*p))
60 p ++;
61 }
62 // Next we accept an optional exponential E or e.
63 if (*p == 'e' || *p == 'E') {
64 p ++;
65 /*
66 In the exponent we accept an optional leading plus or minus.
67 */
68 if (*p == '+' || *p == '-')
69 p ++;
70 /*
71 The exponent shall contain at least one decimal digit.
72 So we don't allow things like "+2.1E".
73 */
74 if (! Melder_isAsciiDecimalNumber (*p))
75 return nullptr; // string is not numeric
76 p ++; // skip first decimal digit
77 /*
78 Then we accept any number of decimal digits.
79 */
80 while (Melder_isAsciiDecimalNumber (*p))
81 p ++;
82 }
83 /*
84 Next we accept an optional percent sign.
85 */
86 if (*p == '%')
87 p ++;
88 /*
89 We have found the end of the numeric string.
90 */
91 return p;
92 }
93
Melder_isStringNumeric(conststring32 string)94 bool Melder_isStringNumeric (conststring32 string) noexcept {
95 if (! string)
96 return false;
97 const char32 *p = findEndOfNumericString (string);
98 bool weFoundANumber = !! p;
99 if (! weFoundANumber)
100 return false;
101 Melder_skipHorizontalOrVerticalSpace (& p);
102 bool contentFollowsTheNumber = ( *p != U'\0' );
103 if (contentFollowsTheNumber)
104 return false;
105 return true;
106 }
107
Melder_a8tof(conststring8 string)108 double Melder_a8tof (conststring8 string) noexcept {
109 if (! string)
110 return undefined;
111 const char *p = findEndOfNumericString (string);
112 bool weFoundANumber = !! p;
113 if (! weFoundANumber)
114 return undefined;
115 Melder_assert (p - & string [0] > 0);
116 return p [-1] == '%' ? 0.01 * strtod (string, nullptr) : strtod (string, nullptr);
117 }
118
Melder_atof(conststring32 string)119 double Melder_atof (conststring32 string) noexcept {
120 return Melder_a8tof (Melder_peek32to8 (string));
121 }
122
Melder_atoi(conststring32 string)123 int64 Melder_atoi (conststring32 string) noexcept {
124 return strtoll (Melder_peek32to8 (string), nullptr, 10);
125 }
126
127 /* End of file melder_atof.cpp */
128