1// Copyright 2018 - 2023 Ulf Adams
2// Copyright 2023 Matt Borland
3// Distributed under the Boost Software License, Version 1.0.
4// https://www.boost.org/LICENSE_1_0.txt
5
6#ifndef BOOST_CHARCONV_DETAIL_RYU_RYU_GENERIC_128_HPP
7#define BOOST_CHARCONV_DETAIL_RYU_RYU_GENERIC_128_HPP
8
9#include <boost/charconv/detail/ryu/generic_128.hpp>
10#include <boost/charconv/detail/integer_search_trees.hpp>
11#include <boost/charconv/detail/config.hpp>
12#include <boost/charconv/detail/bit_layouts.hpp>
13#include <boost/charconv/to_chars.hpp>
14#include <cinttypes>
15#include <cstdio>
16#include <cstdint>
17
18#ifdef BOOST_CHARCONV_DEBUG
19# include <iostream>
20#endif
21
22namespace boost { namespace charconv { namespace detail { namespace ryu {
23
24static constexpr int32_t fd128_exceptional_exponent = 0x7FFFFFFF;
25static constexpr unsigned_128_type one = 1;
26
27struct floating_decimal_128
28{
29 unsigned_128_type mantissa;
30 int32_t exponent;
31 bool sign;
32};
33
34#ifdef BOOST_CHARCONV_DEBUG
35static char* s(unsigned_128_type v) {
36 int len = num_digits(v);
37 char* b = static_cast<char*>(malloc((len + 1) * sizeof(char)));
38 for (int i = 0; i < len; i++) {
39 const uint32_t c = static_cast<uint32_t>(v % 10);
40 v /= 10;
41 b[len - 1 - i] = static_cast<char>('0' + c);
42 }
43 b[len] = 0;
44 return b;
45}
46#endif
47
48static inline struct floating_decimal_128 generic_binary_to_decimal(
49 const unsigned_128_type bits,
50 const uint32_t mantissaBits, const uint32_t exponentBits, const bool explicitLeadingBit) noexcept
51{
52 #ifdef BOOST_CHARCONV_DEBUG
53 printf("IN=");
54 for (int32_t bit = 127; bit >= 0; --bit)
55 {
56 printf("%u", static_cast<uint32_t>((bits >> bit) & 1));
57 }
58 printf("\n");
59 #endif
60
61 const uint32_t bias = (1u << (exponentBits - 1)) - 1;
62 const bool ieeeSign = ((bits >> (mantissaBits + exponentBits)) & 1) != 0;
63 const unsigned_128_type ieeeMantissa = bits & ((one << mantissaBits) - 1);
64 const uint32_t ieeeExponent = static_cast<uint32_t>((bits >> mantissaBits) & ((one << exponentBits) - 1u));
65
66 if (ieeeExponent == 0 && ieeeMantissa == 0)
67 {
68 struct floating_decimal_128 fd {.mantissa: 0, .exponent: 0, .sign: ieeeSign};
69 return fd;
70 }
71 if (ieeeExponent == ((1u << exponentBits) - 1u))
72 {
73 struct floating_decimal_128 fd;
74 fd.mantissa = explicitLeadingBit ? ieeeMantissa & ((one << (mantissaBits - 1)) - 1) : ieeeMantissa;
75 fd.exponent = fd128_exceptional_exponent;
76 fd.sign = ieeeSign;
77 return fd;
78 }
79
80 int32_t e2;
81 unsigned_128_type m2;
82 // We subtract 2 in all cases so that the bounds computation has 2 additional bits.
83 if (explicitLeadingBit)
84 {
85 // mantissaBits includes the explicit leading bit, so we need to correct for that here.
86 if (ieeeExponent == 0)
87 {
88 e2 = static_cast<int32_t>(1 - bias - mantissaBits + 1 - 2);
89 }
90 else
91 {
92 e2 = static_cast<int32_t>(ieeeExponent - bias - mantissaBits + 1 - 2);
93 }
94 m2 = ieeeMantissa;
95 }
96 else
97 {
98 if (ieeeExponent == 0)
99 {
100 e2 = static_cast<int32_t>(1 - bias - mantissaBits - 2);
101 m2 = ieeeMantissa;
102 } else
103 {
104 e2 = static_cast<int32_t>(ieeeExponent - bias - mantissaBits - 2U);
105 m2 = (one << mantissaBits) | ieeeMantissa;
106 }
107 }
108 const bool even = (m2 & 1) == 0;
109 const bool acceptBounds = even;
110
111 #ifdef BOOST_CHARCONV_DEBUG
112 printf("-> %s %s * 2^%d\n", ieeeSign ? "-" : "+", s(m2), e2 + 2);
113 #endif
114
115 // Step 2: Determine the interval of legal decimal representations.
116 const unsigned_128_type mv = 4 * m2;
117 // Implicit bool -> int conversion. True is 1, false is 0.
118 const uint32_t mmShift =
119 (ieeeMantissa != (explicitLeadingBit ? one << (mantissaBits - 1) : 0))
120 || (ieeeExponent == 0);
121
122 // Step 3: Convert to a decimal power base using 128-bit arithmetic.
123 unsigned_128_type vr;
124 unsigned_128_type vp;
125 unsigned_128_type vm;
126 int32_t e10;
127 bool vmIsTrailingZeros = false;
128 bool vrIsTrailingZeros = false;
129 if (e2 >= 0)
130 {
131 // I tried special-casing q == 0, but there was no effect on performance.
132 // This expression is slightly faster than max(0, log10Pow2(e2) - 1).
133 const uint32_t q = log10Pow2(e: e2) - (e2 > 3);
134 e10 = static_cast<int32_t>(q);
135 const int32_t k = BOOST_CHARCONV_POW5_INV_BITCOUNT + static_cast<int32_t>(pow5bits(e: q)) - 1;
136 const int32_t i = -e2 + static_cast<int32_t>(q) + k;
137 uint64_t pow5[4];
138 generic_computeInvPow5(i: q, result: pow5);
139 vr = mulShift(m: 4 * m2, mul: pow5, j: i);
140 vp = mulShift(m: 4 * m2 + 2, mul: pow5, j: i);
141 vm = mulShift(m: 4 * m2 - 1 - mmShift, mul: pow5, j: i);
142
143 #ifdef BOOST_CHARCONV_DEBUG
144 printf("%s * 2^%d / 10^%d\n", s(mv), e2, q);
145 printf("V+=%s\nV =%s\nV-=%s\n", s(vp), s(vr), s(vm));
146 #endif
147
148 // floor(log_5(2^128)) = 55, this is very conservative
149 if (q <= 55)
150 {
151 // Only one of mp, mv, and mm can be a multiple of 5, if any.
152 if (mv % 5 == 0)
153 {
154 vrIsTrailingZeros = multipleOfPowerOf5(value: mv, p: q - 1);
155 }
156 else if (acceptBounds)
157 {
158 // Same as min(e2 + (~mm & 1), pow5Factor(mm)) >= q
159 // <=> e2 + (~mm & 1) >= q && pow5Factor(mm) >= q
160 // <=> true && pow5Factor(mm) >= q, since e2 >= q.
161 vmIsTrailingZeros = multipleOfPowerOf5(value: mv - 1 - mmShift, p: q);
162 }
163 else
164 {
165 // Same as min(e2 + 1, pow5Factor(mp)) >= q.
166 vp -= multipleOfPowerOf5(value: mv + 2, p: q);
167 }
168 }
169 }
170 else
171 {
172 // This expression is slightly faster than max(0, log10Pow5(-e2) - 1).
173 const uint32_t q = log10Pow5(e: -e2) - static_cast<uint32_t>(-e2 > 1);
174 e10 = static_cast<int32_t>(q) + e2;
175 const int32_t i = -e2 - static_cast<int32_t>(q);
176 const int32_t k = static_cast<int32_t>(pow5bits(e: static_cast<uint32_t>(i))) - BOOST_CHARCONV_POW5_BITCOUNT;
177 const int32_t j = static_cast<int32_t>(q) - k;
178 uint64_t pow5[4];
179 generic_computePow5(i: static_cast<uint32_t>(i), result: pow5);
180 vr = mulShift(m: 4 * m2, mul: pow5, j);
181 vp = mulShift(m: 4 * m2 + 2, mul: pow5, j);
182 vm = mulShift(m: 4 * m2 - 1 - mmShift, mul: pow5, j);
183
184 #ifdef BOOST_CHARCONV_DEBUG
185 printf("%s * 5^%d / 10^%d\n", s(mv), -e2, q);
186 printf("%d %d %d %d\n", q, i, k, j);
187 printf("V+=%s\nV =%s\nV-=%s\n", s(vp), s(vr), s(vm));
188 #endif
189
190 if (q <= 1)
191 {
192 // {vr,vp,vm} is trailing zeros if {mv,mp,mm} has at least q trailing 0 bits.
193 // mv = 4 m2, so it always has at least two trailing 0 bits.
194 vrIsTrailingZeros = true;
195 if (acceptBounds)
196 {
197 // mm = mv - 1 - mmShift, so it has 1 trailing 0 bit iff mmShift == 1.
198 vmIsTrailingZeros = mmShift == 1;
199 }
200 else
201 {
202 // mp = mv + 2, so it always has at least one trailing 0 bit.
203 --vp;
204 }
205 }
206 else if (q < 127)
207 {
208 // We need to compute min(ntz(mv), pow5Factor(mv) - e2) >= q-1
209 // <=> ntz(mv) >= q-1 && pow5Factor(mv) - e2 >= q-1
210 // <=> ntz(mv) >= q-1 (e2 is negative and -e2 >= q)
211 // <=> (mv & ((1 << (q-1)) - 1)) == 0
212 // We also need to make sure that the left shift does not overflow.
213 vrIsTrailingZeros = multipleOfPowerOf2(value: mv, p: q - 1);
214
215 #ifdef BOOST_CHARCONV_DEBUG
216 printf("vr is trailing zeros=%s\n", vrIsTrailingZeros ? "true" : "false");
217 #endif
218 }
219 }
220
221 #ifdef BOOST_CHARCONV_DEBUG
222 printf("e10=%d\n", e10);
223 printf("V+=%s\nV =%s\nV-=%s\n", s(vp), s(vr), s(vm));
224 printf("vm is trailing zeros=%s\n", vmIsTrailingZeros ? "true" : "false");
225 printf("vr is trailing zeros=%s\n", vrIsTrailingZeros ? "true" : "false");
226 #endif
227
228 // Step 4: Find the shortest decimal representation in the interval of legal representations.
229 uint32_t removed = 0;
230 uint8_t lastRemovedDigit = 0;
231 unsigned_128_type output;
232
233 while (vp / 10 > vm / 10)
234 {
235 vmIsTrailingZeros &= vm % 10 == 0;
236 vrIsTrailingZeros &= lastRemovedDigit == 0;
237 lastRemovedDigit = static_cast<uint8_t>(vr % 10);
238 vr /= 10;
239 vp /= 10;
240 vm /= 10;
241 ++removed;
242 }
243
244 #ifdef BOOST_CHARCONV_DEBUG
245 printf("V+=%s\nV =%s\nV-=%s\n", s(vp), s(vr), s(vm));
246 printf("d-10=%s\n", vmIsTrailingZeros ? "true" : "false");
247 #endif
248
249 if (vmIsTrailingZeros)
250 {
251 while (vm % 10 == 0)
252 {
253 vrIsTrailingZeros &= lastRemovedDigit == 0;
254 lastRemovedDigit = static_cast<uint8_t>(vr % 10);
255 vr /= 10;
256 vp /= 10;
257 vm /= 10;
258 ++removed;
259 }
260 }
261
262 #ifdef BOOST_CHARCONV_DEBUG
263 printf("%s %d\n", s(vr), lastRemovedDigit);
264 printf("vr is trailing zeros=%s\n", vrIsTrailingZeros ? "true" : "false");
265 #endif
266
267 if (vrIsTrailingZeros && (lastRemovedDigit == 5) && (vr % 2 == 0))
268 {
269 // Round even if the exact numbers is .....50..0.
270 lastRemovedDigit = 4;
271 }
272 // We need to take vr+1 if vr is outside bounds, or we need to round up.
273 output = vr + static_cast<unsigned_128_type>((vr == vm && (!acceptBounds || !vmIsTrailingZeros)) || (lastRemovedDigit >= 5));
274 const int32_t exp = e10 + static_cast<int32_t>(removed);
275
276 #ifdef BOOST_CHARCONV_DEBUG
277 printf("V+=%s\nV =%s\nV-=%s\n", s(vp), s(vr), s(vm));
278 printf("O=%s\n", s(output));
279 printf("EXP=%d\n", exp);
280 #endif
281
282 return {.mantissa: output, .exponent: exp, .sign: ieeeSign};
283}
284
285static inline int copy_special_str(char* result, const std::ptrdiff_t result_size, const struct floating_decimal_128 fd) noexcept
286{
287 if (fd.sign)
288 {
289 *result = '-';
290 ++result;
291 }
292
293 if (fd.mantissa)
294 {
295 if (fd.sign)
296 {
297 if (fd.mantissa == static_cast<unsigned_128_type>(2305843009213693952) ||
298 fd.mantissa == static_cast<unsigned_128_type>(6917529027641081856) ||
299 fd.mantissa == static_cast<unsigned_128_type>(1) << 110) // 2^110
300 {
301 if (result_size >= 10)
302 {
303 std::memcpy(dest: result, src: "nan(snan)", n: 9);
304 return 10;
305 }
306 else
307 {
308 return -1;
309 }
310 }
311 else
312 {
313 if (result_size >= 9)
314 {
315 std::memcpy(dest: result, src: "nan(ind)", n: 8);
316 return 9;
317 }
318 else
319 {
320 return -1;
321 }
322 }
323 }
324 else
325 {
326 if (fd.mantissa == static_cast<unsigned_128_type>(2305843009213693952) ||
327 fd.mantissa == static_cast<unsigned_128_type>(6917529027641081856) ||
328 fd.mantissa == static_cast<unsigned_128_type>(1) << 110) // 2^110
329 {
330 if (result_size >= 9)
331 {
332 std::memcpy(dest: result, src: "nan(snan)", n: 9);
333 return 9;
334 }
335 else
336 {
337 return -1;
338 }
339 }
340 else
341 {
342 if (result_size >= 3)
343 {
344 std::memcpy(dest: result, src: "nan", n: 3);
345 return 3;
346 }
347 else
348 {
349 return -1;
350 }
351 }
352 }
353 }
354
355 if (result_size >= 3 + static_cast<std::ptrdiff_t>(fd.sign))
356 {
357 memcpy(dest: result, src: "inf", count: 3);
358 return static_cast<int>(fd.sign) + 3;
359 }
360
361 return -1;
362}
363
364static inline int generic_to_chars_fixed(const struct floating_decimal_128 v, char* result, const ptrdiff_t result_size, int precision) noexcept
365{
366 if (v.exponent == fd128_exceptional_exponent)
367 {
368 return copy_special_str(result, result_size, fd: v);
369 }
370
371 // Step 5: Print the decimal representation.
372 if (v.sign)
373 {
374 *result++ = '-';
375 }
376
377 unsigned_128_type output = v.mantissa;
378 const auto r = to_chars_128integer_impl(first: result, last: result + result_size, value: output);
379 if (r.ec != std::errc())
380 {
381 return -static_cast<int>(r.ec);
382 }
383
384 auto current_len = static_cast<int>(r.ptr - result);
385
386 #ifdef BOOST_CHARCONV_DEBUG
387 char* man_print = s(v.mantissa);
388 std::cerr << "Exp: " << v.exponent
389 << "\nMantissa: " << man_print
390 << "\nMan len: " << current_len << std::endl;
391 free(man_print);
392 #endif
393
394 if (v.exponent == 0)
395 {
396 // Option 1: We need to do nothing
397 return current_len + static_cast<int>(v.sign);
398 }
399 else if (v.exponent > 0)
400 {
401 // Option 2: Append 0s to the end of the number until we get the proper significand value
402 // Then we need precison worth of zeros after the decimal point as applicable
403 if (current_len + v.exponent > result_size)
404 {
405 return -static_cast<int>(std::errc::value_too_large);
406 }
407
408 result = r.ptr;
409 memset(s: result, c: '0', n: static_cast<std::size_t>(v.exponent));
410 result += static_cast<std::size_t>(v.exponent);
411 current_len += v.exponent;
412 *result++ = '.';
413 ++precision;
414 }
415 else if ((-v.exponent) < current_len)
416 {
417 // Option 3: Insert a decimal point into the middle of the existing number
418 if (current_len + v.exponent + 1 > result_size)
419 {
420 return -static_cast<int>(std::errc::result_out_of_range);
421 }
422
423 memmove(dest: result + current_len + v.exponent + 1, src: result + current_len + v.exponent, n: static_cast<std::size_t>(-v.exponent));
424 memcpy(dest: result + current_len + v.exponent, src: ".", count: 1U);
425 ++current_len;
426 precision -= current_len + v.exponent;
427 result += current_len + v.exponent + 1;
428 }
429 else
430 {
431 // Option 4: Leading 0s
432 if (-v.exponent + 2 > result_size)
433 {
434 return -static_cast<int>(std::errc::value_too_large);
435 }
436
437 memmove(dest: result - v.exponent - current_len + 2, src: result, n: static_cast<std::size_t>(current_len));
438 memcpy(dest: result, src: "0.", count: 2U);
439 memset(s: result + 2, c: '0', n: static_cast<std::size_t>(0 - v.exponent - current_len));
440 current_len = -v.exponent + 2;
441 precision -= current_len - 2;
442 result += current_len;
443 }
444
445 if (precision > 0)
446 {
447 if (current_len + precision > result_size)
448 {
449 return -static_cast<int>(std::errc::result_out_of_range);
450 }
451
452 memset(s: result, c: '0', n: static_cast<std::size_t>(precision));
453 current_len += precision;
454 }
455
456 return current_len + static_cast<int>(v.sign);
457}
458
459// Converts the given decimal floating point number to a string, writing to result, and returning
460// the number characters written. Does not terminate the buffer with a 0. In the worst case, this
461// function can write up to 53 characters.
462//
463// Maximal char buffer requirement:
464// sign + mantissa digits + decimal dot + 'E' + exponent sign + exponent digits
465// = 1 + 39 + 1 + 1 + 1 + 10 = 53
466static inline int generic_to_chars(const struct floating_decimal_128 v, char* result, const ptrdiff_t result_size,
467 chars_format fmt = chars_format::general, int precision = -1) noexcept
468{
469 if (v.exponent == fd128_exceptional_exponent)
470 {
471 return copy_special_str(result, result_size, fd: v);
472 }
473
474 unsigned_128_type output = v.mantissa;
475 const uint32_t olength = static_cast<uint32_t>(num_digits(x: output));
476
477 #ifdef BOOST_CHARCONV_DEBUG
478 printf("DIGITS=%s\n", s(v.mantissa));
479 printf("OLEN=%u\n", olength);
480 printf("EXP=%u\n", v.exponent + olength);
481 #endif
482
483 // See: https://github.com/cppalliance/charconv/issues/64
484 if (fmt == chars_format::general)
485 {
486 const int64_t exp = v.exponent + static_cast<int64_t>(olength);
487 if (std::abs(i: exp) <= olength)
488 {
489 return generic_to_chars_fixed(v, result, result_size, precision);
490 }
491 }
492
493 // Step 5: Print the decimal representation.
494 size_t index = 0;
495 if (v.sign)
496 {
497 result[index++] = '-';
498 }
499
500 if (index + olength > static_cast<size_t>(result_size))
501 {
502 return -static_cast<int>(std::errc::value_too_large);
503 }
504 else if (olength == 0)
505 {
506 return -2; // Something has gone horribly wrong
507 }
508
509 for (uint32_t i = 0; i < olength - 1; ++i)
510 {
511 const auto c = static_cast<uint32_t>(output % 10);
512 output /= 10;
513 result[index + olength - i] = static_cast<char>('0' + c);
514 }
515 BOOST_CHARCONV_ASSERT(output < 10);
516 result[index] = static_cast<char>('0' + static_cast<uint32_t>(output % 10)); // output should be < 10 by now.
517
518 // Print decimal point if needed.
519 if (olength > 1)
520 {
521 result[index + 1] = '.';
522 index += olength + 1;
523 }
524 else
525 {
526 ++index;
527 }
528
529 // Reset the index to where the required precision should be
530 if (precision != -1)
531 {
532 if (static_cast<size_t>(precision) < index)
533 {
534 if (fmt != chars_format::scientific)
535 {
536 index = static_cast<size_t>(precision) + 1 + static_cast<size_t>(v.sign); // Precision is number of characters not just the decimal portion
537 }
538 else
539 {
540 index = static_cast<size_t>(precision) + 2 + static_cast<size_t>(v.sign); // In scientific format the precision is just the decimal places
541 }
542
543 // Now we need to see if we need to round
544 if (result[index] >= '5' && index < olength + 1 + static_cast<size_t>(v.sign))
545 {
546 bool continue_rounding = false;
547 auto current_index = index;
548 do
549 {
550 --current_index;
551 if (result[current_index] == '9')
552 {
553 continue_rounding = true;
554 result[current_index] = '0';
555 }
556 else
557 {
558 continue_rounding = false;
559 result[current_index] = static_cast<char>(result[current_index] + static_cast<char>(1));
560 }
561 } while (continue_rounding && current_index > 2);
562 }
563
564 // If the last digit is a zero than overwrite that as well, but not in scientific formatting
565 if (fmt != chars_format::scientific)
566 {
567 while (result[index - 1] == '0')
568 {
569 --index;
570 }
571 }
572 else
573 {
574 // In scientific formatting we may need a final 0 to achieve the correct precision
575 if (precision + 1 > static_cast<int>(olength))
576 {
577 result[index - 1] = '0';
578 }
579 }
580 }
581 else if (static_cast<size_t>(precision) > index)
582 {
583 // Use our fallback routine that will capture more of the precision
584 return -1;
585 }
586 }
587
588 // Print the exponent.
589 result[index++] = 'e';
590 int32_t exp = v.exponent + static_cast<int32_t>(olength) - 1;
591 if (exp < 0)
592 {
593 result[index++] = '-';
594 exp = -exp;
595 }
596 else
597 {
598 result[index++] = '+';
599 }
600
601 uint32_t elength = static_cast<uint32_t>(num_digits(x: exp));
602 for (uint32_t i = 0; i < elength; ++i)
603 {
604 // Always print a minimum of 2 characters in the exponent field
605 if (elength == 1)
606 {
607 result[index + elength - 1 - i] = '0';
608 ++index;
609 }
610
611 const uint32_t c = static_cast<uint32_t>(exp % 10);
612 exp /= 10;
613 result[index + elength - 1 - i] = static_cast<char>('0' + c);
614 }
615 if (elength == 0)
616 {
617 result[index++] = '0';
618 result[index++] = '0';
619 }
620
621 index += elength;
622 return static_cast<int>(index);
623}
624
625static inline struct floating_decimal_128 float_to_fd128(float f) noexcept
626{
627 static_assert(sizeof(float) == sizeof(uint32_t), "Float is not 32 bits");
628 uint32_t bits = 0;
629 std::memcpy(dest: &bits, src: &f, n: sizeof(float));
630 return generic_binary_to_decimal(bits, mantissaBits: 23, exponentBits: 8, explicitLeadingBit: false);
631}
632
633static inline struct floating_decimal_128 double_to_fd128(double d) noexcept
634{
635 static_assert(sizeof(double) == sizeof(uint64_t), "Float is not 64 bits");
636 uint64_t bits = 0;
637 std::memcpy(dest: &bits, src: &d, n: sizeof(double));
638 return generic_binary_to_decimal(bits, mantissaBits: 52, exponentBits: 11, explicitLeadingBit: false);
639}
640
641#if BOOST_CHARCONV_LDBL_BITS == 80
642
643static inline struct floating_decimal_128 long_double_to_fd128(long double d) noexcept
644{
645 #ifdef BOOST_CHARCONV_HAS_INT128
646 unsigned_128_type bits = 0;
647 std::memcpy(dest: &bits, src: &d, n: sizeof(long double));
648 #else
649 trivial_uint128 trivial_bits;
650 std::memcpy(&trivial_bits, &d, sizeof(long double));
651 unsigned_128_type bits {trivial_bits};
652 #endif
653
654 #ifdef BOOST_CHARCONV_DEBUG
655 // For some odd reason, this ends up with noise in the top 48 bits. We can
656 // clear out those bits with the following line; this is not required, the
657 // conversion routine should ignore those bits, but the debug output can be
658 // confusing if they aren't 0s.
659 bits &= (one << 80) - 1;
660 #endif
661
662 return generic_binary_to_decimal(bits, mantissaBits: 64, exponentBits: 15, explicitLeadingBit: true);
663}
664
665#else
666
667static inline struct floating_decimal_128 long_double_to_fd128(long double d) noexcept
668{
669 unsigned_128_type bits = 0;
670 std::memcpy(&bits, &d, sizeof(long double));
671
672 #if LDBL_MANT_DIG == 113 // binary128 (e.g. ARM, S390X, PPC64LE)
673 # ifdef __PPC64__
674 return generic_binary_to_decimal(bits, 112, 15, false);
675 # else
676 return generic_binary_to_decimal(bits, 112, 15, true);
677 # endif
678 #elif LDBL_MANT_DIG == 106 // ibm128 (e.g. PowerPC)
679 return generic_binary_to_decimal(bits, 105, 11, true);
680 #endif
681}
682
683#endif
684
685#ifdef BOOST_HAS_FLOAT128
686
687static inline struct floating_decimal_128 float128_to_fd128(__float128 d) noexcept
688{
689 #ifdef BOOST_CHARCONV_HAS_INT128
690 unsigned_128_type bits = 0;
691 std::memcpy(&bits, &d, sizeof(__float128));
692 #else
693 trivial_uint128 trivial_bits;
694 std::memcpy(&trivial_bits, &d, sizeof(__float128));
695 unsigned_128_type bits {trivial_bits};
696 #endif
697
698 return generic_binary_to_decimal(bits, 112, 15, false);
699}
700
701#endif
702
703#ifdef BOOST_CHARCONV_HAS_STDFLOAT128
704
705static inline struct floating_decimal_128 stdfloat128_to_fd128(std::float128_t d) noexcept
706{
707 #ifdef BOOST_CHARCONV_HAS_INT128
708 unsigned_128_type bits = 0;
709 std::memcpy(&bits, &d, sizeof(std::float128_t));
710 #else
711 trivial_uint128 trivial_bits;
712 std::memcpy(&trivial_bits, &d, sizeof(std::float128_t));
713 unsigned_128_type bits {trivial_bits};
714 #endif
715
716 return generic_binary_to_decimal(bits, 112, 15, false);
717}
718
719#endif
720
721}}}} // Namespaces
722
723#endif //BOOST_RYU_GENERIC_128_HPP
724

source code of boost/libs/charconv/include/boost/charconv/detail/ryu/ryu_generic_128.hpp