Line data Source code
1 : #ifndef FASTFLOAT_ASCII_NUMBER_H
2 : #define FASTFLOAT_ASCII_NUMBER_H
3 :
4 : #include <cctype>
5 : #include <cstdint>
6 : #include <cstring>
7 : #include <iterator>
8 : #include <limits>
9 : #include <type_traits>
10 :
11 : #include "float_common.h"
12 :
13 : #ifdef FASTFLOAT_SSE2
14 : #include <emmintrin.h>
15 : #endif
16 :
17 : #ifdef FASTFLOAT_NEON
18 : #include <arm_neon.h>
19 : #endif
20 :
21 : namespace fast_float {
22 :
23 : template <typename UC> fastfloat_really_inline constexpr bool has_simd_opt() {
24 : #ifdef FASTFLOAT_HAS_SIMD
25 0 : return std::is_same<UC, char16_t>::value;
26 : #else
27 : return false;
28 : #endif
29 : }
30 :
31 : // Next function can be micro-optimized, but compilers are entirely
32 : // able to optimize it well.
33 : template <typename UC>
34 : fastfloat_really_inline constexpr bool is_integer(UC c) noexcept {
35 105824000 : return !(c > UC('9') || c < UC('0'));
36 : }
37 :
38 : fastfloat_really_inline constexpr uint64_t byteswap(uint64_t val) {
39 : return (val & 0xFF00000000000000) >> 56 | (val & 0x00FF000000000000) >> 40 |
40 : (val & 0x0000FF0000000000) >> 24 | (val & 0x000000FF00000000) >> 8 |
41 : (val & 0x00000000FF000000) << 8 | (val & 0x0000000000FF0000) << 24 |
42 : (val & 0x000000000000FF00) << 40 | (val & 0x00000000000000FF) << 56;
43 : }
44 :
45 : // Read 8 UC into a u64. Truncates UC if not char.
46 : template <typename UC>
47 : fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t
48 : read8_to_u64(const UC *chars) {
49 1543680 : if (cpp20_and_in_constexpr() || !std::is_same<UC, char>::value) {
50 0 : uint64_t val = 0;
51 0 : for (int i = 0; i < 8; ++i) {
52 0 : val |= uint64_t(uint8_t(*chars)) << (i * 8);
53 0 : ++chars;
54 : }
55 0 : return val;
56 : }
57 : uint64_t val;
58 1543680 : ::memcpy(&val, chars, sizeof(uint64_t));
59 : #if FASTFLOAT_IS_BIG_ENDIAN == 1
60 : // Need to read as-if the number was in little-endian order.
61 : val = byteswap(val);
62 : #endif
63 1543680 : return val;
64 : }
65 :
66 : #ifdef FASTFLOAT_SSE2
67 :
68 : fastfloat_really_inline uint64_t simd_read8_to_u64(const __m128i data) {
69 : FASTFLOAT_SIMD_DISABLE_WARNINGS
70 : const __m128i packed = _mm_packus_epi16(data, data);
71 : #ifdef FASTFLOAT_64BIT
72 : return uint64_t(_mm_cvtsi128_si64(packed));
73 : #else
74 : uint64_t value;
75 : // Visual Studio + older versions of GCC don't support _mm_storeu_si64
76 : _mm_storel_epi64(reinterpret_cast<__m128i *>(&value), packed);
77 : return value;
78 : #endif
79 : FASTFLOAT_SIMD_RESTORE_WARNINGS
80 : }
81 :
82 : fastfloat_really_inline uint64_t simd_read8_to_u64(const char16_t *chars) {
83 : FASTFLOAT_SIMD_DISABLE_WARNINGS
84 : return simd_read8_to_u64(
85 : _mm_loadu_si128(reinterpret_cast<const __m128i *>(chars)));
86 : FASTFLOAT_SIMD_RESTORE_WARNINGS
87 : }
88 :
89 : #elif defined(FASTFLOAT_NEON)
90 :
91 : fastfloat_really_inline uint64_t simd_read8_to_u64(const uint16x8_t data) {
92 : FASTFLOAT_SIMD_DISABLE_WARNINGS
93 : uint8x8_t utf8_packed = vmovn_u16(data);
94 : return vget_lane_u64(vreinterpret_u64_u8(utf8_packed), 0);
95 : FASTFLOAT_SIMD_RESTORE_WARNINGS
96 : }
97 :
98 : fastfloat_really_inline uint64_t simd_read8_to_u64(const char16_t *chars) {
99 : FASTFLOAT_SIMD_DISABLE_WARNINGS
100 : return simd_read8_to_u64(
101 : vld1q_u16(reinterpret_cast<const uint16_t *>(chars)));
102 : FASTFLOAT_SIMD_RESTORE_WARNINGS
103 : }
104 :
105 : #endif // FASTFLOAT_SSE2
106 :
107 : // MSVC SFINAE is broken pre-VS2017
108 : #if defined(_MSC_VER) && _MSC_VER <= 1900
109 : template <typename UC>
110 : #else
111 : template <typename UC, FASTFLOAT_ENABLE_IF(!has_simd_opt<UC>()) = 0>
112 : #endif
113 : // dummy for compile
114 0 : uint64_t simd_read8_to_u64(UC const *) {
115 0 : return 0;
116 : }
117 :
118 : // credit @aqrit
119 : fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint32_t
120 : parse_eight_digits_unrolled(uint64_t val) {
121 723914 : const uint64_t mask = 0x000000FF000000FF;
122 723914 : const uint64_t mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32)
123 723914 : const uint64_t mul2 = 0x0000271000000001; // 1 + (10000ULL << 32)
124 723914 : val -= 0x3030303030303030;
125 723914 : val = (val * 10) + (val >> 8); // val = (val * 2561) >> 8;
126 723914 : val = (((val & mask) * mul1) + (((val >> 16) & mask) * mul2)) >> 32;
127 0 : return uint32_t(val);
128 : }
129 :
130 : // Call this if chars are definitely 8 digits.
131 : template <typename UC>
132 : fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint32_t
133 : parse_eight_digits_unrolled(UC const *chars) noexcept {
134 0 : if (cpp20_and_in_constexpr() || !has_simd_opt<UC>()) {
135 0 : return parse_eight_digits_unrolled(read8_to_u64(chars)); // truncation okay
136 : }
137 0 : return parse_eight_digits_unrolled(simd_read8_to_u64(chars));
138 : }
139 :
140 : // credit @aqrit
141 : fastfloat_really_inline constexpr bool
142 : is_made_of_eight_digits_fast(uint64_t val) noexcept {
143 819768 : return !((((val + 0x4646464646464646) | (val - 0x3030303030303030)) &
144 819768 : 0x8080808080808080));
145 : }
146 :
147 : #ifdef FASTFLOAT_HAS_SIMD
148 :
149 : // Call this if chars might not be 8 digits.
150 : // Using this style (instead of is_made_of_eight_digits_fast() then
151 : // parse_eight_digits_unrolled()) ensures we don't load SIMD registers twice.
152 : fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
153 : simd_parse_if_eight_digits_unrolled(const char16_t *chars,
154 : uint64_t &i) noexcept {
155 : if (cpp20_and_in_constexpr()) {
156 : return false;
157 : }
158 : #ifdef FASTFLOAT_SSE2
159 : FASTFLOAT_SIMD_DISABLE_WARNINGS
160 : const __m128i data =
161 : _mm_loadu_si128(reinterpret_cast<const __m128i *>(chars));
162 :
163 : // (x - '0') <= 9
164 : // http://0x80.pl/articles/simd-parsing-int-sequences.html
165 : const __m128i t0 = _mm_add_epi16(data, _mm_set1_epi16(32720));
166 : const __m128i t1 = _mm_cmpgt_epi16(t0, _mm_set1_epi16(-32759));
167 :
168 : if (_mm_movemask_epi8(t1) == 0) {
169 : i = i * 100000000 + parse_eight_digits_unrolled(simd_read8_to_u64(data));
170 : return true;
171 : } else
172 : return false;
173 : FASTFLOAT_SIMD_RESTORE_WARNINGS
174 : #elif defined(FASTFLOAT_NEON)
175 : FASTFLOAT_SIMD_DISABLE_WARNINGS
176 : const uint16x8_t data = vld1q_u16(reinterpret_cast<const uint16_t *>(chars));
177 :
178 : // (x - '0') <= 9
179 : // http://0x80.pl/articles/simd-parsing-int-sequences.html
180 : const uint16x8_t t0 = vsubq_u16(data, vmovq_n_u16('0'));
181 : const uint16x8_t mask = vcltq_u16(t0, vmovq_n_u16('9' - '0' + 1));
182 :
183 : if (vminvq_u16(mask) == 0xFFFF) {
184 : i = i * 100000000 + parse_eight_digits_unrolled(simd_read8_to_u64(data));
185 : return true;
186 : } else
187 : return false;
188 : FASTFLOAT_SIMD_RESTORE_WARNINGS
189 : #else
190 : (void)chars;
191 : (void)i;
192 : return false;
193 : #endif // FASTFLOAT_SSE2
194 : }
195 :
196 : #endif // FASTFLOAT_HAS_SIMD
197 :
198 : // MSVC SFINAE is broken pre-VS2017
199 : #if defined(_MSC_VER) && _MSC_VER <= 1900
200 : template <typename UC>
201 : #else
202 : template <typename UC, FASTFLOAT_ENABLE_IF(!has_simd_opt<UC>()) = 0>
203 : #endif
204 : // dummy for compile
205 : bool simd_parse_if_eight_digits_unrolled(UC const *, uint64_t &) {
206 : return 0;
207 : }
208 :
209 : template <typename UC, FASTFLOAT_ENABLE_IF(!std::is_same<UC, char>::value) = 0>
210 : fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
211 : loop_parse_if_eight_digits(const UC *&p, const UC *const pend, uint64_t &i) {
212 : if (!has_simd_opt<UC>()) {
213 : return;
214 : }
215 : while ((std::distance(p, pend) >= 8) &&
216 : simd_parse_if_eight_digits_unrolled(
217 : p, i)) { // in rare cases, this will overflow, but that's ok
218 : p += 8;
219 : }
220 : }
221 :
222 : fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
223 : loop_parse_if_eight_digits(const char *&p, const char *const pend,
224 : uint64_t &i) {
225 : // optimizes better than parse_if_eight_digits_unrolled() for UC = char.
226 15403700 : while ((std::distance(p, pend) >= 8) &&
227 2459300 : is_made_of_eight_digits_fast(read8_to_u64(p))) {
228 1447830 : i = i * 100000000 +
229 1447830 : parse_eight_digits_unrolled(read8_to_u64(
230 : p)); // in rare cases, this will overflow, but that's ok
231 723914 : p += 8;
232 : }
233 13860000 : }
234 :
235 : enum class parse_error {
236 : no_error,
237 : // [JSON-only] The minus sign must be followed by an integer.
238 : missing_integer_after_sign,
239 : // A sign must be followed by an integer or dot.
240 : missing_integer_or_dot_after_sign,
241 : // [JSON-only] The integer part must not have leading zeros.
242 : leading_zeros_in_integer_part,
243 : // [JSON-only] The integer part must have at least one digit.
244 : no_digits_in_integer_part,
245 : // [JSON-only] If there is a decimal point, there must be digits in the
246 : // fractional part.
247 : no_digits_in_fractional_part,
248 : // The mantissa must have at least one digit.
249 : no_digits_in_mantissa,
250 : // Scientific notation requires an exponential part.
251 : missing_exponential_part,
252 : };
253 :
254 : template <typename UC> struct parsed_number_string_t {
255 : int64_t exponent{0};
256 : uint64_t mantissa{0};
257 : UC const *lastmatch{nullptr};
258 : bool negative{false};
259 : bool valid{false};
260 : bool too_many_digits{false};
261 : // contains the range of the significant digits
262 : span<const UC> integer{}; // non-nullable
263 : span<const UC> fraction{}; // nullable
264 : parse_error error{parse_error::no_error};
265 : };
266 :
267 : using byte_span = span<const char>;
268 : using parsed_number_string = parsed_number_string_t<char>;
269 :
270 : template <typename UC>
271 : fastfloat_really_inline FASTFLOAT_CONSTEXPR20 parsed_number_string_t<UC>
272 : report_parse_error(UC const *p, parse_error error) {
273 662 : parsed_number_string_t<UC> answer;
274 662 : answer.valid = false;
275 662 : answer.lastmatch = p;
276 662 : answer.error = error;
277 662 : return answer;
278 : }
279 :
280 : // Assuming that you use no more than 19 digits, this will
281 : // parse an ASCII string.
282 : template <typename UC>
283 : fastfloat_really_inline FASTFLOAT_CONSTEXPR20 parsed_number_string_t<UC>
284 : parse_number_string(UC const *p, UC const *pend,
285 : parse_options_t<UC> options) noexcept {
286 15234800 : chars_format const fmt = options.format;
287 15234800 : UC const decimal_point = options.decimal_point;
288 :
289 15234800 : parsed_number_string_t<UC> answer;
290 15234800 : answer.valid = false;
291 15234800 : answer.too_many_digits = false;
292 15234800 : answer.negative = (*p == UC('-'));
293 : #ifdef FASTFLOAT_ALLOWS_LEADING_PLUS // disabled by default
294 : if ((*p == UC('-')) || (!(fmt & FASTFLOAT_JSONFMT) && *p == UC('+'))) {
295 : #else
296 15234800 : if (*p == UC('-')) { // C++17 20.19.3.(7.1) explicitly forbids '+' sign here
297 : #endif
298 6542820 : ++p;
299 6542820 : if (p == pend) {
300 241 : return report_parse_error<UC>(
301 241 : p, parse_error::missing_integer_or_dot_after_sign);
302 : }
303 6542580 : if (fmt & FASTFLOAT_JSONFMT) {
304 0 : if (!is_integer(*p)) { // a sign must be followed by an integer
305 0 : return report_parse_error<UC>(p,
306 0 : parse_error::missing_integer_after_sign);
307 : }
308 : } else {
309 13085200 : if (!is_integer(*p) &&
310 13 : (*p !=
311 : decimal_point)) { // a sign must be followed by an integer or the dot
312 5 : return report_parse_error<UC>(
313 5 : p, parse_error::missing_integer_or_dot_after_sign);
314 : }
315 : }
316 : }
317 15234600 : UC const *const start_digits = p;
318 :
319 15234600 : uint64_t i = 0; // an unsigned int avoids signed overflows (which are bad)
320 :
321 120403000 : while ((p != pend) && is_integer(*p)) {
322 : // a multiplication by 10 is cheaper than an arbitrary integer
323 : // multiplication
324 45653700 : i = 10 * i +
325 45653700 : uint64_t(*p -
326 : UC('0')); // might overflow, we will handle the overflow later
327 45653700 : ++p;
328 : }
329 15234600 : UC const *const end_of_integer_part = p;
330 15234600 : int64_t digit_count = int64_t(end_of_integer_part - start_digits);
331 15234600 : answer.integer = span<const UC>(start_digits, size_t(digit_count));
332 15234600 : if (fmt & FASTFLOAT_JSONFMT) {
333 : // at least 1 digit in integer part, without leading zeros
334 0 : if (digit_count == 0) {
335 0 : return report_parse_error<UC>(p, parse_error::no_digits_in_integer_part);
336 : }
337 0 : if ((start_digits[0] == UC('0') && digit_count > 1)) {
338 : return report_parse_error<UC>(start_digits,
339 0 : parse_error::leading_zeros_in_integer_part);
340 : }
341 : }
342 :
343 15234600 : int64_t exponent = 0;
344 15234600 : const bool has_decimal_point = (p != pend) && (*p == decimal_point);
345 15234600 : if (has_decimal_point) {
346 13860000 : ++p;
347 13860000 : UC const *before = p;
348 : // can occur at most twice without overflowing, but let it occur more, since
349 : // for integers with many digits, digit parsing is the primary bottleneck.
350 : loop_parse_if_eight_digits(p, pend, i);
351 :
352 92647400 : while ((p != pend) && is_integer(*p)) {
353 39339600 : uint8_t digit = uint8_t(*p - UC('0'));
354 39339600 : ++p;
355 39339600 : i = i * 10 + digit; // in rare cases, this will overflow, but that's ok
356 : }
357 13860000 : exponent = before - p;
358 13860000 : answer.fraction = span<const UC>(before, size_t(p - before));
359 13860000 : digit_count -= exponent;
360 : }
361 15234600 : if (fmt & FASTFLOAT_JSONFMT) {
362 : // at least 1 digit in fractional part
363 0 : if (has_decimal_point && exponent == 0) {
364 0 : return report_parse_error<UC>(p,
365 0 : parse_error::no_digits_in_fractional_part);
366 : }
367 15234600 : } else if (digit_count ==
368 : 0) { // we must have encountered at least one integer!
369 832 : return report_parse_error<UC>(p, parse_error::no_digits_in_mantissa);
370 : }
371 15234200 : int64_t exp_number = 0; // explicit exponential part
372 15234200 : if (((fmt & chars_format::scientific) && (p != pend) &&
373 108853 : ((UC('e') == *p) || (UC('E') == *p))) ||
374 15126000 : ((fmt & FASTFLOAT_FORTRANFMT) && (p != pend) &&
375 0 : ((UC('+') == *p) || (UC('-') == *p) || (UC('d') == *p) ||
376 0 : (UC('D') == *p)))) {
377 108206 : UC const *location_of_e = p;
378 108206 : if ((UC('e') == *p) || (UC('E') == *p) || (UC('d') == *p) ||
379 0 : (UC('D') == *p)) {
380 108206 : ++p;
381 : }
382 108206 : bool neg_exp = false;
383 108206 : if ((p != pend) && (UC('-') == *p)) {
384 8560 : neg_exp = true;
385 8560 : ++p;
386 99646 : } else if ((p != pend) &&
387 99646 : (UC('+') ==
388 : *p)) { // '+' on exponent is allowed by C++17 20.19.3.(7.1)
389 99443 : ++p;
390 : }
391 216409 : if ((p == pend) || !is_integer(*p)) {
392 4 : if (!(fmt & chars_format::fixed)) {
393 : // The exponential part is invalid for scientific notation, so it must
394 : // be a trailing token for fixed notation. However, fixed notation is
395 : // disabled, so report a scientific notation error.
396 0 : return report_parse_error<UC>(p, parse_error::missing_exponential_part);
397 : }
398 : // Otherwise, we will be ignoring the 'e'.
399 4 : p = location_of_e;
400 : } else {
401 529750 : while ((p != pend) && is_integer(*p)) {
402 210774 : uint8_t digit = uint8_t(*p - UC('0'));
403 210774 : if (exp_number < 0x10000000) {
404 210774 : exp_number = 10 * exp_number + digit;
405 : }
406 210774 : ++p;
407 : }
408 108202 : if (neg_exp) {
409 8560 : exp_number = -exp_number;
410 : }
411 108202 : exponent += exp_number;
412 108206 : }
413 : } else {
414 : // If it scientific and not fixed, we have to bail out.
415 15126000 : if ((fmt & chars_format::scientific) && !(fmt & chars_format::fixed)) {
416 0 : return report_parse_error<UC>(p, parse_error::missing_exponential_part);
417 : }
418 : }
419 15234200 : answer.lastmatch = p;
420 15234200 : answer.valid = true;
421 :
422 : // If we frequently had to deal with long strings of digits,
423 : // we could extend our code by using a 128-bit integer instead
424 : // of a 64-bit integer. However, this is uncommon.
425 : //
426 : // We can deal with up to 19 digits.
427 15234200 : if (digit_count > 19) { // this is uncommon
428 : // It is possible that the integer had an overflow.
429 : // We have to handle the case where we have 0.0000somenumber.
430 : // We need to be mindful of the case where we only have zeroes...
431 : // E.g., 0.000000000...000.
432 63011 : UC const *start = start_digits;
433 67904 : while ((start != pend) && (*start == UC('0') || *start == decimal_point)) {
434 4893 : if (*start == UC('0')) {
435 4651 : digit_count--;
436 : }
437 4893 : start++;
438 : }
439 :
440 63011 : if (digit_count > 19) {
441 62591 : answer.too_many_digits = true;
442 : // Let us start again, this time, avoiding overflows.
443 : // We don't need to check if is_integer, since we use the
444 : // pre-tokenized spans from above.
445 62591 : i = 0;
446 62591 : p = answer.integer.ptr;
447 62591 : UC const *int_end = p + answer.integer.len();
448 62591 : const uint64_t minimal_nineteen_digit_integer{1000000000000000000};
449 471495 : while ((i < minimal_nineteen_digit_integer) && (p != int_end)) {
450 408904 : i = i * 10 + uint64_t(*p - UC('0'));
451 408904 : ++p;
452 : }
453 62591 : if (i >= minimal_nineteen_digit_integer) { // We have a big integers
454 0 : exponent = end_of_integer_part - p + exp_number;
455 : } else { // We have a value with a fractional component.
456 62591 : p = answer.fraction.ptr;
457 62591 : UC const *frac_end = p + answer.fraction.len();
458 842918 : while ((i < minimal_nineteen_digit_integer) && (p != frac_end)) {
459 780327 : i = i * 10 + uint64_t(*p - UC('0'));
460 780327 : ++p;
461 : }
462 62591 : exponent = answer.fraction.ptr - p + exp_number;
463 : }
464 : // We have now corrected both exponent and i, to a truncated value
465 : }
466 : }
467 15234200 : answer.exponent = exponent;
468 15234200 : answer.mantissa = i;
469 15234200 : return answer;
470 : }
471 :
472 : template <typename T, typename UC>
473 : fastfloat_really_inline FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
474 : parse_int_string(UC const *p, UC const *pend, T &value, int base) {
475 : from_chars_result_t<UC> answer;
476 :
477 : UC const *const first = p;
478 :
479 : bool negative = (*p == UC('-'));
480 : if (!std::is_signed<T>::value && negative) {
481 : answer.ec = std::errc::invalid_argument;
482 : answer.ptr = first;
483 : return answer;
484 : }
485 : #ifdef FASTFLOAT_ALLOWS_LEADING_PLUS // disabled by default
486 : if ((*p == UC('-')) || (*p == UC('+'))) {
487 : #else
488 : if (*p == UC('-')) {
489 : #endif
490 : ++p;
491 : }
492 :
493 : UC const *const start_num = p;
494 :
495 : while (p != pend && *p == UC('0')) {
496 : ++p;
497 : }
498 :
499 : const bool has_leading_zeros = p > start_num;
500 :
501 : UC const *const start_digits = p;
502 :
503 : uint64_t i = 0;
504 : if (base == 10) {
505 : loop_parse_if_eight_digits(p, pend, i); // use SIMD if possible
506 : }
507 : while (p != pend) {
508 : uint8_t digit = ch_to_digit(*p);
509 : if (digit >= base) {
510 : break;
511 : }
512 : i = uint64_t(base) * i + digit; // might overflow, check this later
513 : p++;
514 : }
515 :
516 : size_t digit_count = size_t(p - start_digits);
517 :
518 : if (digit_count == 0) {
519 : if (has_leading_zeros) {
520 : value = 0;
521 : answer.ec = std::errc();
522 : answer.ptr = p;
523 : } else {
524 : answer.ec = std::errc::invalid_argument;
525 : answer.ptr = first;
526 : }
527 : return answer;
528 : }
529 :
530 : answer.ptr = p;
531 :
532 : // check u64 overflow
533 : size_t max_digits = max_digits_u64(base);
534 : if (digit_count > max_digits) {
535 : answer.ec = std::errc::result_out_of_range;
536 : return answer;
537 : }
538 : // this check can be eliminated for all other types, but they will all require
539 : // a max_digits(base) equivalent
540 : if (digit_count == max_digits && i < min_safe_u64(base)) {
541 : answer.ec = std::errc::result_out_of_range;
542 : return answer;
543 : }
544 :
545 : // check other types overflow
546 : if (!std::is_same<T, uint64_t>::value) {
547 : if (i > uint64_t(std::numeric_limits<T>::max()) + uint64_t(negative)) {
548 : answer.ec = std::errc::result_out_of_range;
549 : return answer;
550 : }
551 : }
552 :
553 : if (negative) {
554 : #ifdef FASTFLOAT_VISUAL_STUDIO
555 : #pragma warning(push)
556 : #pragma warning(disable : 4146)
557 : #endif
558 : // this weird workaround is required because:
559 : // - converting unsigned to signed when its value is greater than signed max
560 : // is UB pre-C++23.
561 : // - reinterpret_casting (~i + 1) would work, but it is not constexpr
562 : // this is always optimized into a neg instruction (note: T is an integer
563 : // type)
564 : value = T(-std::numeric_limits<T>::max() -
565 : T(i - uint64_t(std::numeric_limits<T>::max())));
566 : #ifdef FASTFLOAT_VISUAL_STUDIO
567 : #pragma warning(pop)
568 : #endif
569 : } else {
570 : value = T(i);
571 : }
572 :
573 : answer.ec = std::errc();
574 : return answer;
575 : }
576 :
577 : } // namespace fast_float
578 :
579 : #endif
|