| 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 | |
| 22 | namespace boost { namespace charconv { namespace detail { namespace ryu { |
| 23 | |
| 24 | static constexpr int32_t fd128_exceptional_exponent = 0x7FFFFFFF; |
| 25 | static constexpr unsigned_128_type one = 1; |
| 26 | |
| 27 | struct floating_decimal_128 |
| 28 | { |
| 29 | unsigned_128_type mantissa; |
| 30 | int32_t exponent; |
| 31 | bool sign; |
| 32 | }; |
| 33 | |
| 34 | #ifdef BOOST_CHARCONV_DEBUG |
| 35 | static 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 | |
| 48 | static 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 | |
| 285 | static 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 | |
| 364 | static 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 |
| 466 | static 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 | |
| 625 | static 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 | |
| 633 | static 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 | |
| 643 | static 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 | |
| 667 | static 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 | |
| 687 | static 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 | |
| 705 | static 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 | |