xref: /llvm-project/flang/lib/Decimal/decimal-to-binary.cpp (revision 788be0d9fc6aeca548c90bac5ebe6990dd3c66ec)
164ab3302SCarolineConcatto //===-- lib/Decimal/decimal-to-binary.cpp ---------------------------------===//
264ab3302SCarolineConcatto //
364ab3302SCarolineConcatto // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
464ab3302SCarolineConcatto // See https://llvm.org/LICENSE.txt for license information.
564ab3302SCarolineConcatto // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
664ab3302SCarolineConcatto //
764ab3302SCarolineConcatto //===----------------------------------------------------------------------===//
864ab3302SCarolineConcatto 
964ab3302SCarolineConcatto #include "big-radix-floating-point.h"
1064ab3302SCarolineConcatto #include "flang/Common/bit-population-count.h"
1164ab3302SCarolineConcatto #include "flang/Common/leading-zero-bit-count.h"
1264ab3302SCarolineConcatto #include "flang/Decimal/binary-floating-point.h"
1364ab3302SCarolineConcatto #include "flang/Decimal/decimal.h"
14ed1b24bfSSlava Zakharin #include "flang/Runtime/freestanding-tools.h"
1564ab3302SCarolineConcatto #include <cinttypes>
1664ab3302SCarolineConcatto #include <cstring>
174762c655SPeter Klausler #include <utility>
1864ab3302SCarolineConcatto 
19*788be0d9SBrooks Davis // Some environments, viz. glibc 2.17 and *BSD, allow the macro HUGE
20*788be0d9SBrooks Davis // to leak out of <math.h>.
21*788be0d9SBrooks Davis #undef HUGE
22*788be0d9SBrooks Davis 
2364ab3302SCarolineConcatto namespace Fortran::decimal {
2464ab3302SCarolineConcatto 
2564ab3302SCarolineConcatto template <int PREC, int LOG10RADIX>
ParseNumber(const char * & p,bool & inexact,const char * end)2664ab3302SCarolineConcatto bool BigRadixFloatingPointNumber<PREC, LOG10RADIX>::ParseNumber(
27da25f968SPeter Klausler     const char *&p, bool &inexact, const char *end) {
2864ab3302SCarolineConcatto   SetToZero();
29da25f968SPeter Klausler   if (end && p >= end) {
30da25f968SPeter Klausler     return false;
31da25f968SPeter Klausler   }
32da25f968SPeter Klausler   // Skip leading spaces
33da25f968SPeter Klausler   for (; p != end && *p == ' '; ++p) {
34da25f968SPeter Klausler   }
35da25f968SPeter Klausler   if (p == end) {
36da25f968SPeter Klausler     return false;
3764ab3302SCarolineConcatto   }
3864ab3302SCarolineConcatto   const char *q{p};
3964ab3302SCarolineConcatto   isNegative_ = *q == '-';
4064ab3302SCarolineConcatto   if (*q == '-' || *q == '+') {
4164ab3302SCarolineConcatto     ++q;
4264ab3302SCarolineConcatto   }
4364ab3302SCarolineConcatto   const char *start{q};
44da25f968SPeter Klausler   for (; q != end && *q == '0'; ++q) {
4564ab3302SCarolineConcatto   }
46da25f968SPeter Klausler   const char *firstDigit{q};
47da25f968SPeter Klausler   for (; q != end && *q >= '0' && *q <= '9'; ++q) {
4864ab3302SCarolineConcatto   }
4964ab3302SCarolineConcatto   const char *point{nullptr};
50da25f968SPeter Klausler   if (q != end && *q == '.') {
5164ab3302SCarolineConcatto     point = q;
52da25f968SPeter Klausler     for (++q; q != end && *q >= '0' && *q <= '9'; ++q) {
5364ab3302SCarolineConcatto     }
5464ab3302SCarolineConcatto   }
55da25f968SPeter Klausler   if (q == start || (q == start + 1 && start == point)) {
5664ab3302SCarolineConcatto     return false; // require at least one digit
5764ab3302SCarolineConcatto   }
5864ab3302SCarolineConcatto   // There's a valid number here; set the reference argument to point to
59da25f968SPeter Klausler   // the first character afterward, which might be an exponent part.
6064ab3302SCarolineConcatto   p = q;
6164ab3302SCarolineConcatto   // Strip off trailing zeroes
6264ab3302SCarolineConcatto   if (point) {
6364ab3302SCarolineConcatto     while (q[-1] == '0') {
6464ab3302SCarolineConcatto       --q;
6564ab3302SCarolineConcatto     }
6664ab3302SCarolineConcatto     if (q[-1] == '.') {
6764ab3302SCarolineConcatto       point = nullptr;
6864ab3302SCarolineConcatto       --q;
6964ab3302SCarolineConcatto     }
7064ab3302SCarolineConcatto   }
7164ab3302SCarolineConcatto   if (!point) {
72da25f968SPeter Klausler     while (q > firstDigit && q[-1] == '0') {
7364ab3302SCarolineConcatto       --q;
7464ab3302SCarolineConcatto       ++exponent_;
7564ab3302SCarolineConcatto     }
7664ab3302SCarolineConcatto   }
7764ab3302SCarolineConcatto   // Trim any excess digits
78da25f968SPeter Klausler   const char *limit{firstDigit + maxDigits * log10Radix + (point != nullptr)};
7964ab3302SCarolineConcatto   if (q > limit) {
8064ab3302SCarolineConcatto     inexact = true;
8164ab3302SCarolineConcatto     if (point >= limit) {
8264ab3302SCarolineConcatto       q = point;
8364ab3302SCarolineConcatto       point = nullptr;
8464ab3302SCarolineConcatto     }
8564ab3302SCarolineConcatto     if (!point) {
8664ab3302SCarolineConcatto       exponent_ += q - limit;
8764ab3302SCarolineConcatto     }
8864ab3302SCarolineConcatto     q = limit;
8964ab3302SCarolineConcatto   }
9064ab3302SCarolineConcatto   if (point) {
9164ab3302SCarolineConcatto     exponent_ -= static_cast<int>(q - point - 1);
9264ab3302SCarolineConcatto   }
93da25f968SPeter Klausler   if (q == firstDigit) {
9464ab3302SCarolineConcatto     exponent_ = 0; // all zeros
9564ab3302SCarolineConcatto   }
9664ab3302SCarolineConcatto   // Rack the decimal digits up into big Digits.
97da25f968SPeter Klausler   for (auto times{radix}; q-- > firstDigit;) {
9864ab3302SCarolineConcatto     if (*q != '.') {
9964ab3302SCarolineConcatto       if (times == radix) {
10064ab3302SCarolineConcatto         digit_[digits_++] = *q - '0';
10164ab3302SCarolineConcatto         times = 10;
10264ab3302SCarolineConcatto       } else {
10364ab3302SCarolineConcatto         digit_[digits_ - 1] += times * (*q - '0');
10464ab3302SCarolineConcatto         times *= 10;
10564ab3302SCarolineConcatto       }
10664ab3302SCarolineConcatto     }
10764ab3302SCarolineConcatto   }
10864ab3302SCarolineConcatto   // Look for an optional exponent field.
109da25f968SPeter Klausler   if (p == end) {
110da25f968SPeter Klausler     return true;
111da25f968SPeter Klausler   }
11264ab3302SCarolineConcatto   q = p;
11364ab3302SCarolineConcatto   switch (*q) {
11464ab3302SCarolineConcatto   case 'e':
11564ab3302SCarolineConcatto   case 'E':
11664ab3302SCarolineConcatto   case 'd':
11764ab3302SCarolineConcatto   case 'D':
11864ab3302SCarolineConcatto   case 'q':
11964ab3302SCarolineConcatto   case 'Q': {
120da25f968SPeter Klausler     if (++q == end) {
121da25f968SPeter Klausler       break;
122da25f968SPeter Klausler     }
123da25f968SPeter Klausler     bool negExpo{*q == '-'};
12464ab3302SCarolineConcatto     if (*q == '-' || *q == '+') {
12564ab3302SCarolineConcatto       ++q;
12664ab3302SCarolineConcatto     }
127da25f968SPeter Klausler     if (q != end && *q >= '0' && *q <= '9') {
12864ab3302SCarolineConcatto       int expo{0};
129da25f968SPeter Klausler       for (; q != end && *q == '0'; ++q) {
13064ab3302SCarolineConcatto       }
13164ab3302SCarolineConcatto       const char *expDig{q};
132da25f968SPeter Klausler       for (; q != end && *q >= '0' && *q <= '9'; ++q) {
133da25f968SPeter Klausler         expo = 10 * expo + *q - '0';
13464ab3302SCarolineConcatto       }
13564ab3302SCarolineConcatto       if (q >= expDig + 8) {
13664ab3302SCarolineConcatto         // There's a ridiculous number of nonzero exponent digits.
13764ab3302SCarolineConcatto         // The decimal->binary conversion routine will cope with
13864ab3302SCarolineConcatto         // returning 0 or Inf, but we must ensure that "expo" didn't
13964ab3302SCarolineConcatto         // overflow back around to something legal.
14064ab3302SCarolineConcatto         expo = 10 * Real::decimalRange;
14164ab3302SCarolineConcatto         exponent_ = 0;
14264ab3302SCarolineConcatto       }
143da25f968SPeter Klausler       p = q; // exponent is valid; advance the termination pointer
14464ab3302SCarolineConcatto       if (negExpo) {
14564ab3302SCarolineConcatto         exponent_ -= expo;
14664ab3302SCarolineConcatto       } else {
14764ab3302SCarolineConcatto         exponent_ += expo;
14864ab3302SCarolineConcatto       }
14964ab3302SCarolineConcatto     }
15064ab3302SCarolineConcatto   } break;
1511f879005STim Keith   default:
1521f879005STim Keith     break;
15364ab3302SCarolineConcatto   }
15464ab3302SCarolineConcatto   return true;
15564ab3302SCarolineConcatto }
15664ab3302SCarolineConcatto 
1576fec2c44Speter klausler template <int PREC, int LOG10RADIX>
1586fec2c44Speter klausler void BigRadixFloatingPointNumber<PREC,
LoseLeastSignificantDigit()1596fec2c44Speter klausler     LOG10RADIX>::LoseLeastSignificantDigit() {
1606fec2c44Speter klausler   Digit LSD{digit_[0]};
1616fec2c44Speter klausler   for (int j{0}; j < digits_ - 1; ++j) {
1626fec2c44Speter klausler     digit_[j] = digit_[j + 1];
1636fec2c44Speter klausler   }
1646fec2c44Speter klausler   digit_[digits_ - 1] = 0;
1656fec2c44Speter klausler   bool incr{false};
1666fec2c44Speter klausler   switch (rounding_) {
1676fec2c44Speter klausler   case RoundNearest:
1686fec2c44Speter klausler     incr = LSD > radix / 2 || (LSD == radix / 2 && digit_[0] % 2 != 0);
1696fec2c44Speter klausler     break;
1706fec2c44Speter klausler   case RoundUp:
1716fec2c44Speter klausler     incr = LSD > 0 && !isNegative_;
1726fec2c44Speter klausler     break;
1736fec2c44Speter klausler   case RoundDown:
1746fec2c44Speter klausler     incr = LSD > 0 && isNegative_;
1756fec2c44Speter klausler     break;
1766fec2c44Speter klausler   case RoundToZero:
1776fec2c44Speter klausler     break;
1786fec2c44Speter klausler   case RoundCompatible:
1796fec2c44Speter klausler     incr = LSD >= radix / 2;
1806fec2c44Speter klausler     break;
1816fec2c44Speter klausler   }
1826fec2c44Speter klausler   for (int j{0}; (digit_[j] += incr) == radix; ++j) {
1836fec2c44Speter klausler     digit_[j] = 0;
1846fec2c44Speter klausler   }
1856fec2c44Speter klausler }
1866fec2c44Speter klausler 
18764ab3302SCarolineConcatto // This local utility class represents an unrounded nonnegative
18864ab3302SCarolineConcatto // binary floating-point value with an unbiased (i.e., signed)
18964ab3302SCarolineConcatto // binary exponent, an integer value (not a fraction) with an implied
19064ab3302SCarolineConcatto // binary point to its *right*, and some guard bits for rounding.
19164ab3302SCarolineConcatto template <int PREC> class IntermediateFloat {
19264ab3302SCarolineConcatto public:
19364ab3302SCarolineConcatto   static constexpr int precision{PREC};
19464ab3302SCarolineConcatto   using IntType = common::HostUnsignedIntType<precision>;
19564ab3302SCarolineConcatto   static constexpr IntType topBit{IntType{1} << (precision - 1)};
19664ab3302SCarolineConcatto   static constexpr IntType mask{topBit + (topBit - 1)};
19764ab3302SCarolineConcatto 
IntermediateFloat()198b329da89SSlava Zakharin   RT_API_ATTRS IntermediateFloat() {}
19964ab3302SCarolineConcatto   IntermediateFloat(const IntermediateFloat &) = default;
20064ab3302SCarolineConcatto 
20164ab3302SCarolineConcatto   // Assumes that exponent_ is valid on entry, and may increment it.
20264ab3302SCarolineConcatto   // Returns the number of guard_ bits that have been determined.
SetTo(UINT n)203b329da89SSlava Zakharin   template <typename UINT> RT_API_ATTRS bool SetTo(UINT n) {
20464ab3302SCarolineConcatto     static constexpr int nBits{CHAR_BIT * sizeof n};
20564ab3302SCarolineConcatto     if constexpr (precision >= nBits) {
20664ab3302SCarolineConcatto       value_ = n;
20764ab3302SCarolineConcatto       guard_ = 0;
20864ab3302SCarolineConcatto       return 0;
20964ab3302SCarolineConcatto     } else {
21064ab3302SCarolineConcatto       int shift{common::BitsNeededFor(n) - precision};
21164ab3302SCarolineConcatto       if (shift <= 0) {
21264ab3302SCarolineConcatto         value_ = n;
21364ab3302SCarolineConcatto         guard_ = 0;
21464ab3302SCarolineConcatto         return 0;
21564ab3302SCarolineConcatto       } else {
21664ab3302SCarolineConcatto         value_ = n >> shift;
21764ab3302SCarolineConcatto         exponent_ += shift;
21864ab3302SCarolineConcatto         n <<= nBits - shift;
21964ab3302SCarolineConcatto         guard_ = (n >> (nBits - guardBits)) | ((n << guardBits) != 0);
22064ab3302SCarolineConcatto         return shift;
22164ab3302SCarolineConcatto       }
22264ab3302SCarolineConcatto     }
22364ab3302SCarolineConcatto   }
22464ab3302SCarolineConcatto 
ShiftIn(int bit=0)225b329da89SSlava Zakharin   RT_API_ATTRS void ShiftIn(int bit = 0) { value_ = value_ + value_ + bit; }
IsFull() const226b329da89SSlava Zakharin   RT_API_ATTRS bool IsFull() const { return value_ >= topBit; }
AdjustExponent(int by)227b329da89SSlava Zakharin   RT_API_ATTRS void AdjustExponent(int by) { exponent_ += by; }
SetGuard(int g)228b329da89SSlava Zakharin   RT_API_ATTRS void SetGuard(int g) {
22964ab3302SCarolineConcatto     guard_ |= (static_cast<GuardType>(g & 6) << (guardBits - 3)) | (g & 1);
23064ab3302SCarolineConcatto   }
23164ab3302SCarolineConcatto 
232b329da89SSlava Zakharin   RT_API_ATTRS ConversionToBinaryResult<PREC> ToBinary(
23364ab3302SCarolineConcatto       bool isNegative, FortranRounding) const;
23464ab3302SCarolineConcatto 
23564ab3302SCarolineConcatto private:
23664ab3302SCarolineConcatto   static constexpr int guardBits{3}; // guard, round, sticky
23764ab3302SCarolineConcatto   using GuardType = int;
23864ab3302SCarolineConcatto   static constexpr GuardType oneHalf{GuardType{1} << (guardBits - 1)};
23964ab3302SCarolineConcatto 
24064ab3302SCarolineConcatto   IntType value_{0};
24164ab3302SCarolineConcatto   GuardType guard_{0};
24264ab3302SCarolineConcatto   int exponent_{0};
24364ab3302SCarolineConcatto };
24464ab3302SCarolineConcatto 
2451346037fSPeter Klausler // The standard says that these overflow cases round to "representable"
2461346037fSPeter Klausler // numbers, and some popular compilers interpret that to mean +/-HUGE()
2471346037fSPeter Klausler // rather than +/-Inf.
RoundOverflowToHuge(enum FortranRounding rounding,bool isNegative)248b329da89SSlava Zakharin static inline RT_API_ATTRS constexpr bool RoundOverflowToHuge(
2491346037fSPeter Klausler     enum FortranRounding rounding, bool isNegative) {
2501346037fSPeter Klausler   return rounding == RoundToZero || (!isNegative && rounding == RoundDown) ||
2511346037fSPeter Klausler       (isNegative && rounding == RoundUp);
2521346037fSPeter Klausler }
2531346037fSPeter Klausler 
25464ab3302SCarolineConcatto template <int PREC>
ToBinary(bool isNegative,FortranRounding rounding) const25564ab3302SCarolineConcatto ConversionToBinaryResult<PREC> IntermediateFloat<PREC>::ToBinary(
25664ab3302SCarolineConcatto     bool isNegative, FortranRounding rounding) const {
25764ab3302SCarolineConcatto   using Binary = BinaryFloatingPointNumber<PREC>;
25864ab3302SCarolineConcatto   // Create a fraction with a binary point to the left of the integer
25964ab3302SCarolineConcatto   // value_, and bias the exponent.
26064ab3302SCarolineConcatto   IntType fraction{value_};
26164ab3302SCarolineConcatto   GuardType guard{guard_};
26264ab3302SCarolineConcatto   int expo{exponent_ + Binary::exponentBias + (precision - 1)};
26364ab3302SCarolineConcatto   while (expo < 1 && (fraction > 0 || guard > oneHalf)) {
26464ab3302SCarolineConcatto     guard = (guard & 1) | (guard >> 1) |
26564ab3302SCarolineConcatto         ((static_cast<GuardType>(fraction) & 1) << (guardBits - 1));
26664ab3302SCarolineConcatto     fraction >>= 1;
26764ab3302SCarolineConcatto     ++expo;
26864ab3302SCarolineConcatto   }
26964ab3302SCarolineConcatto   int flags{Exact};
27064ab3302SCarolineConcatto   if (guard != 0) {
27164ab3302SCarolineConcatto     flags |= Inexact;
27264ab3302SCarolineConcatto   }
273befdfae1SPeter Klausler   if (fraction == 0) {
274befdfae1SPeter Klausler     if (guard <= oneHalf) {
275e35cb730SPeter Klausler       if ((!isNegative && rounding == RoundUp) ||
276e35cb730SPeter Klausler           (isNegative && rounding == RoundDown)) {
2771346037fSPeter Klausler         // round to least nonzero value
278f45723cdSPeter Klausler         expo = 0;
279befdfae1SPeter Klausler       } else { // round to zero
280befdfae1SPeter Klausler         if (guard != 0) {
281befdfae1SPeter Klausler           flags |= Underflow;
282befdfae1SPeter Klausler         }
2834762c655SPeter Klausler         Binary zero;
2844762c655SPeter Klausler         if (isNegative) {
2854762c655SPeter Klausler           zero.Negate();
2864762c655SPeter Klausler         }
2874762c655SPeter Klausler         return {
2884762c655SPeter Klausler             std::move(zero), static_cast<enum ConversionResultFlags>(flags)};
28964ab3302SCarolineConcatto       }
290befdfae1SPeter Klausler     }
291e35cb730SPeter Klausler   } else {
29264ab3302SCarolineConcatto     // The value is nonzero; normalize it.
29364ab3302SCarolineConcatto     while (fraction < topBit && expo > 1) {
29464ab3302SCarolineConcatto       --expo;
29564ab3302SCarolineConcatto       fraction = fraction * 2 + (guard >> (guardBits - 2));
296e35cb730SPeter Klausler       guard =
297e35cb730SPeter Klausler           (((guard >> (guardBits - 2)) & 1) << (guardBits - 1)) | (guard & 1);
298e35cb730SPeter Klausler     }
29964ab3302SCarolineConcatto   }
30064ab3302SCarolineConcatto   // Apply rounding
30164ab3302SCarolineConcatto   bool incr{false};
30264ab3302SCarolineConcatto   switch (rounding) {
30364ab3302SCarolineConcatto   case RoundNearest:
30464ab3302SCarolineConcatto     incr = guard > oneHalf || (guard == oneHalf && (fraction & 1));
30564ab3302SCarolineConcatto     break;
3061f879005STim Keith   case RoundUp:
3071f879005STim Keith     incr = guard != 0 && !isNegative;
3081f879005STim Keith     break;
3091f879005STim Keith   case RoundDown:
3101f879005STim Keith     incr = guard != 0 && isNegative;
3111f879005STim Keith     break;
3121f879005STim Keith   case RoundToZero:
3131f879005STim Keith     break;
3141f879005STim Keith   case RoundCompatible:
3151f879005STim Keith     incr = guard >= oneHalf;
3161f879005STim Keith     break;
31764ab3302SCarolineConcatto   }
31864ab3302SCarolineConcatto   if (incr) {
31964ab3302SCarolineConcatto     if (fraction == mask) {
32064ab3302SCarolineConcatto       // rounding causes a carry
32164ab3302SCarolineConcatto       ++expo;
32264ab3302SCarolineConcatto       fraction = topBit;
32364ab3302SCarolineConcatto     } else {
32464ab3302SCarolineConcatto       ++fraction;
32564ab3302SCarolineConcatto     }
32664ab3302SCarolineConcatto   }
32764ab3302SCarolineConcatto   if (expo == 1 && fraction < topBit) {
32864ab3302SCarolineConcatto     expo = 0; // subnormal
329befdfae1SPeter Klausler     flags |= Underflow;
330befdfae1SPeter Klausler   } else if (expo == 0) {
331befdfae1SPeter Klausler     flags |= Underflow;
332befdfae1SPeter Klausler   } else if (expo >= Binary::maxExponent) {
3331346037fSPeter Klausler     if (RoundOverflowToHuge(rounding, isNegative)) {
3341346037fSPeter Klausler       expo = Binary::maxExponent - 1;
3351346037fSPeter Klausler       fraction = mask;
3361346037fSPeter Klausler     } else { // Inf
3371346037fSPeter Klausler       expo = Binary::maxExponent;
33864ab3302SCarolineConcatto       flags |= Overflow;
3390e05ab67SPeter Klausler       if constexpr (Binary::bits == 80) { // x87
3400e05ab67SPeter Klausler         fraction = IntType{1} << 63;
3410e05ab67SPeter Klausler       } else {
34264ab3302SCarolineConcatto         fraction = 0;
34364ab3302SCarolineConcatto       }
3440e05ab67SPeter Klausler     }
3451346037fSPeter Klausler   }
34664ab3302SCarolineConcatto   using Raw = typename Binary::RawType;
34764ab3302SCarolineConcatto   Raw raw = static_cast<Raw>(isNegative) << (Binary::bits - 1);
34864ab3302SCarolineConcatto   raw |= static_cast<Raw>(expo) << Binary::significandBits;
34964ab3302SCarolineConcatto   if constexpr (Binary::isImplicitMSB) {
35064ab3302SCarolineConcatto     fraction &= ~topBit;
35164ab3302SCarolineConcatto   }
35264ab3302SCarolineConcatto   raw |= fraction;
35364ab3302SCarolineConcatto   return {Binary(raw), static_cast<enum ConversionResultFlags>(flags)};
35464ab3302SCarolineConcatto }
35564ab3302SCarolineConcatto 
35664ab3302SCarolineConcatto template <int PREC, int LOG10RADIX>
35764ab3302SCarolineConcatto ConversionToBinaryResult<PREC>
ConvertToBinary()35864ab3302SCarolineConcatto BigRadixFloatingPointNumber<PREC, LOG10RADIX>::ConvertToBinary() {
35964ab3302SCarolineConcatto   // On entry, *this holds a multi-precision integer value in a radix of a
36064ab3302SCarolineConcatto   // large power of ten.  Its radix point is defined to be to the right of its
36164ab3302SCarolineConcatto   // digits, and "exponent_" is the power of ten by which it is to be scaled.
36264ab3302SCarolineConcatto   Normalize();
36364ab3302SCarolineConcatto   if (digits_ == 0) { // zero value
36464ab3302SCarolineConcatto     return {Real{SignBit()}};
36564ab3302SCarolineConcatto   }
36664ab3302SCarolineConcatto   // The value is not zero:  x = D. * 10.**E
36764ab3302SCarolineConcatto   // Shift our perspective on the radix (& decimal) point so that
36864ab3302SCarolineConcatto   // it sits to the *left* of the digits: i.e., x = .D * 10.**E
36964ab3302SCarolineConcatto   exponent_ += digits_ * log10Radix;
37064ab3302SCarolineConcatto   // Sanity checks for ridiculous exponents
37164ab3302SCarolineConcatto   static constexpr int crazy{2 * Real::decimalRange + log10Radix};
372e35cb730SPeter Klausler   if (exponent_ < -crazy) {
373befdfae1SPeter Klausler     enum ConversionResultFlags flags {
374befdfae1SPeter Klausler       static_cast<enum ConversionResultFlags>(Inexact | Underflow)
375befdfae1SPeter Klausler     };
376e35cb730SPeter Klausler     if ((!isNegative_ && rounding_ == RoundUp) ||
377e35cb730SPeter Klausler         (isNegative_ && rounding_ == RoundDown)) {
378befdfae1SPeter Klausler       // return least nonzero value
379befdfae1SPeter Klausler       return {Real{Raw{1} | SignBit()}, flags};
380e35cb730SPeter Klausler     } else { // underflow to +/-0.
381befdfae1SPeter Klausler       return {Real{SignBit()}, flags};
382e35cb730SPeter Klausler     }
3831346037fSPeter Klausler   } else if (exponent_ > crazy) { // overflow to +/-HUGE() or +/-Inf
3841346037fSPeter Klausler     if (RoundOverflowToHuge(rounding_, isNegative_)) {
3851346037fSPeter Klausler       return {Real{HUGE()}};
3861346037fSPeter Klausler     } else {
38764ab3302SCarolineConcatto       return {Real{Infinity()}, Overflow};
38864ab3302SCarolineConcatto     }
3891346037fSPeter Klausler   }
39064ab3302SCarolineConcatto   // Apply any negative decimal exponent by multiplication
39164ab3302SCarolineConcatto   // by a power of two, adjusting the binary exponent to compensate.
39264ab3302SCarolineConcatto   IntermediateFloat<PREC> f;
39364ab3302SCarolineConcatto   while (exponent_ < log10Radix) {
39464ab3302SCarolineConcatto     // x = 0.D * 10.**E * 2.**(f.ex) -> 512 * 0.D * 10.**E * 2.**(f.ex-9)
39564ab3302SCarolineConcatto     f.AdjustExponent(-9);
39664ab3302SCarolineConcatto     digitLimit_ = digits_;
39764ab3302SCarolineConcatto     if (int carry{MultiplyWithoutNormalization<512>()}) {
39864ab3302SCarolineConcatto       // x = c.D * 10.**E * 2.**(f.ex) -> .cD * 10.**(E+16) * 2.**(f.ex)
39964ab3302SCarolineConcatto       PushCarry(carry);
40064ab3302SCarolineConcatto       exponent_ += log10Radix;
40164ab3302SCarolineConcatto     }
40264ab3302SCarolineConcatto   }
40364ab3302SCarolineConcatto   // Apply any positive decimal exponent greater than
40464ab3302SCarolineConcatto   // is needed to treat the topmost digit as an integer
40564ab3302SCarolineConcatto   // part by multiplying by 10 or 10000 repeatedly.
40664ab3302SCarolineConcatto   while (exponent_ > log10Radix) {
40764ab3302SCarolineConcatto     digitLimit_ = digits_;
40864ab3302SCarolineConcatto     int carry;
40964ab3302SCarolineConcatto     if (exponent_ >= log10Radix + 4) {
41064ab3302SCarolineConcatto       // x = 0.D * 10.**E * 2.**(f.ex) -> 625 * .D * 10.**(E-4) * 2.**(f.ex+4)
41164ab3302SCarolineConcatto       exponent_ -= 4;
41264ab3302SCarolineConcatto       carry = MultiplyWithoutNormalization<(5 * 5 * 5 * 5)>();
41364ab3302SCarolineConcatto       f.AdjustExponent(4);
41464ab3302SCarolineConcatto     } else {
41564ab3302SCarolineConcatto       // x = 0.D * 10.**E * 2.**(f.ex) -> 5 * .D * 10.**(E-1) * 2.**(f.ex+1)
41664ab3302SCarolineConcatto       --exponent_;
41764ab3302SCarolineConcatto       carry = MultiplyWithoutNormalization<5>();
41864ab3302SCarolineConcatto       f.AdjustExponent(1);
41964ab3302SCarolineConcatto     }
42064ab3302SCarolineConcatto     if (carry != 0) {
42164ab3302SCarolineConcatto       // x = c.D * 10.**E * 2.**(f.ex) -> .cD * 10.**(E+16) * 2.**(f.ex)
42264ab3302SCarolineConcatto       PushCarry(carry);
42364ab3302SCarolineConcatto       exponent_ += log10Radix;
42464ab3302SCarolineConcatto     }
42564ab3302SCarolineConcatto   }
42664ab3302SCarolineConcatto   // So exponent_ is now log10Radix, meaning that the
42764ab3302SCarolineConcatto   // MSD can be taken as an integer part and transferred
42864ab3302SCarolineConcatto   // to the binary result.
42964ab3302SCarolineConcatto   // x = .jD * 10.**16 * 2.**(f.ex) -> .D * j * 2.**(f.ex)
43064ab3302SCarolineConcatto   int guardShift{f.SetTo(digit_[--digits_])};
43164ab3302SCarolineConcatto   // Transfer additional bits until the result is normal.
43264ab3302SCarolineConcatto   digitLimit_ = digits_;
43364ab3302SCarolineConcatto   while (!f.IsFull()) {
43464ab3302SCarolineConcatto     // x = ((b.D)/2) * j * 2.**(f.ex) -> .D * (2j + b) * 2.**(f.ex-1)
43564ab3302SCarolineConcatto     f.AdjustExponent(-1);
43664ab3302SCarolineConcatto     std::uint32_t carry = MultiplyWithoutNormalization<2>();
43764ab3302SCarolineConcatto     f.ShiftIn(carry);
43864ab3302SCarolineConcatto   }
43964ab3302SCarolineConcatto   // Get the next few bits for rounding.  Allow for some guard bits
44064ab3302SCarolineConcatto   // that may have already been set in f.SetTo() above.
44164ab3302SCarolineConcatto   int guard{0};
44264ab3302SCarolineConcatto   if (guardShift == 0) {
44364ab3302SCarolineConcatto     guard = MultiplyWithoutNormalization<4>();
44464ab3302SCarolineConcatto   } else if (guardShift == 1) {
44564ab3302SCarolineConcatto     guard = MultiplyWithoutNormalization<2>();
44664ab3302SCarolineConcatto   }
44764ab3302SCarolineConcatto   guard = guard + guard + !IsZero();
44864ab3302SCarolineConcatto   f.SetGuard(guard);
44964ab3302SCarolineConcatto   return f.ToBinary(isNegative_, rounding_);
45064ab3302SCarolineConcatto }
45164ab3302SCarolineConcatto 
45264ab3302SCarolineConcatto template <int PREC, int LOG10RADIX>
45364ab3302SCarolineConcatto ConversionToBinaryResult<PREC>
ConvertToBinary(const char * & p,const char * limit)454da25f968SPeter Klausler BigRadixFloatingPointNumber<PREC, LOG10RADIX>::ConvertToBinary(
455da25f968SPeter Klausler     const char *&p, const char *limit) {
45664ab3302SCarolineConcatto   bool inexact{false};
457da25f968SPeter Klausler   if (ParseNumber(p, inexact, limit)) {
45864ab3302SCarolineConcatto     auto result{ConvertToBinary()};
45964ab3302SCarolineConcatto     if (inexact) {
46064ab3302SCarolineConcatto       result.flags =
46164ab3302SCarolineConcatto           static_cast<enum ConversionResultFlags>(result.flags | Inexact);
46264ab3302SCarolineConcatto     }
46364ab3302SCarolineConcatto     return result;
46464ab3302SCarolineConcatto   } else {
46564ab3302SCarolineConcatto     // Could not parse a decimal floating-point number.  p has been
466e593940bSPeter Klausler     // advanced over any leading spaces.  Most Fortran compilers set
467e593940bSPeter Klausler     // the sign bit for -NaN.
46864ab3302SCarolineConcatto     const char *q{p};
469f1dbf8e4SPeter Klausler     if (!limit || q < limit) {
47064ab3302SCarolineConcatto       isNegative_ = *q == '-';
471f1dbf8e4SPeter Klausler       if (isNegative_ || *q == '+') {
47264ab3302SCarolineConcatto         ++q;
47364ab3302SCarolineConcatto       }
474f1dbf8e4SPeter Klausler     }
475ed1b24bfSSlava Zakharin     if ((!limit || limit >= q + 3) && runtime::toupper(q[0]) == 'N' &&
476ed1b24bfSSlava Zakharin         runtime::toupper(q[1]) == 'A' && runtime::toupper(q[2]) == 'N') {
477e593940bSPeter Klausler       // NaN
478e593940bSPeter Klausler       p = q + 3;
479e593940bSPeter Klausler       bool isQuiet{true};
480e593940bSPeter Klausler       if ((!limit || p < limit) && *p == '(') {
481e593940bSPeter Klausler         int depth{1};
482e593940bSPeter Klausler         do {
483e593940bSPeter Klausler           ++p;
484e593940bSPeter Klausler           if (limit && p >= limit) {
485e593940bSPeter Klausler             // Invalid input
486e593940bSPeter Klausler             return {Real{NaN(false)}, Invalid};
487e593940bSPeter Klausler           } else if (*p == '(') {
488e593940bSPeter Klausler             ++depth;
489e593940bSPeter Klausler           } else if (*p == ')') {
490e593940bSPeter Klausler             --depth;
491e593940bSPeter Klausler           } else if (*p != ' ') {
492e593940bSPeter Klausler             // Implementation dependent, but other compilers
493e593940bSPeter Klausler             // all return quiet NaNs.
494e593940bSPeter Klausler           }
495e593940bSPeter Klausler         } while (depth > 0);
496e593940bSPeter Klausler         ++p;
497e593940bSPeter Klausler       }
498e593940bSPeter Klausler       return {Real{NaN(isQuiet)}};
499e593940bSPeter Klausler     } else { // Inf?
500ed1b24bfSSlava Zakharin       if ((!limit || limit >= q + 3) && runtime::toupper(q[0]) == 'I' &&
501ed1b24bfSSlava Zakharin           runtime::toupper(q[1]) == 'N' && runtime::toupper(q[2]) == 'F') {
502ed1b24bfSSlava Zakharin         if ((!limit || limit >= q + 8) && runtime::toupper(q[3]) == 'I' &&
503ed1b24bfSSlava Zakharin             runtime::toupper(q[4]) == 'N' && runtime::toupper(q[5]) == 'I' &&
504ed1b24bfSSlava Zakharin             runtime::toupper(q[6]) == 'T' && runtime::toupper(q[7]) == 'Y') {
505b8f5cf9dSPeter Klausler           p = q + 8;
506b8f5cf9dSPeter Klausler         } else {
50764ab3302SCarolineConcatto           p = q + 3;
508b8f5cf9dSPeter Klausler         }
50964ab3302SCarolineConcatto         return {Real{Infinity()}};
51064ab3302SCarolineConcatto       } else {
51164ab3302SCarolineConcatto         // Invalid input
51264ab3302SCarolineConcatto         return {Real{NaN()}, Invalid};
51364ab3302SCarolineConcatto       }
51464ab3302SCarolineConcatto     }
51564ab3302SCarolineConcatto   }
51664ab3302SCarolineConcatto }
51764ab3302SCarolineConcatto 
51864ab3302SCarolineConcatto template <int PREC>
ConvertToBinary(const char * & p,enum FortranRounding rounding,const char * end)51964ab3302SCarolineConcatto ConversionToBinaryResult<PREC> ConvertToBinary(
520da25f968SPeter Klausler     const char *&p, enum FortranRounding rounding, const char *end) {
521da25f968SPeter Klausler   return BigRadixFloatingPointNumber<PREC>{rounding}.ConvertToBinary(p, end);
52264ab3302SCarolineConcatto }
52364ab3302SCarolineConcatto 
52464ab3302SCarolineConcatto template ConversionToBinaryResult<8> ConvertToBinary<8>(
525da25f968SPeter Klausler     const char *&, enum FortranRounding, const char *end);
52664ab3302SCarolineConcatto template ConversionToBinaryResult<11> ConvertToBinary<11>(
527da25f968SPeter Klausler     const char *&, enum FortranRounding, const char *end);
52864ab3302SCarolineConcatto template ConversionToBinaryResult<24> ConvertToBinary<24>(
529da25f968SPeter Klausler     const char *&, enum FortranRounding, const char *end);
53064ab3302SCarolineConcatto template ConversionToBinaryResult<53> ConvertToBinary<53>(
531da25f968SPeter Klausler     const char *&, enum FortranRounding, const char *end);
53264ab3302SCarolineConcatto template ConversionToBinaryResult<64> ConvertToBinary<64>(
533da25f968SPeter Klausler     const char *&, enum FortranRounding, const char *end);
534b7a5b5c7Speter klausler template ConversionToBinaryResult<113> ConvertToBinary<113>(
535da25f968SPeter Klausler     const char *&, enum FortranRounding, const char *end);
53664ab3302SCarolineConcatto 
53764ab3302SCarolineConcatto extern "C" {
538b329da89SSlava Zakharin RT_EXT_API_GROUP_BEGIN
539b329da89SSlava Zakharin 
ConvertDecimalToFloat(const char ** p,float * f,enum FortranRounding rounding)54064ab3302SCarolineConcatto enum ConversionResultFlags ConvertDecimalToFloat(
54164ab3302SCarolineConcatto     const char **p, float *f, enum FortranRounding rounding) {
54264ab3302SCarolineConcatto   auto result{Fortran::decimal::ConvertToBinary<24>(*p, rounding)};
54364ab3302SCarolineConcatto   std::memcpy(reinterpret_cast<void *>(f),
54464ab3302SCarolineConcatto       reinterpret_cast<const void *>(&result.binary), sizeof *f);
54564ab3302SCarolineConcatto   return result.flags;
54664ab3302SCarolineConcatto }
ConvertDecimalToDouble(const char ** p,double * d,enum FortranRounding rounding)54764ab3302SCarolineConcatto enum ConversionResultFlags ConvertDecimalToDouble(
54864ab3302SCarolineConcatto     const char **p, double *d, enum FortranRounding rounding) {
54964ab3302SCarolineConcatto   auto result{Fortran::decimal::ConvertToBinary<53>(*p, rounding)};
55064ab3302SCarolineConcatto   std::memcpy(reinterpret_cast<void *>(d),
55164ab3302SCarolineConcatto       reinterpret_cast<const void *>(&result.binary), sizeof *d);
55264ab3302SCarolineConcatto   return result.flags;
55364ab3302SCarolineConcatto }
ConvertDecimalToLongDouble(const char ** p,long double * ld,enum FortranRounding rounding)55464ab3302SCarolineConcatto enum ConversionResultFlags ConvertDecimalToLongDouble(
55564ab3302SCarolineConcatto     const char **p, long double *ld, enum FortranRounding rounding) {
55664ab3302SCarolineConcatto   auto result{Fortran::decimal::ConvertToBinary<64>(*p, rounding)};
55764ab3302SCarolineConcatto   std::memcpy(reinterpret_cast<void *>(ld),
55864ab3302SCarolineConcatto       reinterpret_cast<const void *>(&result.binary), sizeof *ld);
55964ab3302SCarolineConcatto   return result.flags;
56064ab3302SCarolineConcatto }
561b329da89SSlava Zakharin 
562b329da89SSlava Zakharin RT_EXT_API_GROUP_END
563b329da89SSlava Zakharin } // extern "C"
5641f879005STim Keith } // namespace Fortran::decimal
565