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