Skip to content

Commit bb140f0

Browse files
authored
Merge pull request #91 from pitrou/issue90-decimal-point
Issue #90: accept custom decimal point
2 parents 3bd0c01 + 3881ea6 commit bb140f0

File tree

5 files changed

+196
-16
lines changed

5 files changed

+196
-16
lines changed

include/fast_float/ascii_number.h

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,10 @@ struct parsed_number_string {
8181
// Assuming that you use no more than 19 digits, this will
8282
// parse an ASCII string.
8383
fastfloat_really_inline
84-
parsed_number_string parse_number_string(const char *p, const char *pend, chars_format fmt) noexcept {
84+
parsed_number_string parse_number_string(const char *p, const char *pend, parse_options options) noexcept {
85+
const chars_format fmt = options.format;
86+
const char decimal_point = options.decimal_point;
87+
8588
parsed_number_string answer;
8689
answer.valid = false;
8790
answer.too_many_digits = false;
@@ -91,7 +94,7 @@ parsed_number_string parse_number_string(const char *p, const char *pend, chars_
9194
if (p == pend) {
9295
return answer;
9396
}
94-
if (!is_integer(*p) && (*p != '.')) { // a sign must be followed by an integer or the dot
97+
if (!is_integer(*p) && (*p != decimal_point)) { // a sign must be followed by an integer or the dot
9598
return answer;
9699
}
97100
}
@@ -109,7 +112,7 @@ parsed_number_string parse_number_string(const char *p, const char *pend, chars_
109112
const char *const end_of_integer_part = p;
110113
int64_t digit_count = int64_t(end_of_integer_part - start_digits);
111114
int64_t exponent = 0;
112-
if ((p != pend) && (*p == '.')) {
115+
if ((p != pend) && (*p == decimal_point)) {
113116
++p;
114117
// Fast approach only tested under little endian systems
115118
if ((p + 8 <= pend) && is_made_of_eight_digits_fast(p)) {
@@ -179,7 +182,7 @@ parsed_number_string parse_number_string(const char *p, const char *pend, chars_
179182
// We need to be mindful of the case where we only have zeroes...
180183
// E.g., 0.000000000...000.
181184
const char *start = start_digits;
182-
while ((start != pend) && (*start == '0' || *start == '.')) {
185+
while ((start != pend) && (*start == '0' || *start == decimal_point)) {
183186
if(*start == '0') { digit_count --; }
184187
start++;
185188
}
@@ -196,7 +199,7 @@ parsed_number_string parse_number_string(const char *p, const char *pend, chars_
196199
if (i >= minimal_nineteen_digit_integer) { // We have a big integers
197200
exponent = end_of_integer_part - p + exp_number;
198201
} else { // We have a value with a fractional component.
199-
p++; // skip the '.'
202+
p++; // skip the dot
200203
const char *first_after_period = p;
201204
while((i < minimal_nineteen_digit_integer) && (p != pend) && is_integer(*p)) {
202205
i = i * 10 + uint64_t(*p - '0');
@@ -217,7 +220,9 @@ parsed_number_string parse_number_string(const char *p, const char *pend, chars_
217220
// This function could be optimized. In particular, we could stop after 19 digits
218221
// and try to bail out. Furthermore, we should be able to recover the computed
219222
// exponent from the pass in parse_number_string.
220-
fastfloat_really_inline decimal parse_decimal(const char *p, const char *pend) noexcept {
223+
fastfloat_really_inline decimal parse_decimal(const char *p, const char *pend, parse_options options) noexcept {
224+
const char decimal_point = options.decimal_point;
225+
221226
decimal answer;
222227
answer.num_digits = 0;
223228
answer.decimal_point = 0;
@@ -237,7 +242,7 @@ fastfloat_really_inline decimal parse_decimal(const char *p, const char *pend) n
237242
answer.num_digits++;
238243
++p;
239244
}
240-
if ((p != pend) && (*p == '.')) {
245+
if ((p != pend) && (*p == decimal_point)) {
241246
++p;
242247
const char *first_after_period = p;
243248
// if we have not yet encountered a zero, we have to skip it as well
@@ -276,7 +281,7 @@ fastfloat_really_inline decimal parse_decimal(const char *p, const char *pend) n
276281
// we have at least one non-zero digit.
277282
const char *preverse = p - 1;
278283
int32_t trailing_zeros = 0;
279-
while ((*preverse == '0') || (*preverse == '.')) {
284+
while ((*preverse == '0') || (*preverse == decimal_point)) {
280285
if(*preverse == '0') { trailing_zeros++; };
281286
--preverse;
282287
}

include/fast_float/fast_float.h

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,17 @@ struct from_chars_result {
1717
std::errc ec;
1818
};
1919

20+
struct parse_options {
21+
explicit parse_options(chars_format fmt = chars_format::general,
22+
char dot = '.')
23+
: format(fmt), decimal_point(dot) {}
24+
25+
/** Which number formats are accepted */
26+
chars_format format;
27+
/** The character used as decimal point */
28+
char decimal_point;
29+
};
30+
2031
/**
2132
* This function parses the character sequence [first,last) for a number. It parses floating-point numbers expecting
2233
* a locale-indepent format equivalent to what is used by std::strtod in the default ("C") locale.
@@ -40,6 +51,13 @@ template<typename T>
4051
from_chars_result from_chars(const char *first, const char *last,
4152
T &value, chars_format fmt = chars_format::general) noexcept;
4253

54+
/**
55+
* Like from_chars, but accepts an `options` argument to govern number parsing.
56+
*/
57+
template<typename T>
58+
from_chars_result from_chars_advanced(const char *first, const char *last,
59+
T &value, parse_options options) noexcept;
60+
4361
}
4462
#include "parse_number.h"
45-
#endif // FASTFLOAT_FAST_FLOAT_H
63+
#endif // FASTFLOAT_FAST_FLOAT_H

include/fast_float/parse_number.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,13 @@ fastfloat_really_inline void to_float(bool negative, adjusted_mantissa am, T &va
8585
template<typename T>
8686
from_chars_result from_chars(const char *first, const char *last,
8787
T &value, chars_format fmt /*= chars_format::general*/) noexcept {
88+
return from_chars_advanced(first, last, value, parse_options{fmt});
89+
}
90+
91+
template<typename T>
92+
from_chars_result from_chars_advanced(const char *first, const char *last,
93+
T &value, parse_options options) noexcept {
94+
8895
static_assert (std::is_same<T, double>::value || std::is_same<T, float>::value, "only float and double are supported");
8996

9097

@@ -94,7 +101,7 @@ from_chars_result from_chars(const char *first, const char *last,
94101
answer.ptr = first;
95102
return answer;
96103
}
97-
parsed_number_string pns = parse_number_string(first, last, fmt);
104+
parsed_number_string pns = parse_number_string(first, last, options);
98105
if (!pns.valid) {
99106
return detail::parse_infnan(first, last, value);
100107
}
@@ -116,7 +123,7 @@ from_chars_result from_chars(const char *first, const char *last,
116123
}
117124
// If we called compute_float<binary_format<T>>(pns.exponent, pns.mantissa) and we have an invalid power (am.power2 < 0),
118125
// then we need to go the long way around again. This is very uncommon.
119-
if(am.power2 < 0) { am = parse_long_mantissa<binary_format<T>>(first,last); }
126+
if(am.power2 < 0) { am = parse_long_mantissa<binary_format<T>>(first, last, options); }
120127
detail::to_float(pns.negative, am, value);
121128
return answer;
122129
}

include/fast_float/simple_decimal_conversion.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -351,8 +351,8 @@ adjusted_mantissa compute_float(decimal &d) {
351351
}
352352

353353
template <typename binary>
354-
adjusted_mantissa parse_long_mantissa(const char *first, const char* last) {
355-
decimal d = parse_decimal(first, last);
354+
adjusted_mantissa parse_long_mantissa(const char *first, const char* last, parse_options options) {
355+
decimal d = parse_decimal(first, last, options);
356356
return compute_float<binary>(d);
357357
}
358358

0 commit comments

Comments
 (0)