File: | build/source/flang/include/flang/Evaluate/integer.h |
Warning: | line 471, column 28 The result of the left shift is undefined due to shifting '-1' by '32', which is unrepresentable in the unsigned version of the return type 'unsigned long' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | #include "fp-testing.h" | |||
2 | #include "testing.h" | |||
3 | #include "flang/Evaluate/type.h" | |||
4 | #include "llvm/Support/raw_ostream.h" | |||
5 | #include <cmath> | |||
6 | #include <cstdio> | |||
7 | #include <cstdlib> | |||
8 | #include <type_traits> | |||
9 | ||||
10 | using namespace Fortran::evaluate; | |||
11 | using namespace Fortran::common; | |||
12 | ||||
13 | using Real2 = Scalar<Type<TypeCategory::Real, 2>>; | |||
14 | using Real3 = Scalar<Type<TypeCategory::Real, 3>>; | |||
15 | using Real4 = Scalar<Type<TypeCategory::Real, 4>>; | |||
16 | using Real8 = Scalar<Type<TypeCategory::Real, 8>>; | |||
17 | using Real10 = Scalar<Type<TypeCategory::Real, 10>>; | |||
18 | using Real16 = Scalar<Type<TypeCategory::Real, 16>>; | |||
19 | using Integer4 = Scalar<Type<TypeCategory::Integer, 4>>; | |||
20 | using Integer8 = Scalar<Type<TypeCategory::Integer, 8>>; | |||
21 | ||||
22 | void dumpTest() { | |||
23 | struct { | |||
24 | std::uint64_t raw; | |||
25 | const char *expected; | |||
26 | } table[] = { | |||
27 | {0x7f876543, "NaN0x7f876543"}, | |||
28 | {0x7f800000, "Inf"}, | |||
29 | {0xff800000, "-Inf"}, | |||
30 | {0x00000000, "0.0"}, | |||
31 | {0x80000000, "-0.0"}, | |||
32 | {0x3f800000, "0x1.0p0"}, | |||
33 | {0xbf800000, "-0x1.0p0"}, | |||
34 | {0x40000000, "0x1.0p1"}, | |||
35 | {0x3f000000, "0x1.0p-1"}, | |||
36 | {0x7f7fffff, "0x1.fffffep127"}, | |||
37 | {0x00800000, "0x1.0p-126"}, | |||
38 | {0x00400000, "0x0.8p-126"}, | |||
39 | {0x00000001, "0x0.000002p-126"}, | |||
40 | {0, nullptr}, | |||
41 | }; | |||
42 | for (int j{0}; table[j].expected != nullptr; ++j) { | |||
43 | TEST(Real4{Integer4{table[j].raw}}.DumpHexadecimal() == table[j].expected)testing::Test("flang/unittests/Evaluate/real.cpp", 43, "Real4{Integer4{table[j].raw}}.DumpHexadecimal() == table[j].expected" , (Real4{Integer4{table[j].raw}}.DumpHexadecimal() == table[j ].expected)) | |||
44 | ("%d", j); | |||
45 | } | |||
46 | } | |||
47 | ||||
48 | template <typename R> void basicTests(int rm, Rounding rounding) { | |||
49 | static constexpr int kind{R::bits / 8}; | |||
50 | char desc[64]; | |||
51 | using Word = typename R::Word; | |||
52 | std::snprintf(desc, sizeof desc, "bits=%d, le=%d, kind=%d", R::bits, | |||
53 | Word::littleEndian, kind); | |||
54 | R zero; | |||
55 | TEST(!zero.IsNegative())testing::Test("flang/unittests/Evaluate/real.cpp", 55, "!zero.IsNegative()" , (!zero.IsNegative()))(desc); | |||
56 | TEST(!zero.IsNotANumber())testing::Test("flang/unittests/Evaluate/real.cpp", 56, "!zero.IsNotANumber()" , (!zero.IsNotANumber()))(desc); | |||
57 | TEST(!zero.IsInfinite())testing::Test("flang/unittests/Evaluate/real.cpp", 57, "!zero.IsInfinite()" , (!zero.IsInfinite()))(desc); | |||
58 | TEST(zero.IsZero())testing::Test("flang/unittests/Evaluate/real.cpp", 58, "zero.IsZero()" , (zero.IsZero()))(desc); | |||
59 | MATCH(0, zero.Exponent())testing::Match("flang/unittests/Evaluate/real.cpp", 59, (0), "zero.Exponent()" , (zero.Exponent()))(desc); | |||
60 | TEST(zero.RawBits().IsZero())testing::Test("flang/unittests/Evaluate/real.cpp", 60, "zero.RawBits().IsZero()" , (zero.RawBits().IsZero()))(desc); | |||
61 | MATCH(0, zero.RawBits().ToUInt64())testing::Match("flang/unittests/Evaluate/real.cpp", 61, (0), "zero.RawBits().ToUInt64()" , (zero.RawBits().ToUInt64()))(desc); | |||
62 | TEST(zero.ABS().RawBits().IsZero())testing::Test("flang/unittests/Evaluate/real.cpp", 62, "zero.ABS().RawBits().IsZero()" , (zero.ABS().RawBits().IsZero()))(desc); | |||
63 | TEST(zero.Negate().RawBits().IEOR(Word::MASKL(1)).IsZero())testing::Test("flang/unittests/Evaluate/real.cpp", 63, "zero.Negate().RawBits().IEOR(Word::MASKL(1)).IsZero()" , (zero.Negate().RawBits().IEOR(Word::MASKL(1)).IsZero()))(desc); | |||
64 | TEST(zero.Compare(zero) == Relation::Equal)testing::Test("flang/unittests/Evaluate/real.cpp", 64, "zero.Compare(zero) == Relation::Equal" , (zero.Compare(zero) == Relation::Equal))(desc); | |||
| ||||
65 | R minusZero{Word{std::uint64_t{1}}.SHIFTL(R::bits - 1)}; | |||
66 | TEST(minusZero.IsNegative())testing::Test("flang/unittests/Evaluate/real.cpp", 66, "minusZero.IsNegative()" , (minusZero.IsNegative()))(desc); | |||
67 | TEST(!minusZero.IsNotANumber())testing::Test("flang/unittests/Evaluate/real.cpp", 67, "!minusZero.IsNotANumber()" , (!minusZero.IsNotANumber()))(desc); | |||
68 | TEST(!minusZero.IsInfinite())testing::Test("flang/unittests/Evaluate/real.cpp", 68, "!minusZero.IsInfinite()" , (!minusZero.IsInfinite()))(desc); | |||
69 | TEST(minusZero.IsZero())testing::Test("flang/unittests/Evaluate/real.cpp", 69, "minusZero.IsZero()" , (minusZero.IsZero()))(desc); | |||
70 | TEST(minusZero.ABS().RawBits().IsZero())testing::Test("flang/unittests/Evaluate/real.cpp", 70, "minusZero.ABS().RawBits().IsZero()" , (minusZero.ABS().RawBits().IsZero()))(desc); | |||
71 | TEST(minusZero.Negate().RawBits().IsZero())testing::Test("flang/unittests/Evaluate/real.cpp", 71, "minusZero.Negate().RawBits().IsZero()" , (minusZero.Negate().RawBits().IsZero()))(desc); | |||
72 | MATCH(0, minusZero.Exponent())testing::Match("flang/unittests/Evaluate/real.cpp", 72, (0), "minusZero.Exponent()" , (minusZero.Exponent()))(desc); | |||
73 | MATCH(0, minusZero.RawBits().LEADZ())testing::Match("flang/unittests/Evaluate/real.cpp", 73, (0), "minusZero.RawBits().LEADZ()" , (minusZero.RawBits().LEADZ()))(desc); | |||
74 | MATCH(1, minusZero.RawBits().POPCNT())testing::Match("flang/unittests/Evaluate/real.cpp", 74, (1), "minusZero.RawBits().POPCNT()" , (minusZero.RawBits().POPCNT()))(desc); | |||
75 | TEST(minusZero.Compare(minusZero) == Relation::Equal)testing::Test("flang/unittests/Evaluate/real.cpp", 75, "minusZero.Compare(minusZero) == Relation::Equal" , (minusZero.Compare(minusZero) == Relation::Equal))(desc); | |||
76 | TEST(zero.Compare(minusZero) == Relation::Equal)testing::Test("flang/unittests/Evaluate/real.cpp", 76, "zero.Compare(minusZero) == Relation::Equal" , (zero.Compare(minusZero) == Relation::Equal))(desc); | |||
77 | ValueWithRealFlags<R> vr; | |||
78 | MATCH(0, vr.value.RawBits().ToUInt64())testing::Match("flang/unittests/Evaluate/real.cpp", 78, (0), "vr.value.RawBits().ToUInt64()" , (vr.value.RawBits().ToUInt64()))(desc); | |||
79 | TEST(vr.flags.empty())testing::Test("flang/unittests/Evaluate/real.cpp", 79, "vr.flags.empty()" , (vr.flags.empty()))(desc); | |||
80 | R nan{Word{std::uint64_t{1}} | |||
81 | .SHIFTL(R::bits) | |||
82 | .SubtractSigned(Word{std::uint64_t{1}}) | |||
83 | .value}; | |||
84 | MATCH(R::bits, nan.RawBits().POPCNT())testing::Match("flang/unittests/Evaluate/real.cpp", 84, (R::bits ), "nan.RawBits().POPCNT()", (nan.RawBits().POPCNT()))(desc); | |||
85 | TEST(!nan.IsNegative())testing::Test("flang/unittests/Evaluate/real.cpp", 85, "!nan.IsNegative()" , (!nan.IsNegative()))(desc); | |||
86 | TEST(nan.IsNotANumber())testing::Test("flang/unittests/Evaluate/real.cpp", 86, "nan.IsNotANumber()" , (nan.IsNotANumber()))(desc); | |||
87 | TEST(!nan.IsInfinite())testing::Test("flang/unittests/Evaluate/real.cpp", 87, "!nan.IsInfinite()" , (!nan.IsInfinite()))(desc); | |||
88 | TEST(!nan.IsZero())testing::Test("flang/unittests/Evaluate/real.cpp", 88, "!nan.IsZero()" , (!nan.IsZero()))(desc); | |||
89 | TEST(zero.Compare(nan) == Relation::Unordered)testing::Test("flang/unittests/Evaluate/real.cpp", 89, "zero.Compare(nan) == Relation::Unordered" , (zero.Compare(nan) == Relation::Unordered))(desc); | |||
90 | TEST(minusZero.Compare(nan) == Relation::Unordered)testing::Test("flang/unittests/Evaluate/real.cpp", 90, "minusZero.Compare(nan) == Relation::Unordered" , (minusZero.Compare(nan) == Relation::Unordered))(desc); | |||
91 | TEST(nan.Compare(zero) == Relation::Unordered)testing::Test("flang/unittests/Evaluate/real.cpp", 91, "nan.Compare(zero) == Relation::Unordered" , (nan.Compare(zero) == Relation::Unordered))(desc); | |||
92 | TEST(nan.Compare(minusZero) == Relation::Unordered)testing::Test("flang/unittests/Evaluate/real.cpp", 92, "nan.Compare(minusZero) == Relation::Unordered" , (nan.Compare(minusZero) == Relation::Unordered))(desc); | |||
93 | TEST(nan.Compare(nan) == Relation::Unordered)testing::Test("flang/unittests/Evaluate/real.cpp", 93, "nan.Compare(nan) == Relation::Unordered" , (nan.Compare(nan) == Relation::Unordered))(desc); | |||
94 | int significandBits{R::binaryPrecision - R::isImplicitMSB}; | |||
95 | int exponentBits{R::bits - significandBits - 1}; | |||
96 | std::uint64_t maxExponent{(std::uint64_t{1} << exponentBits) - 1}; | |||
97 | MATCH(nan.Exponent(), maxExponent)testing::Match("flang/unittests/Evaluate/real.cpp", 97, (nan. Exponent()), "maxExponent", (maxExponent))(desc); | |||
98 | R inf{Word{maxExponent}.SHIFTL(significandBits)}; | |||
99 | TEST(!inf.IsNegative())testing::Test("flang/unittests/Evaluate/real.cpp", 99, "!inf.IsNegative()" , (!inf.IsNegative()))(desc); | |||
100 | TEST(!inf.IsNotANumber())testing::Test("flang/unittests/Evaluate/real.cpp", 100, "!inf.IsNotANumber()" , (!inf.IsNotANumber()))(desc); | |||
101 | TEST(inf.IsInfinite())testing::Test("flang/unittests/Evaluate/real.cpp", 101, "inf.IsInfinite()" , (inf.IsInfinite()))(desc); | |||
102 | TEST(!inf.IsZero())testing::Test("flang/unittests/Evaluate/real.cpp", 102, "!inf.IsZero()" , (!inf.IsZero()))(desc); | |||
103 | TEST(inf.RawBits().CompareUnsigned(inf.ABS().RawBits()) == Ordering::Equal)testing::Test("flang/unittests/Evaluate/real.cpp", 103, "inf.RawBits().CompareUnsigned(inf.ABS().RawBits()) == Ordering::Equal" , (inf.RawBits().CompareUnsigned(inf.ABS().RawBits()) == Ordering ::Equal)) | |||
104 | (desc); | |||
105 | TEST(zero.Compare(inf) == Relation::Less)testing::Test("flang/unittests/Evaluate/real.cpp", 105, "zero.Compare(inf) == Relation::Less" , (zero.Compare(inf) == Relation::Less))(desc); | |||
106 | TEST(minusZero.Compare(inf) == Relation::Less)testing::Test("flang/unittests/Evaluate/real.cpp", 106, "minusZero.Compare(inf) == Relation::Less" , (minusZero.Compare(inf) == Relation::Less))(desc); | |||
107 | TEST(nan.Compare(inf) == Relation::Unordered)testing::Test("flang/unittests/Evaluate/real.cpp", 107, "nan.Compare(inf) == Relation::Unordered" , (nan.Compare(inf) == Relation::Unordered))(desc); | |||
108 | TEST(inf.Compare(inf) == Relation::Equal)testing::Test("flang/unittests/Evaluate/real.cpp", 108, "inf.Compare(inf) == Relation::Equal" , (inf.Compare(inf) == Relation::Equal))(desc); | |||
109 | R negInf{Word{maxExponent}.SHIFTL(significandBits).IOR(Word::MASKL(1))}; | |||
110 | TEST(negInf.IsNegative())testing::Test("flang/unittests/Evaluate/real.cpp", 110, "negInf.IsNegative()" , (negInf.IsNegative()))(desc); | |||
111 | TEST(!negInf.IsNotANumber())testing::Test("flang/unittests/Evaluate/real.cpp", 111, "!negInf.IsNotANumber()" , (!negInf.IsNotANumber()))(desc); | |||
112 | TEST(negInf.IsInfinite())testing::Test("flang/unittests/Evaluate/real.cpp", 112, "negInf.IsInfinite()" , (negInf.IsInfinite()))(desc); | |||
113 | TEST(!negInf.IsZero())testing::Test("flang/unittests/Evaluate/real.cpp", 113, "!negInf.IsZero()" , (!negInf.IsZero()))(desc); | |||
114 | TEST(inf.RawBits().CompareUnsigned(negInf.ABS().RawBits()) == Ordering::Equal)testing::Test("flang/unittests/Evaluate/real.cpp", 114, "inf.RawBits().CompareUnsigned(negInf.ABS().RawBits()) == Ordering::Equal" , (inf.RawBits().CompareUnsigned(negInf.ABS().RawBits()) == Ordering ::Equal)) | |||
115 | (desc); | |||
116 | TEST(inf.RawBits().CompareUnsigned(negInf.Negate().RawBits()) ==testing::Test("flang/unittests/Evaluate/real.cpp", 117, "inf.RawBits().CompareUnsigned(negInf.Negate().RawBits()) == Ordering::Equal" , (inf.RawBits().CompareUnsigned(negInf.Negate().RawBits()) == Ordering::Equal)) | |||
117 | Ordering::Equal)testing::Test("flang/unittests/Evaluate/real.cpp", 117, "inf.RawBits().CompareUnsigned(negInf.Negate().RawBits()) == Ordering::Equal" , (inf.RawBits().CompareUnsigned(negInf.Negate().RawBits()) == Ordering::Equal)) | |||
118 | (desc); | |||
119 | TEST(inf.Negate().RawBits().CompareUnsigned(negInf.RawBits()) ==testing::Test("flang/unittests/Evaluate/real.cpp", 120, "inf.Negate().RawBits().CompareUnsigned(negInf.RawBits()) == Ordering::Equal" , (inf.Negate().RawBits().CompareUnsigned(negInf.RawBits()) == Ordering::Equal)) | |||
120 | Ordering::Equal)testing::Test("flang/unittests/Evaluate/real.cpp", 120, "inf.Negate().RawBits().CompareUnsigned(negInf.RawBits()) == Ordering::Equal" , (inf.Negate().RawBits().CompareUnsigned(negInf.RawBits()) == Ordering::Equal)) | |||
121 | (desc); | |||
122 | TEST(zero.Compare(negInf) == Relation::Greater)testing::Test("flang/unittests/Evaluate/real.cpp", 122, "zero.Compare(negInf) == Relation::Greater" , (zero.Compare(negInf) == Relation::Greater))(desc); | |||
123 | TEST(minusZero.Compare(negInf) == Relation::Greater)testing::Test("flang/unittests/Evaluate/real.cpp", 123, "minusZero.Compare(negInf) == Relation::Greater" , (minusZero.Compare(negInf) == Relation::Greater))(desc); | |||
124 | TEST(nan.Compare(negInf) == Relation::Unordered)testing::Test("flang/unittests/Evaluate/real.cpp", 124, "nan.Compare(negInf) == Relation::Unordered" , (nan.Compare(negInf) == Relation::Unordered))(desc); | |||
125 | TEST(inf.Compare(negInf) == Relation::Greater)testing::Test("flang/unittests/Evaluate/real.cpp", 125, "inf.Compare(negInf) == Relation::Greater" , (inf.Compare(negInf) == Relation::Greater))(desc); | |||
126 | TEST(negInf.Compare(negInf) == Relation::Equal)testing::Test("flang/unittests/Evaluate/real.cpp", 126, "negInf.Compare(negInf) == Relation::Equal" , (negInf.Compare(negInf) == Relation::Equal))(desc); | |||
127 | for (std::uint64_t j{0}; j < 63; ++j) { | |||
128 | char ldesc[128]; | |||
129 | std::uint64_t x{1}; | |||
130 | x <<= j; | |||
131 | std::snprintf(ldesc, sizeof ldesc, "%s j=%d x=0x%jx rm=%d", desc, | |||
132 | static_cast<int>(j), static_cast<std::intmax_t>(x), rm); | |||
133 | Integer8 ix{x}; | |||
134 | TEST(!ix.IsNegative())testing::Test("flang/unittests/Evaluate/real.cpp", 134, "!ix.IsNegative()" , (!ix.IsNegative()))(ldesc); | |||
135 | MATCH(x, ix.ToUInt64())testing::Match("flang/unittests/Evaluate/real.cpp", 135, (x), "ix.ToUInt64()", (ix.ToUInt64()))(ldesc); | |||
136 | vr = R::FromInteger(ix, rounding); | |||
137 | TEST(!vr.value.IsNegative())testing::Test("flang/unittests/Evaluate/real.cpp", 137, "!vr.value.IsNegative()" , (!vr.value.IsNegative()))(ldesc); | |||
138 | TEST(!vr.value.IsNotANumber())testing::Test("flang/unittests/Evaluate/real.cpp", 138, "!vr.value.IsNotANumber()" , (!vr.value.IsNotANumber()))(ldesc); | |||
139 | TEST(!vr.value.IsZero())testing::Test("flang/unittests/Evaluate/real.cpp", 139, "!vr.value.IsZero()" , (!vr.value.IsZero()))(ldesc); | |||
140 | auto ivf = vr.value.template ToInteger<Integer8>(); | |||
141 | if (j > (maxExponent / 2)) { | |||
142 | TEST(vr.flags.test(RealFlag::Overflow))testing::Test("flang/unittests/Evaluate/real.cpp", 142, "vr.flags.test(RealFlag::Overflow)" , (vr.flags.test(RealFlag::Overflow)))(ldesc); | |||
143 | TEST(vr.value.IsInfinite())testing::Test("flang/unittests/Evaluate/real.cpp", 143, "vr.value.IsInfinite()" , (vr.value.IsInfinite()))(ldesc); | |||
144 | TEST(ivf.flags.test(RealFlag::Overflow))testing::Test("flang/unittests/Evaluate/real.cpp", 144, "ivf.flags.test(RealFlag::Overflow)" , (ivf.flags.test(RealFlag::Overflow)))(ldesc); | |||
145 | MATCH(0x7fffffffffffffff, ivf.value.ToUInt64())testing::Match("flang/unittests/Evaluate/real.cpp", 145, (0x7fffffffffffffff ), "ivf.value.ToUInt64()", (ivf.value.ToUInt64()))(ldesc); | |||
146 | } else { | |||
147 | TEST(vr.flags.empty())testing::Test("flang/unittests/Evaluate/real.cpp", 147, "vr.flags.empty()" , (vr.flags.empty()))(ldesc); | |||
148 | TEST(!vr.value.IsInfinite())testing::Test("flang/unittests/Evaluate/real.cpp", 148, "!vr.value.IsInfinite()" , (!vr.value.IsInfinite()))(ldesc); | |||
149 | TEST(ivf.flags.empty())testing::Test("flang/unittests/Evaluate/real.cpp", 149, "ivf.flags.empty()" , (ivf.flags.empty()))(ldesc); | |||
150 | MATCH(x, ivf.value.ToUInt64())testing::Match("flang/unittests/Evaluate/real.cpp", 150, (x), "ivf.value.ToUInt64()", (ivf.value.ToUInt64()))(ldesc); | |||
151 | if (rounding.mode == RoundingMode::TiesToEven) { // to match stold() | |||
152 | std::string buf; | |||
153 | llvm::raw_string_ostream ss{buf}; | |||
154 | vr.value.AsFortran(ss, kind, false /*exact*/); | |||
155 | std::string decimal{ss.str()}; | |||
156 | const char *p{decimal.data()}; | |||
157 | MATCH(x, static_cast<std::uint64_t>(std::stold(decimal)))testing::Match("flang/unittests/Evaluate/real.cpp", 157, (x), "static_cast<std::uint64_t>(std::stold(decimal))", (static_cast <std::uint64_t>(std::stold(decimal)))) | |||
158 | ("%s %s", ldesc, p); | |||
159 | auto check{R::Read(p, rounding)}; | |||
160 | auto icheck{check.value.template ToInteger<Integer8>()}; | |||
161 | MATCH(x, icheck.value.ToUInt64())testing::Match("flang/unittests/Evaluate/real.cpp", 161, (x), "icheck.value.ToUInt64()", (icheck.value.ToUInt64()))(ldesc); | |||
162 | TEST(vr.value.Compare(check.value) == Relation::Equal)testing::Test("flang/unittests/Evaluate/real.cpp", 162, "vr.value.Compare(check.value) == Relation::Equal" , (vr.value.Compare(check.value) == Relation::Equal))(ldesc); | |||
163 | } | |||
164 | } | |||
165 | TEST(vr.value.ToWholeNumber().value.Compare(vr.value) == Relation::Equal)testing::Test("flang/unittests/Evaluate/real.cpp", 165, "vr.value.ToWholeNumber().value.Compare(vr.value) == Relation::Equal" , (vr.value.ToWholeNumber().value.Compare(vr.value) == Relation ::Equal)) | |||
166 | (ldesc); | |||
167 | ix = ix.Negate().value; | |||
168 | TEST(ix.IsNegative())testing::Test("flang/unittests/Evaluate/real.cpp", 168, "ix.IsNegative()" , (ix.IsNegative()))(ldesc); | |||
169 | x = -x; | |||
170 | std::int64_t nx = x; | |||
171 | MATCH(x, ix.ToUInt64())testing::Match("flang/unittests/Evaluate/real.cpp", 171, (x), "ix.ToUInt64()", (ix.ToUInt64()))(ldesc); | |||
172 | MATCH(nx, ix.ToInt64())testing::Match("flang/unittests/Evaluate/real.cpp", 172, (nx) , "ix.ToInt64()", (ix.ToInt64()))(ldesc); | |||
173 | vr = R::FromInteger(ix); | |||
174 | TEST(vr.value.IsNegative())testing::Test("flang/unittests/Evaluate/real.cpp", 174, "vr.value.IsNegative()" , (vr.value.IsNegative()))(ldesc); | |||
175 | TEST(!vr.value.IsNotANumber())testing::Test("flang/unittests/Evaluate/real.cpp", 175, "!vr.value.IsNotANumber()" , (!vr.value.IsNotANumber()))(ldesc); | |||
176 | TEST(!vr.value.IsZero())testing::Test("flang/unittests/Evaluate/real.cpp", 176, "!vr.value.IsZero()" , (!vr.value.IsZero()))(ldesc); | |||
177 | ivf = vr.value.template ToInteger<Integer8>(); | |||
178 | if (j > (maxExponent / 2)) { | |||
179 | TEST(vr.flags.test(RealFlag::Overflow))testing::Test("flang/unittests/Evaluate/real.cpp", 179, "vr.flags.test(RealFlag::Overflow)" , (vr.flags.test(RealFlag::Overflow)))(ldesc); | |||
180 | TEST(vr.value.IsInfinite())testing::Test("flang/unittests/Evaluate/real.cpp", 180, "vr.value.IsInfinite()" , (vr.value.IsInfinite()))(ldesc); | |||
181 | TEST(ivf.flags.test(RealFlag::Overflow))testing::Test("flang/unittests/Evaluate/real.cpp", 181, "ivf.flags.test(RealFlag::Overflow)" , (ivf.flags.test(RealFlag::Overflow)))(ldesc); | |||
182 | MATCH(0x8000000000000000, ivf.value.ToUInt64())testing::Match("flang/unittests/Evaluate/real.cpp", 182, (0x8000000000000000 ), "ivf.value.ToUInt64()", (ivf.value.ToUInt64()))(ldesc); | |||
183 | } else { | |||
184 | TEST(vr.flags.empty())testing::Test("flang/unittests/Evaluate/real.cpp", 184, "vr.flags.empty()" , (vr.flags.empty()))(ldesc); | |||
185 | TEST(!vr.value.IsInfinite())testing::Test("flang/unittests/Evaluate/real.cpp", 185, "!vr.value.IsInfinite()" , (!vr.value.IsInfinite()))(ldesc); | |||
186 | TEST(ivf.flags.empty())testing::Test("flang/unittests/Evaluate/real.cpp", 186, "ivf.flags.empty()" , (ivf.flags.empty()))(ldesc); | |||
187 | MATCH(x, ivf.value.ToUInt64())testing::Match("flang/unittests/Evaluate/real.cpp", 187, (x), "ivf.value.ToUInt64()", (ivf.value.ToUInt64()))(ldesc); | |||
188 | MATCH(nx, ivf.value.ToInt64())testing::Match("flang/unittests/Evaluate/real.cpp", 188, (nx) , "ivf.value.ToInt64()", (ivf.value.ToInt64()))(ldesc); | |||
189 | } | |||
190 | TEST(vr.value.ToWholeNumber().value.Compare(vr.value) == Relation::Equal)testing::Test("flang/unittests/Evaluate/real.cpp", 190, "vr.value.ToWholeNumber().value.Compare(vr.value) == Relation::Equal" , (vr.value.ToWholeNumber().value.Compare(vr.value) == Relation ::Equal)) | |||
191 | (ldesc); | |||
192 | } | |||
193 | } | |||
194 | ||||
195 | // Takes an integer and distributes its bits across a floating | |||
196 | // point value. The LSB is used to complement the result. | |||
197 | std::uint32_t MakeReal(std::uint32_t n) { | |||
198 | int shifts[] = {-1, 31, 23, 30, 22, 0, 24, 29, 25, 28, 26, 1, 16, 21, 2, -1}; | |||
199 | std::uint32_t x{0}; | |||
200 | for (int j{1}; shifts[j] >= 0; ++j) { | |||
201 | x |= ((n >> j) & 1) << shifts[j]; | |||
202 | } | |||
203 | x ^= -(n & 1); | |||
204 | return x; | |||
205 | } | |||
206 | ||||
207 | std::uint64_t MakeReal(std::uint64_t n) { | |||
208 | int shifts[] = { | |||
209 | -1, 63, 52, 62, 51, 0, 53, 61, 54, 60, 55, 59, 1, 16, 50, 2, -1}; | |||
210 | std::uint64_t x{0}; | |||
211 | for (int j{1}; shifts[j] >= 0; ++j) { | |||
212 | x |= ((n >> j) & 1) << shifts[j]; | |||
213 | } | |||
214 | x ^= -(n & 1); | |||
215 | return x; | |||
216 | } | |||
217 | ||||
218 | inline bool IsNaN(std::uint32_t x) { | |||
219 | return (x & 0x7f800000) == 0x7f800000 && (x & 0x007fffff) != 0; | |||
220 | } | |||
221 | ||||
222 | inline bool IsNaN(std::uint64_t x) { | |||
223 | return (x & 0x7ff0000000000000) == 0x7ff0000000000000 && | |||
224 | (x & 0x000fffffffffffff) != 0; | |||
225 | } | |||
226 | ||||
227 | inline bool IsInfinite(std::uint32_t x) { | |||
228 | return (x & 0x7fffffff) == 0x7f800000; | |||
229 | } | |||
230 | ||||
231 | inline bool IsInfinite(std::uint64_t x) { | |||
232 | return (x & 0x7fffffffffffffff) == 0x7ff0000000000000; | |||
233 | } | |||
234 | ||||
235 | inline bool IsNegative(std::uint32_t x) { return (x & 0x80000000) != 0; } | |||
236 | ||||
237 | inline bool IsNegative(std::uint64_t x) { | |||
238 | return (x & 0x8000000000000000) != 0; | |||
239 | } | |||
240 | ||||
241 | inline std::uint32_t NormalizeNaN(std::uint32_t x) { | |||
242 | if (IsNaN(x)) { | |||
243 | x = 0x7fe00000; | |||
244 | } | |||
245 | return x; | |||
246 | } | |||
247 | ||||
248 | inline std::uint64_t NormalizeNaN(std::uint64_t x) { | |||
249 | if (IsNaN(x)) { | |||
250 | x = 0x7ffc000000000000; | |||
251 | } | |||
252 | return x; | |||
253 | } | |||
254 | ||||
255 | enum FlagBits { | |||
256 | Overflow = 1, | |||
257 | DivideByZero = 2, | |||
258 | InvalidArgument = 4, | |||
259 | Underflow = 8, | |||
260 | Inexact = 16, | |||
261 | }; | |||
262 | ||||
263 | #ifdef __clang__1 | |||
264 | // clang support for fenv.h is broken, so tests of flag settings | |||
265 | // are disabled. | |||
266 | inline std::uint32_t FlagsToBits(const RealFlags &) { return 0; } | |||
267 | #else | |||
268 | inline std::uint32_t FlagsToBits(const RealFlags &flags) { | |||
269 | std::uint32_t bits{0}; | |||
270 | if (flags.test(RealFlag::Overflow)) { | |||
271 | bits |= Overflow; | |||
272 | } | |||
273 | if (flags.test(RealFlag::DivideByZero)) { | |||
274 | bits |= DivideByZero; | |||
275 | } | |||
276 | if (flags.test(RealFlag::InvalidArgument)) { | |||
277 | bits |= InvalidArgument; | |||
278 | } | |||
279 | if (flags.test(RealFlag::Underflow)) { | |||
280 | bits |= Underflow; | |||
281 | } | |||
282 | if (flags.test(RealFlag::Inexact)) { | |||
283 | bits |= Inexact; | |||
284 | } | |||
285 | return bits; | |||
286 | } | |||
287 | #endif // __clang__ | |||
288 | ||||
289 | template <typename UINT = std::uint32_t, typename FLT = float, typename REAL> | |||
290 | void inttest(std::int64_t x, int pass, Rounding rounding) { | |||
291 | union { | |||
292 | UINT ui; | |||
293 | FLT f; | |||
294 | } u; | |||
295 | ScopedHostFloatingPointEnvironment fpenv; | |||
296 | Integer8 ix{x}; | |||
297 | ValueWithRealFlags<REAL> real; | |||
298 | real = real.value.FromInteger(ix, rounding); | |||
299 | #ifndef __clang__1 // broken and also slow | |||
300 | fpenv.ClearFlags(); | |||
301 | #endif | |||
302 | FLT fcheck = x; // TODO unsigned too | |||
303 | auto actualFlags{FlagsToBits(fpenv.CurrentFlags())}; | |||
304 | u.f = fcheck; | |||
305 | UINT rcheck{NormalizeNaN(u.ui)}; | |||
306 | UINT check = real.value.RawBits().ToUInt64(); | |||
307 | MATCH(rcheck, check)testing::Match("flang/unittests/Evaluate/real.cpp", 307, (rcheck ), "check", (check))("%d 0x%llx", pass, x); | |||
308 | MATCH(actualFlags, FlagsToBits(real.flags))testing::Match("flang/unittests/Evaluate/real.cpp", 308, (actualFlags ), "FlagsToBits(real.flags)", (FlagsToBits(real.flags)))("%d 0x%llx", pass, x); | |||
309 | } | |||
310 | ||||
311 | template <typename FLT = float> FLT ToIntPower(FLT x, int power) { | |||
312 | if (power == 0) { | |||
313 | return x / x; | |||
314 | } | |||
315 | bool negative{power < 0}; | |||
316 | if (negative) { | |||
317 | power = -power; | |||
318 | } | |||
319 | FLT result{1}; | |||
320 | while (power > 0) { | |||
321 | if (power & 1) { | |||
322 | result *= x; | |||
323 | } | |||
324 | x *= x; | |||
325 | power >>= 1; | |||
326 | } | |||
327 | if (negative) { | |||
328 | result = 1.0 / result; | |||
329 | } | |||
330 | return result; | |||
331 | } | |||
332 | ||||
333 | template <typename FLT, int decimalDigits> | |||
334 | FLT TimesIntPowerOfTen(FLT x, int power) { | |||
335 | if (power > decimalDigits || power < -decimalDigits) { | |||
336 | auto maxExactPowerOfTen{ | |||
337 | TimesIntPowerOfTen<FLT, decimalDigits>(1, decimalDigits)}; | |||
338 | auto big{ToIntPower<FLT>(maxExactPowerOfTen, power / decimalDigits)}; | |||
339 | auto small{ | |||
340 | TimesIntPowerOfTen<FLT, decimalDigits>(1, power % decimalDigits)}; | |||
341 | return (x * big) * small; | |||
342 | } | |||
343 | return x * ToIntPower<FLT>(10.0, power); | |||
344 | } | |||
345 | ||||
346 | template <typename UINT = std::uint32_t, typename FLT = float, | |||
347 | typename REAL = Real4> | |||
348 | void subsetTests(int pass, Rounding rounding, std::uint32_t opds) { | |||
349 | for (int j{0}; j < 63; ++j) { | |||
350 | std::int64_t x{1}; | |||
351 | x <<= j; | |||
352 | inttest<UINT, FLT, REAL>(x, pass, rounding); | |||
353 | inttest<UINT, FLT, REAL>(-x, pass, rounding); | |||
354 | } | |||
355 | inttest<UINT, FLT, REAL>(0, pass, rounding); | |||
356 | inttest<UINT, FLT, REAL>( | |||
357 | static_cast<std::int64_t>(0x8000000000000000), pass, rounding); | |||
358 | ||||
359 | union { | |||
360 | UINT ui; | |||
361 | FLT f; | |||
362 | } u; | |||
363 | ScopedHostFloatingPointEnvironment fpenv; | |||
364 | ||||
365 | for (UINT j{0}; j < opds; ++j) { | |||
366 | ||||
367 | UINT rj{MakeReal(j)}; | |||
368 | u.ui = rj; | |||
369 | FLT fj{u.f}; | |||
370 | REAL x{typename REAL::Word{std::uint64_t{rj}}}; | |||
371 | ||||
372 | // unary operations | |||
373 | { | |||
374 | ValueWithRealFlags<REAL> aint{x.ToWholeNumber()}; | |||
375 | #ifndef __clang__1 // broken and also slow | |||
376 | fpenv.ClearFlags(); | |||
377 | #endif | |||
378 | FLT fcheck{std::trunc(fj)}; | |||
379 | auto actualFlags{FlagsToBits(fpenv.CurrentFlags())}; | |||
380 | actualFlags &= ~Inexact; // x86 std::trunc can set Inexact; AINT ain't | |||
381 | u.f = fcheck; | |||
382 | #ifndef __clang__1 | |||
383 | if (IsNaN(u.ui)) { | |||
384 | actualFlags |= InvalidArgument; // x86 std::trunc(NaN) workaround | |||
385 | } | |||
386 | #endif | |||
387 | UINT rcheck{NormalizeNaN(u.ui)}; | |||
388 | UINT check = aint.value.RawBits().ToUInt64(); | |||
389 | MATCH(rcheck, check)testing::Match("flang/unittests/Evaluate/real.cpp", 389, (rcheck ), "check", (check)) | |||
390 | ("%d AINT(0x%jx)", pass, static_cast<std::intmax_t>(rj)); | |||
391 | MATCH(actualFlags, FlagsToBits(aint.flags))testing::Match("flang/unittests/Evaluate/real.cpp", 391, (actualFlags ), "FlagsToBits(aint.flags)", (FlagsToBits(aint.flags))) | |||
392 | ("%d AINT(0x%jx)", pass, static_cast<std::intmax_t>(rj)); | |||
393 | } | |||
394 | ||||
395 | { | |||
396 | ValueWithRealFlags<REAL> root{x.SQRT(rounding)}; | |||
397 | #ifndef __clang__1 // broken and also slow | |||
398 | fpenv.ClearFlags(); | |||
399 | #endif | |||
400 | FLT fcheck{std::sqrt(fj)}; | |||
401 | auto actualFlags{FlagsToBits(fpenv.CurrentFlags())}; | |||
402 | u.f = fcheck; | |||
403 | UINT rcheck{NormalizeNaN(u.ui)}; | |||
404 | UINT check = root.value.RawBits().ToUInt64(); | |||
405 | MATCH(rcheck, check)testing::Match("flang/unittests/Evaluate/real.cpp", 405, (rcheck ), "check", (check)) | |||
406 | ("%d SQRT(0x%jx)", pass, static_cast<std::intmax_t>(rj)); | |||
407 | MATCH(actualFlags, FlagsToBits(root.flags))testing::Match("flang/unittests/Evaluate/real.cpp", 407, (actualFlags ), "FlagsToBits(root.flags)", (FlagsToBits(root.flags))) | |||
408 | ("%d SQRT(0x%jx)", pass, static_cast<std::intmax_t>(rj)); | |||
409 | } | |||
410 | ||||
411 | { | |||
412 | MATCH(IsNaN(rj), x.IsNotANumber())testing::Match("flang/unittests/Evaluate/real.cpp", 412, (IsNaN (rj)), "x.IsNotANumber()", (x.IsNotANumber())) | |||
413 | ("%d IsNaN(0x%jx)", pass, static_cast<std::intmax_t>(rj)); | |||
414 | MATCH(IsInfinite(rj), x.IsInfinite())testing::Match("flang/unittests/Evaluate/real.cpp", 414, (IsInfinite (rj)), "x.IsInfinite()", (x.IsInfinite())) | |||
415 | ("%d IsInfinite(0x%jx)", pass, static_cast<std::intmax_t>(rj)); | |||
416 | ||||
417 | static constexpr int kind{REAL::bits / 8}; | |||
418 | std::string ssBuf, cssBuf; | |||
419 | llvm::raw_string_ostream ss{ssBuf}; | |||
420 | llvm::raw_string_ostream css{cssBuf}; | |||
421 | x.AsFortran(ss, kind, false /*exact*/); | |||
422 | std::string s{ss.str()}; | |||
423 | if (IsNaN(rj)) { | |||
424 | css << "(0._" << kind << "/0.)"; | |||
425 | MATCH(css.str(), s)testing::Match("flang/unittests/Evaluate/real.cpp", 425, (css .str()), "s", (s)) | |||
426 | ("%d invalid(0x%jx)", pass, static_cast<std::intmax_t>(rj)); | |||
427 | } else if (IsInfinite(rj)) { | |||
428 | css << '('; | |||
429 | if (IsNegative(rj)) { | |||
430 | css << '-'; | |||
431 | } | |||
432 | css << "1._" << kind << "/0.)"; | |||
433 | MATCH(css.str(), s)testing::Match("flang/unittests/Evaluate/real.cpp", 433, (css .str()), "s", (s)) | |||
434 | ("%d overflow(0x%jx)", pass, static_cast<std::intmax_t>(rj)); | |||
435 | } else { | |||
436 | const char *p = s.data(); | |||
437 | if (*p == '(') { | |||
438 | ++p; | |||
439 | } | |||
440 | auto readBack{REAL::Read(p, rounding)}; | |||
441 | MATCH(rj, readBack.value.RawBits().ToUInt64())testing::Match("flang/unittests/Evaluate/real.cpp", 441, (rj) , "readBack.value.RawBits().ToUInt64()", (readBack.value.RawBits ().ToUInt64())) | |||
442 | ("%d Read(AsFortran()) 0x%jx %s %g", pass, | |||
443 | static_cast<std::intmax_t>(rj), s.data(), static_cast<double>(fj)); | |||
444 | MATCH('_', *p)testing::Match("flang/unittests/Evaluate/real.cpp", 444, ('_' ), "*p", (*p)) | |||
445 | ("%d Read(AsFortran()) 0x%jx %s %d", pass, | |||
446 | static_cast<std::intmax_t>(rj), s.data(), | |||
447 | static_cast<int>(p - s.data())); | |||
448 | } | |||
449 | } | |||
450 | ||||
451 | // dyadic operations | |||
452 | for (UINT k{0}; k < opds; ++k) { | |||
453 | UINT rk{MakeReal(k)}; | |||
454 | u.ui = rk; | |||
455 | FLT fk{u.f}; | |||
456 | REAL y{typename REAL::Word{std::uint64_t{rk}}}; | |||
457 | { | |||
458 | ValueWithRealFlags<REAL> sum{x.Add(y, rounding)}; | |||
459 | #ifndef __clang__1 // broken and also slow | |||
460 | fpenv.ClearFlags(); | |||
461 | #endif | |||
462 | FLT fcheck{fj + fk}; | |||
463 | auto actualFlags{FlagsToBits(fpenv.CurrentFlags())}; | |||
464 | u.f = fcheck; | |||
465 | UINT rcheck{NormalizeNaN(u.ui)}; | |||
466 | UINT check = sum.value.RawBits().ToUInt64(); | |||
467 | MATCH(rcheck, check)testing::Match("flang/unittests/Evaluate/real.cpp", 467, (rcheck ), "check", (check)) | |||
468 | ("%d 0x%jx + 0x%jx", pass, static_cast<std::intmax_t>(rj), | |||
469 | static_cast<std::intmax_t>(rk)); | |||
470 | MATCH(actualFlags, FlagsToBits(sum.flags))testing::Match("flang/unittests/Evaluate/real.cpp", 470, (actualFlags ), "FlagsToBits(sum.flags)", (FlagsToBits(sum.flags))) | |||
471 | ("%d 0x%jx + 0x%jx", pass, static_cast<std::intmax_t>(rj), | |||
472 | static_cast<std::intmax_t>(rk)); | |||
473 | } | |||
474 | { | |||
475 | ValueWithRealFlags<REAL> diff{x.Subtract(y, rounding)}; | |||
476 | #ifndef __clang__1 // broken and also slow | |||
477 | fpenv.ClearFlags(); | |||
478 | #endif | |||
479 | FLT fcheck{fj - fk}; | |||
480 | auto actualFlags{FlagsToBits(fpenv.CurrentFlags())}; | |||
481 | u.f = fcheck; | |||
482 | UINT rcheck{NormalizeNaN(u.ui)}; | |||
483 | UINT check = diff.value.RawBits().ToUInt64(); | |||
484 | MATCH(rcheck, check)testing::Match("flang/unittests/Evaluate/real.cpp", 484, (rcheck ), "check", (check)) | |||
485 | ("%d 0x%jx - 0x%jx", pass, static_cast<std::intmax_t>(rj), | |||
486 | static_cast<std::intmax_t>(rk)); | |||
487 | MATCH(actualFlags, FlagsToBits(diff.flags))testing::Match("flang/unittests/Evaluate/real.cpp", 487, (actualFlags ), "FlagsToBits(diff.flags)", (FlagsToBits(diff.flags))) | |||
488 | ("%d 0x%jx - 0x%jx", pass, static_cast<std::intmax_t>(rj), | |||
489 | static_cast<std::intmax_t>(rk)); | |||
490 | } | |||
491 | { | |||
492 | ValueWithRealFlags<REAL> prod{x.Multiply(y, rounding)}; | |||
493 | #ifndef __clang__1 // broken and also slow | |||
494 | fpenv.ClearFlags(); | |||
495 | #endif | |||
496 | FLT fcheck{fj * fk}; | |||
497 | auto actualFlags{FlagsToBits(fpenv.CurrentFlags())}; | |||
498 | u.f = fcheck; | |||
499 | UINT rcheck{NormalizeNaN(u.ui)}; | |||
500 | UINT check = prod.value.RawBits().ToUInt64(); | |||
501 | MATCH(rcheck, check)testing::Match("flang/unittests/Evaluate/real.cpp", 501, (rcheck ), "check", (check)) | |||
502 | ("%d 0x%jx * 0x%jx", pass, static_cast<std::intmax_t>(rj), | |||
503 | static_cast<std::intmax_t>(rk)); | |||
504 | MATCH(actualFlags, FlagsToBits(prod.flags))testing::Match("flang/unittests/Evaluate/real.cpp", 504, (actualFlags ), "FlagsToBits(prod.flags)", (FlagsToBits(prod.flags))) | |||
505 | ("%d 0x%jx * 0x%jx", pass, static_cast<std::intmax_t>(rj), | |||
506 | static_cast<std::intmax_t>(rk)); | |||
507 | } | |||
508 | { | |||
509 | ValueWithRealFlags<REAL> quot{x.Divide(y, rounding)}; | |||
510 | #ifndef __clang__1 // broken and also slow | |||
511 | fpenv.ClearFlags(); | |||
512 | #endif | |||
513 | FLT fcheck{fj / fk}; | |||
514 | auto actualFlags{FlagsToBits(fpenv.CurrentFlags())}; | |||
515 | u.f = fcheck; | |||
516 | UINT rcheck{NormalizeNaN(u.ui)}; | |||
517 | UINT check = quot.value.RawBits().ToUInt64(); | |||
518 | MATCH(rcheck, check)testing::Match("flang/unittests/Evaluate/real.cpp", 518, (rcheck ), "check", (check)) | |||
519 | ("%d 0x%jx / 0x%jx", pass, static_cast<std::intmax_t>(rj), | |||
520 | static_cast<std::intmax_t>(rk)); | |||
521 | MATCH(actualFlags, FlagsToBits(quot.flags))testing::Match("flang/unittests/Evaluate/real.cpp", 521, (actualFlags ), "FlagsToBits(quot.flags)", (FlagsToBits(quot.flags))) | |||
522 | ("%d 0x%jx / 0x%jx", pass, static_cast<std::intmax_t>(rj), | |||
523 | static_cast<std::intmax_t>(rk)); | |||
524 | } | |||
525 | } | |||
526 | } | |||
527 | } | |||
528 | ||||
529 | void roundTest(int rm, Rounding rounding, std::uint32_t opds) { | |||
530 | basicTests<Real2>(rm, rounding); | |||
531 | basicTests<Real3>(rm, rounding); | |||
532 | basicTests<Real4>(rm, rounding); | |||
533 | basicTests<Real8>(rm, rounding); | |||
534 | basicTests<Real10>(rm, rounding); | |||
535 | basicTests<Real16>(rm, rounding); | |||
536 | ScopedHostFloatingPointEnvironment::SetRounding(rounding); | |||
537 | subsetTests<std::uint32_t, float, Real4>(rm, rounding, opds); | |||
538 | subsetTests<std::uint64_t, double, Real8>(rm, rounding, opds); | |||
539 | } | |||
540 | ||||
541 | int main() { | |||
542 | dumpTest(); | |||
543 | std::uint32_t opds{512}; // for quick testing by default | |||
544 | if (const char *p{std::getenv("REAL_TEST_OPERANDS")}) { | |||
545 | // Use 8192 or 16384 for more exhaustive testing. | |||
546 | opds = std::atol(p); | |||
547 | } | |||
548 | roundTest(0, Rounding{RoundingMode::TiesToEven}, opds); | |||
549 | roundTest(1, Rounding{RoundingMode::ToZero}, opds); | |||
550 | roundTest(2, Rounding{RoundingMode::Up}, opds); | |||
551 | roundTest(3, Rounding{RoundingMode::Down}, opds); | |||
552 | // TODO: how to test Rounding::TiesAwayFromZero on x86? | |||
553 | return testing::Complete(); | |||
554 | } |
1 | //===-- include/flang/Evaluate/real.h ---------------------------*- C++ -*-===// | ||||||
2 | // | ||||||
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||||||
4 | // See https://llvm.org/LICENSE.txt for license information. | ||||||
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||||||
6 | // | ||||||
7 | //===----------------------------------------------------------------------===// | ||||||
8 | |||||||
9 | #ifndef FORTRAN_EVALUATE_REAL_H_ | ||||||
10 | #define FORTRAN_EVALUATE_REAL_H_ | ||||||
11 | |||||||
12 | #include "formatting.h" | ||||||
13 | #include "integer.h" | ||||||
14 | #include "rounding-bits.h" | ||||||
15 | #include "flang/Common/real.h" | ||||||
16 | #include "flang/Evaluate/target.h" | ||||||
17 | #include <cinttypes> | ||||||
18 | #include <limits> | ||||||
19 | #include <string> | ||||||
20 | |||||||
21 | // Some environments, viz. clang on Darwin, allow the macro HUGE | ||||||
22 | // to leak out of <math.h> even when it is never directly included. | ||||||
23 | #undef HUGE | ||||||
24 | |||||||
25 | namespace llvm { | ||||||
26 | class raw_ostream; | ||||||
27 | } | ||||||
28 | namespace Fortran::evaluate::value { | ||||||
29 | |||||||
30 | // LOG10(2.)*1E12 | ||||||
31 | static constexpr std::int64_t ScaledLogBaseTenOfTwo{301029995664}; | ||||||
32 | |||||||
33 | // Models IEEE binary floating-point numbers (IEEE 754-2008, | ||||||
34 | // ISO/IEC/IEEE 60559.2011). The first argument to this | ||||||
35 | // class template must be (or look like) an instance of Integer<>; | ||||||
36 | // the second specifies the number of effective bits (binary precision) | ||||||
37 | // in the fraction. | ||||||
38 | template <typename WORD, int PREC> | ||||||
39 | class Real : public common::RealDetails<PREC> { | ||||||
40 | public: | ||||||
41 | using Word = WORD; | ||||||
42 | static constexpr int binaryPrecision{PREC}; | ||||||
43 | using Details = common::RealDetails<PREC>; | ||||||
44 | using Details::exponentBias; | ||||||
45 | using Details::exponentBits; | ||||||
46 | using Details::isImplicitMSB; | ||||||
47 | using Details::maxExponent; | ||||||
48 | using Details::significandBits; | ||||||
49 | |||||||
50 | static constexpr int bits{Word::bits}; | ||||||
51 | static_assert(bits >= Details::bits); | ||||||
52 | using Fraction = Integer<binaryPrecision>; // all bits made explicit | ||||||
53 | |||||||
54 | template <typename W, int P> friend class Real; | ||||||
55 | |||||||
56 | constexpr Real() {} // +0.0 | ||||||
57 | constexpr Real(const Real &) = default; | ||||||
58 | constexpr Real(Real &&) = default; | ||||||
59 | constexpr Real(const Word &bits) : word_{bits} {} | ||||||
60 | constexpr Real &operator=(const Real &) = default; | ||||||
61 | constexpr Real &operator=(Real &&) = default; | ||||||
62 | |||||||
63 | constexpr bool operator==(const Real &that) const { | ||||||
64 | return word_ == that.word_; | ||||||
65 | } | ||||||
66 | |||||||
67 | constexpr bool IsSignBitSet() const { return word_.BTEST(bits - 1); } | ||||||
68 | constexpr bool IsNegative() const { | ||||||
69 | return !IsNotANumber() && IsSignBitSet(); | ||||||
70 | } | ||||||
71 | constexpr bool IsNotANumber() const { | ||||||
72 | return Exponent() == maxExponent && !GetSignificand().IsZero(); | ||||||
73 | } | ||||||
74 | constexpr bool IsQuietNaN() const { | ||||||
75 | return Exponent() == maxExponent && | ||||||
76 | GetSignificand().BTEST(significandBits - 1); | ||||||
77 | } | ||||||
78 | constexpr bool IsSignalingNaN() const { | ||||||
79 | return IsNotANumber() && !GetSignificand().BTEST(significandBits - 1); | ||||||
80 | } | ||||||
81 | constexpr bool IsInfinite() const { | ||||||
82 | return Exponent() == maxExponent && GetSignificand().IsZero(); | ||||||
83 | } | ||||||
84 | constexpr bool IsFinite() const { return Exponent() != maxExponent; } | ||||||
85 | constexpr bool IsZero() const { | ||||||
86 | return Exponent() == 0 && GetSignificand().IsZero(); | ||||||
87 | } | ||||||
88 | constexpr bool IsSubnormal() const { | ||||||
89 | return Exponent() == 0 && !GetSignificand().IsZero(); | ||||||
90 | } | ||||||
91 | constexpr bool IsNormal() const { | ||||||
92 | return !(IsInfinite() || IsNotANumber() || IsSubnormal()); | ||||||
93 | } | ||||||
94 | |||||||
95 | constexpr Real ABS() const { // non-arithmetic, no flags returned | ||||||
96 | return {word_.IBCLR(bits - 1)}; | ||||||
97 | } | ||||||
98 | constexpr Real SetSign(bool toNegative) const { // non-arithmetic | ||||||
99 | if (toNegative) { | ||||||
100 | return {word_.IBSET(bits - 1)}; | ||||||
101 | } else { | ||||||
102 | return ABS(); | ||||||
103 | } | ||||||
104 | } | ||||||
105 | constexpr Real SIGN(const Real &x) const { return SetSign(x.IsSignBitSet()); } | ||||||
106 | |||||||
107 | constexpr Real Negate() const { return {word_.IEOR(word_.MASKL(1))}; } | ||||||
108 | |||||||
109 | Relation Compare(const Real &) const; | ||||||
110 | ValueWithRealFlags<Real> Add(const Real &, | ||||||
111 | Rounding rounding = TargetCharacteristics::defaultRounding) const; | ||||||
112 | ValueWithRealFlags<Real> Subtract(const Real &y, | ||||||
113 | Rounding rounding = TargetCharacteristics::defaultRounding) const { | ||||||
114 | return Add(y.Negate(), rounding); | ||||||
115 | } | ||||||
116 | ValueWithRealFlags<Real> Multiply(const Real &, | ||||||
117 | Rounding rounding = TargetCharacteristics::defaultRounding) const; | ||||||
118 | ValueWithRealFlags<Real> Divide(const Real &, | ||||||
119 | Rounding rounding = TargetCharacteristics::defaultRounding) const; | ||||||
120 | |||||||
121 | ValueWithRealFlags<Real> SQRT( | ||||||
122 | Rounding rounding = TargetCharacteristics::defaultRounding) const; | ||||||
123 | // NEAREST(), IEEE_NEXT_AFTER(), IEEE_NEXT_UP(), and IEEE_NEXT_DOWN() | ||||||
124 | ValueWithRealFlags<Real> NEAREST(bool upward) const; | ||||||
125 | // HYPOT(x,y)=SQRT(x**2 + y**2) computed so as to avoid spurious | ||||||
126 | // intermediate overflows. | ||||||
127 | ValueWithRealFlags<Real> HYPOT(const Real &, | ||||||
128 | Rounding rounding = TargetCharacteristics::defaultRounding) const; | ||||||
129 | // DIM(X,Y) = MAX(X-Y, 0) | ||||||
130 | ValueWithRealFlags<Real> DIM(const Real &, | ||||||
131 | Rounding rounding = TargetCharacteristics::defaultRounding) const; | ||||||
132 | // MOD(x,y) = x - AINT(x/y)*y | ||||||
133 | // MODULO(x,y) = x - FLOOR(x/y)*y | ||||||
134 | ValueWithRealFlags<Real> MOD(const Real &, | ||||||
135 | Rounding rounding = TargetCharacteristics::defaultRounding) const; | ||||||
136 | ValueWithRealFlags<Real> MODULO(const Real &, | ||||||
137 | Rounding rounding = TargetCharacteristics::defaultRounding) const; | ||||||
138 | |||||||
139 | template <typename INT> constexpr INT EXPONENT() const { | ||||||
140 | if (Exponent() == maxExponent) { | ||||||
141 | return INT::HUGE(); | ||||||
142 | } else if (IsZero()) { | ||||||
143 | return {0}; | ||||||
144 | } else { | ||||||
145 | return {UnbiasedExponent() + 1}; | ||||||
146 | } | ||||||
147 | } | ||||||
148 | |||||||
149 | static constexpr Real EPSILON() { | ||||||
150 | Real epsilon; | ||||||
151 | epsilon.Normalize( | ||||||
152 | false, exponentBias + 1 - binaryPrecision, Fraction::MASKL(1)); | ||||||
153 | return epsilon; | ||||||
154 | } | ||||||
155 | static constexpr Real HUGE() { | ||||||
156 | Real huge; | ||||||
157 | huge.Normalize(false, maxExponent - 1, Fraction::MASKR(binaryPrecision)); | ||||||
158 | return huge; | ||||||
159 | } | ||||||
160 | static constexpr Real TINY() { | ||||||
161 | Real tiny; | ||||||
162 | tiny.Normalize(false, 1, Fraction::MASKL(1)); // minimum *normal* number | ||||||
163 | return tiny; | ||||||
164 | } | ||||||
165 | |||||||
166 | static constexpr int DIGITS{binaryPrecision}; | ||||||
167 | static constexpr int PRECISION{Details::decimalPrecision}; | ||||||
168 | static constexpr int RANGE{Details::decimalRange}; | ||||||
169 | static constexpr int MAXEXPONENT{maxExponent - exponentBias}; | ||||||
170 | static constexpr int MINEXPONENT{2 - exponentBias}; | ||||||
171 | Real RRSPACING() const; | ||||||
172 | Real SPACING() const; | ||||||
173 | Real SET_EXPONENT(std::int64_t) const; | ||||||
174 | Real FRACTION() const; | ||||||
175 | |||||||
176 | // SCALE(); also known as IEEE_SCALB and (in IEEE-754 '08) ScaleB. | ||||||
177 | template <typename INT> | ||||||
178 | ValueWithRealFlags<Real> SCALE(const INT &by, | ||||||
179 | Rounding rounding = TargetCharacteristics::defaultRounding) const { | ||||||
180 | // Normalize a fraction with just its LSB set and then multiply. | ||||||
181 | // (Set the LSB, not the MSB, in case the scale factor needs to | ||||||
182 | // be subnormal.) | ||||||
183 | auto adjust{exponentBias + binaryPrecision - 1}; | ||||||
184 | auto expo{adjust + by.ToInt64()}; | ||||||
185 | Real twoPow; | ||||||
186 | RealFlags flags; | ||||||
187 | int rMask{1}; | ||||||
188 | if (IsZero()) { | ||||||
189 | expo = exponentBias; // ignore by, don't overflow | ||||||
190 | } else if (by > INT{maxExponent}) { | ||||||
191 | expo = maxExponent + binaryPrecision - 1; | ||||||
192 | } else if (by < INT{-adjust}) { // underflow | ||||||
193 | expo = 0; | ||||||
194 | rMask = 0; | ||||||
195 | flags.set(RealFlag::Underflow); | ||||||
196 | } | ||||||
197 | flags |= | ||||||
198 | twoPow.Normalize(false, static_cast<int>(expo), Fraction::MASKR(rMask)); | ||||||
199 | ValueWithRealFlags<Real> result{Multiply(twoPow, rounding)}; | ||||||
200 | result.flags |= flags; | ||||||
201 | return result; | ||||||
202 | } | ||||||
203 | |||||||
204 | constexpr Real FlushSubnormalToZero() const { | ||||||
205 | if (IsSubnormal()) { | ||||||
206 | return Real{}; | ||||||
207 | } | ||||||
208 | return *this; | ||||||
209 | } | ||||||
210 | |||||||
211 | // TODO: Configurable NotANumber representations | ||||||
212 | static constexpr Real NotANumber() { | ||||||
213 | return {Word{maxExponent} | ||||||
214 | .SHIFTL(significandBits) | ||||||
215 | .IBSET(significandBits - 1) | ||||||
216 | .IBSET(significandBits - 2)}; | ||||||
217 | } | ||||||
218 | |||||||
219 | static constexpr Real PositiveZero() { return Real{}; } | ||||||
220 | |||||||
221 | static constexpr Real NegativeZero() { return {Word{}.MASKL(1)}; } | ||||||
222 | |||||||
223 | static constexpr Real Infinity(bool negative) { | ||||||
224 | Word infinity{maxExponent}; | ||||||
225 | infinity = infinity.SHIFTL(significandBits); | ||||||
226 | if (negative) { | ||||||
227 | infinity = infinity.IBSET(infinity.bits - 1); | ||||||
228 | } | ||||||
229 | return {infinity}; | ||||||
230 | } | ||||||
231 | |||||||
232 | template <typename INT> | ||||||
233 | static ValueWithRealFlags<Real> FromInteger(const INT &n, | ||||||
234 | Rounding rounding = TargetCharacteristics::defaultRounding) { | ||||||
235 | bool isNegative{n.IsNegative()}; | ||||||
236 | INT absN{n}; | ||||||
237 | if (isNegative) { | ||||||
238 | absN = n.Negate().value; // overflow is safe to ignore | ||||||
239 | } | ||||||
240 | int leadz{absN.LEADZ()}; | ||||||
241 | if (leadz >= absN.bits) { | ||||||
242 | return {}; // all bits zero -> +0.0 | ||||||
243 | } | ||||||
244 | ValueWithRealFlags<Real> result; | ||||||
245 | int exponent{exponentBias + absN.bits - leadz - 1}; | ||||||
246 | int bitsNeeded{absN.bits - (leadz + isImplicitMSB)}; | ||||||
247 | int bitsLost{bitsNeeded - significandBits}; | ||||||
248 | if (bitsLost <= 0) { | ||||||
249 | Fraction fraction{Fraction::ConvertUnsigned(absN).value}; | ||||||
250 | result.flags |= result.value.Normalize( | ||||||
251 | isNegative, exponent, fraction.SHIFTL(-bitsLost)); | ||||||
252 | } else { | ||||||
253 | Fraction fraction{Fraction::ConvertUnsigned(absN.SHIFTR(bitsLost)).value}; | ||||||
254 | result.flags |= result.value.Normalize(isNegative, exponent, fraction); | ||||||
255 | RoundingBits roundingBits{absN, bitsLost}; | ||||||
256 | result.flags |= result.value.Round(rounding, roundingBits); | ||||||
257 | } | ||||||
258 | return result; | ||||||
259 | } | ||||||
260 | |||||||
261 | // Conversion to integer in the same real format (AINT(), ANINT()) | ||||||
262 | ValueWithRealFlags<Real> ToWholeNumber( | ||||||
263 | common::RoundingMode = common::RoundingMode::ToZero) const; | ||||||
264 | |||||||
265 | // Conversion to an integer (INT(), NINT(), FLOOR(), CEILING()) | ||||||
266 | template <typename INT> | ||||||
267 | constexpr ValueWithRealFlags<INT> ToInteger( | ||||||
268 | common::RoundingMode mode = common::RoundingMode::ToZero) const { | ||||||
269 | ValueWithRealFlags<INT> result; | ||||||
270 | if (IsNotANumber()) { | ||||||
271 | result.flags.set(RealFlag::InvalidArgument); | ||||||
272 | result.value = result.value.HUGE(); | ||||||
273 | return result; | ||||||
274 | } | ||||||
275 | ValueWithRealFlags<Real> intPart{ToWholeNumber(mode)}; | ||||||
276 | result.flags |= intPart.flags; | ||||||
277 | int exponent{intPart.value.Exponent()}; | ||||||
278 | // shift positive -> left shift, negative -> right shift | ||||||
279 | int shift{exponent - exponentBias - binaryPrecision + 1}; | ||||||
280 | // Apply any right shift before moving to the result type | ||||||
281 | auto rshifted{intPart.value.GetFraction().SHIFTR(-shift)}; | ||||||
282 | auto converted{result.value.ConvertUnsigned(rshifted)}; | ||||||
283 | if (converted.overflow
| ||||||
284 | result.flags.set(RealFlag::Overflow); | ||||||
285 | } | ||||||
286 | result.value = converted.value.SHIFTL(shift); | ||||||
287 | if (converted.value.CompareUnsigned(result.value.SHIFTR(shift)) != | ||||||
288 | Ordering::Equal) { | ||||||
289 | result.flags.set(RealFlag::Overflow); | ||||||
290 | } | ||||||
291 | if (IsSignBitSet()) { | ||||||
292 | result.value = result.value.Negate().value; | ||||||
293 | } | ||||||
294 | if (!result.value.IsZero()) { | ||||||
295 | if (IsSignBitSet() != result.value.IsNegative()) { | ||||||
296 | result.flags.set(RealFlag::Overflow); | ||||||
297 | } | ||||||
298 | } | ||||||
299 | if (result.flags.test(RealFlag::Overflow)) { | ||||||
300 | result.value = | ||||||
301 | IsSignBitSet() ? result.value.MASKL(1) : result.value.HUGE(); | ||||||
302 | } | ||||||
303 | return result; | ||||||
304 | } | ||||||
305 | |||||||
306 | template <typename A> | ||||||
307 | static ValueWithRealFlags<Real> Convert( | ||||||
308 | const A &x, Rounding rounding = TargetCharacteristics::defaultRounding) { | ||||||
309 | ValueWithRealFlags<Real> result; | ||||||
310 | if (x.IsNotANumber()) { | ||||||
311 | result.flags.set(RealFlag::InvalidArgument); | ||||||
312 | result.value = NotANumber(); | ||||||
313 | return result; | ||||||
314 | } | ||||||
315 | bool isNegative{x.IsNegative()}; | ||||||
316 | A absX{x}; | ||||||
317 | if (isNegative) { | ||||||
318 | absX = x.Negate(); | ||||||
319 | } | ||||||
320 | int exponent{exponentBias + x.UnbiasedExponent()}; | ||||||
321 | int bitsLost{A::binaryPrecision - binaryPrecision}; | ||||||
322 | if (exponent < 1) { | ||||||
323 | bitsLost += 1 - exponent; | ||||||
324 | exponent = 1; | ||||||
325 | } | ||||||
326 | typename A::Fraction xFraction{x.GetFraction()}; | ||||||
327 | if (bitsLost <= 0) { | ||||||
328 | Fraction fraction{ | ||||||
329 | Fraction::ConvertUnsigned(xFraction).value.SHIFTL(-bitsLost)}; | ||||||
330 | result.flags |= result.value.Normalize(isNegative, exponent, fraction); | ||||||
331 | } else { | ||||||
332 | Fraction fraction{ | ||||||
333 | Fraction::ConvertUnsigned(xFraction.SHIFTR(bitsLost)).value}; | ||||||
334 | result.flags |= result.value.Normalize(isNegative, exponent, fraction); | ||||||
335 | RoundingBits roundingBits{xFraction, bitsLost}; | ||||||
336 | result.flags |= result.value.Round(rounding, roundingBits); | ||||||
337 | } | ||||||
338 | return result; | ||||||
339 | } | ||||||
340 | |||||||
341 | constexpr Word RawBits() const { return word_; } | ||||||
342 | |||||||
343 | // Extracts "raw" biased exponent field. | ||||||
344 | constexpr int Exponent() const { | ||||||
345 | return word_.IBITS(significandBits, exponentBits).ToUInt64(); | ||||||
346 | } | ||||||
347 | |||||||
348 | // Extracts the fraction; any implied bit is made explicit. | ||||||
349 | constexpr Fraction GetFraction() const { | ||||||
350 | Fraction result{Fraction::ConvertUnsigned(word_).value}; | ||||||
351 | if constexpr (!isImplicitMSB) { | ||||||
352 | return result; | ||||||
353 | } else { | ||||||
354 | int exponent{Exponent()}; | ||||||
355 | if (exponent > 0 && exponent < maxExponent) { | ||||||
356 | return result.IBSET(significandBits); | ||||||
357 | } else { | ||||||
358 | return result.IBCLR(significandBits); | ||||||
359 | } | ||||||
360 | } | ||||||
361 | } | ||||||
362 | |||||||
363 | // Extracts unbiased exponent value. | ||||||
364 | // Corrects the exponent value of a subnormal number. | ||||||
365 | // Note that the result is one less than the EXPONENT intrinsic; | ||||||
366 | // UnbiasedExponent(1.0) is 0, not 1. | ||||||
367 | constexpr int UnbiasedExponent() const { | ||||||
368 | int exponent{Exponent() - exponentBias}; | ||||||
369 | if (IsSubnormal()) { | ||||||
370 | ++exponent; | ||||||
371 | } | ||||||
372 | return exponent; | ||||||
373 | } | ||||||
374 | |||||||
375 | static ValueWithRealFlags<Real> Read(const char *&, | ||||||
376 | Rounding rounding = TargetCharacteristics::defaultRounding); | ||||||
377 | std::string DumpHexadecimal() const; | ||||||
378 | |||||||
379 | // Emits a character representation for an equivalent Fortran constant | ||||||
380 | // or parenthesized constant expression that produces this value. | ||||||
381 | llvm::raw_ostream &AsFortran( | ||||||
382 | llvm::raw_ostream &, int kind, bool minimal = false) const; | ||||||
383 | |||||||
384 | private: | ||||||
385 | using Significand = Integer<significandBits>; // no implicit bit | ||||||
386 | |||||||
387 | constexpr Significand GetSignificand() const { | ||||||
388 | return Significand::ConvertUnsigned(word_).value; | ||||||
389 | } | ||||||
390 | |||||||
391 | constexpr int CombineExponents(const Real &y, bool forDivide) const { | ||||||
392 | int exponent = Exponent(), yExponent = y.Exponent(); | ||||||
393 | // A zero exponent field value has the same weight as 1. | ||||||
394 | exponent += !exponent; | ||||||
395 | yExponent += !yExponent; | ||||||
396 | if (forDivide) { | ||||||
397 | exponent += exponentBias - yExponent; | ||||||
398 | } else { | ||||||
399 | exponent += yExponent - exponentBias + 1; | ||||||
400 | } | ||||||
401 | return exponent; | ||||||
402 | } | ||||||
403 | |||||||
404 | static constexpr bool NextQuotientBit( | ||||||
405 | Fraction &top, bool &msb, const Fraction &divisor) { | ||||||
406 | bool greaterOrEqual{msb || top.CompareUnsigned(divisor) != Ordering::Less}; | ||||||
407 | if (greaterOrEqual) { | ||||||
408 | top = top.SubtractSigned(divisor).value; | ||||||
409 | } | ||||||
410 | auto doubled{top.AddUnsigned(top)}; | ||||||
411 | top = doubled.value; | ||||||
412 | msb = doubled.carry; | ||||||
413 | return greaterOrEqual; | ||||||
414 | } | ||||||
415 | |||||||
416 | // Normalizes and marshals the fields of a floating-point number in place. | ||||||
417 | // The value is a number, and a zero fraction means a zero value (i.e., | ||||||
418 | // a maximal exponent and zero fraction doesn't signify infinity, although | ||||||
419 | // this member function will detect overflow and encode infinities). | ||||||
420 | RealFlags Normalize(bool negative, int exponent, const Fraction &fraction, | ||||||
421 | Rounding rounding = TargetCharacteristics::defaultRounding, | ||||||
422 | RoundingBits *roundingBits = nullptr); | ||||||
423 | |||||||
424 | // Rounds a result, if necessary, in place. | ||||||
425 | RealFlags Round(Rounding, const RoundingBits &, bool multiply = false); | ||||||
426 | |||||||
427 | static void NormalizeAndRound(ValueWithRealFlags<Real> &result, | ||||||
428 | bool isNegative, int exponent, const Fraction &, Rounding, RoundingBits, | ||||||
429 | bool multiply = false); | ||||||
430 | |||||||
431 | Word word_{}; // an Integer<> | ||||||
432 | }; | ||||||
433 | |||||||
434 | extern template class Real<Integer<16>, 11>; // IEEE half format | ||||||
435 | extern template class Real<Integer<16>, 8>; // the "other" half format | ||||||
436 | extern template class Real<Integer<32>, 24>; // IEEE single | ||||||
437 | extern template class Real<Integer<64>, 53>; // IEEE double | ||||||
438 | extern template class Real<Integer<80>, 64>; // 80387 extended precision | ||||||
439 | extern template class Real<Integer<128>, 113>; // IEEE quad | ||||||
440 | // N.B. No "double-double" support. | ||||||
441 | } // namespace Fortran::evaluate::value | ||||||
442 | #endif // FORTRAN_EVALUATE_REAL_H_ |
1 | //===-- include/flang/Evaluate/integer.h ------------------------*- C++ -*-===// | ||||||
2 | // | ||||||
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||||||
4 | // See https://llvm.org/LICENSE.txt for license information. | ||||||
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||||||
6 | // | ||||||
7 | //===----------------------------------------------------------------------===// | ||||||
8 | |||||||
9 | #ifndef FORTRAN_EVALUATE_INTEGER_H_ | ||||||
10 | #define FORTRAN_EVALUATE_INTEGER_H_ | ||||||
11 | |||||||
12 | // Emulates binary integers of an arbitrary (but fixed) bit size for use | ||||||
13 | // when the host C++ environment does not support that size or when the | ||||||
14 | // full suite of Fortran's integer intrinsic scalar functions are needed. | ||||||
15 | // The data model is typeless, so signed* and unsigned operations | ||||||
16 | // are distinguished from each other with distinct member function interfaces. | ||||||
17 | // (*"Signed" here means two's-complement, just to be clear. Ones'-complement | ||||||
18 | // and signed-magnitude encodings appear to be extinct in 2018.) | ||||||
19 | |||||||
20 | #include "flang/Common/bit-population-count.h" | ||||||
21 | #include "flang/Common/leading-zero-bit-count.h" | ||||||
22 | #include "flang/Evaluate/common.h" | ||||||
23 | #include <cinttypes> | ||||||
24 | #include <climits> | ||||||
25 | #include <cstddef> | ||||||
26 | #include <cstdint> | ||||||
27 | #include <string> | ||||||
28 | #include <type_traits> | ||||||
29 | |||||||
30 | // Some environments, viz. clang on Darwin, allow the macro HUGE | ||||||
31 | // to leak out of <math.h> even when it is never directly included. | ||||||
32 | #undef HUGE | ||||||
33 | |||||||
34 | namespace Fortran::evaluate::value { | ||||||
35 | |||||||
36 | // Implements an integer as an assembly of smaller host integer parts | ||||||
37 | // that constitute the digits of a large-radix fixed-point number. | ||||||
38 | // For best performance, the type of these parts should be half of the | ||||||
39 | // size of the largest efficient integer supported by the host processor. | ||||||
40 | // These parts are stored in either little- or big-endian order, which can | ||||||
41 | // match that of the host's endianness or not; but if the ordering matches | ||||||
42 | // that of the host, raw host data can be overlaid with a properly configured | ||||||
43 | // instance of this class and used in situ. | ||||||
44 | // To facilitate exhaustive testing of what would otherwise be more rare | ||||||
45 | // edge cases, this class template may be configured to use other part | ||||||
46 | // types &/or partial fields in the parts. The radix (i.e., the number | ||||||
47 | // of possible values in a part), however, must be a power of two; this | ||||||
48 | // template class is not generalized to enable, say, decimal arithmetic. | ||||||
49 | // Member functions that correspond to Fortran intrinsic functions are | ||||||
50 | // named accordingly in ALL CAPS so that they can be referenced easily in | ||||||
51 | // the language standard. | ||||||
52 | template <int BITS, bool IS_LITTLE_ENDIAN = isHostLittleEndian, | ||||||
53 | int PARTBITS = BITS <= 32 ? BITS : 32, | ||||||
54 | typename PART = HostUnsignedInt<PARTBITS>, | ||||||
55 | typename BIGPART = HostUnsignedInt<PARTBITS * 2>> | ||||||
56 | class Integer { | ||||||
57 | public: | ||||||
58 | static constexpr int bits{BITS}; | ||||||
59 | static constexpr int partBits{PARTBITS}; | ||||||
60 | using Part = PART; | ||||||
61 | using BigPart = BIGPART; | ||||||
62 | static_assert(std::is_integral_v<Part>); | ||||||
63 | static_assert(std::is_unsigned_v<Part>); | ||||||
64 | static_assert(std::is_integral_v<BigPart>); | ||||||
65 | static_assert(std::is_unsigned_v<BigPart>); | ||||||
66 | static_assert(CHAR_BIT8 * sizeof(BigPart) >= 2 * partBits); | ||||||
67 | static constexpr bool littleEndian{IS_LITTLE_ENDIAN}; | ||||||
68 | |||||||
69 | private: | ||||||
70 | static constexpr int maxPartBits{CHAR_BIT8 * sizeof(Part)}; | ||||||
71 | static_assert(partBits > 0 && partBits <= maxPartBits); | ||||||
72 | static constexpr int extraPartBits{maxPartBits - partBits}; | ||||||
73 | static constexpr int parts{(bits + partBits - 1) / partBits}; | ||||||
74 | static_assert(parts >= 1); | ||||||
75 | static constexpr int extraTopPartBits{ | ||||||
76 | extraPartBits + (parts * partBits) - bits}; | ||||||
77 | static constexpr int topPartBits{maxPartBits - extraTopPartBits}; | ||||||
78 | static_assert(topPartBits > 0 && topPartBits <= partBits); | ||||||
79 | static_assert((parts - 1) * partBits + topPartBits == bits); | ||||||
80 | static constexpr Part partMask{static_cast<Part>(~0) >> extraPartBits}; | ||||||
81 | static constexpr Part topPartMask{static_cast<Part>(~0) >> extraTopPartBits}; | ||||||
82 | |||||||
83 | public: | ||||||
84 | // Some types used for member function results | ||||||
85 | struct ValueWithOverflow { | ||||||
86 | Integer value; | ||||||
87 | bool overflow; | ||||||
88 | }; | ||||||
89 | |||||||
90 | struct ValueWithCarry { | ||||||
91 | Integer value; | ||||||
92 | bool carry; | ||||||
93 | }; | ||||||
94 | |||||||
95 | struct Product { | ||||||
96 | bool SignedMultiplicationOverflowed() const { | ||||||
97 | return lower.IsNegative() ? (upper.POPCNT() != bits) : !upper.IsZero(); | ||||||
98 | } | ||||||
99 | Integer upper, lower; | ||||||
100 | }; | ||||||
101 | |||||||
102 | struct QuotientWithRemainder { | ||||||
103 | Integer quotient, remainder; | ||||||
104 | bool divisionByZero, overflow; | ||||||
105 | }; | ||||||
106 | |||||||
107 | struct PowerWithErrors { | ||||||
108 | Integer power; | ||||||
109 | bool divisionByZero{false}, overflow{false}, zeroToZero{false}; | ||||||
110 | }; | ||||||
111 | |||||||
112 | // Constructors and value-generating static functions | ||||||
113 | constexpr Integer() { Clear(); } // default constructor: zero | ||||||
114 | constexpr Integer(const Integer &) = default; | ||||||
115 | constexpr Integer(Integer &&) = default; | ||||||
116 | |||||||
117 | // C++'s integral types can all be converted to Integer | ||||||
118 | // with silent truncation. | ||||||
119 | template <typename INT, typename = std::enable_if_t<std::is_integral_v<INT>>> | ||||||
120 | constexpr Integer(INT n) { | ||||||
121 | constexpr int nBits = CHAR_BIT8 * sizeof n; | ||||||
122 | if constexpr (nBits < partBits) { | ||||||
123 | if constexpr (std::is_unsigned_v<INT>) { | ||||||
124 | // Zero-extend an unsigned smaller value. | ||||||
125 | SetLEPart(0, n); | ||||||
126 | for (int j{1}; j < parts; ++j) { | ||||||
127 | SetLEPart(j, 0); | ||||||
128 | } | ||||||
129 | } else { | ||||||
130 | // n has a signed type smaller than the usable | ||||||
131 | // bits in a Part. | ||||||
132 | // Avoid conversions that change both size and sign. | ||||||
133 | using SignedPart = std::make_signed_t<Part>; | ||||||
134 | Part p = static_cast<SignedPart>(n); | ||||||
135 | SetLEPart(0, p); | ||||||
136 | if constexpr (parts > 1) { | ||||||
137 | Part signExtension = static_cast<SignedPart>(-(n < 0)); | ||||||
138 | for (int j{1}; j < parts; ++j) { | ||||||
139 | SetLEPart(j, signExtension); | ||||||
140 | } | ||||||
141 | } | ||||||
142 | } | ||||||
143 | } else { | ||||||
144 | // n has some integral type no smaller than the usable | ||||||
145 | // bits in a Part. | ||||||
146 | // Ensure that all shifts are smaller than a whole word. | ||||||
147 | if constexpr (std::is_unsigned_v<INT>) { | ||||||
148 | for (int j{0}; j < parts; ++j) { | ||||||
149 | SetLEPart(j, static_cast<Part>(n)); | ||||||
150 | if constexpr (nBits > partBits) { | ||||||
151 | n >>= partBits; | ||||||
152 | } else { | ||||||
153 | n = 0; | ||||||
154 | } | ||||||
155 | } | ||||||
156 | } else { | ||||||
157 | INT signExtension{-(n < 0)}; | ||||||
158 | static_assert(nBits >= partBits); | ||||||
159 | if constexpr (nBits > partBits) { | ||||||
160 | signExtension <<= nBits - partBits; | ||||||
161 | for (int j{0}; j < parts; ++j) { | ||||||
162 | SetLEPart(j, static_cast<Part>(n)); | ||||||
163 | n >>= partBits; | ||||||
164 | n |= signExtension; | ||||||
165 | } | ||||||
166 | } else { | ||||||
167 | SetLEPart(0, static_cast<Part>(n)); | ||||||
168 | for (int j{1}; j < parts; ++j) { | ||||||
169 | SetLEPart(j, static_cast<Part>(signExtension)); | ||||||
170 | } | ||||||
171 | } | ||||||
172 | } | ||||||
173 | } | ||||||
174 | } | ||||||
175 | |||||||
176 | constexpr Integer &operator=(const Integer &) = default; | ||||||
177 | |||||||
178 | constexpr bool operator<(const Integer &that) const { | ||||||
179 | return CompareSigned(that) == Ordering::Less; | ||||||
180 | } | ||||||
181 | constexpr bool operator<=(const Integer &that) const { | ||||||
182 | return CompareSigned(that) != Ordering::Greater; | ||||||
183 | } | ||||||
184 | constexpr bool operator==(const Integer &that) const { | ||||||
185 | return CompareSigned(that) == Ordering::Equal; | ||||||
186 | } | ||||||
187 | constexpr bool operator!=(const Integer &that) const { | ||||||
188 | return !(*this == that); | ||||||
189 | } | ||||||
190 | constexpr bool operator>=(const Integer &that) const { | ||||||
191 | return CompareSigned(that) != Ordering::Less; | ||||||
192 | } | ||||||
193 | constexpr bool operator>(const Integer &that) const { | ||||||
194 | return CompareSigned(that) == Ordering::Greater; | ||||||
195 | } | ||||||
196 | |||||||
197 | // Left-justified mask (e.g., MASKL(1) has only its sign bit set) | ||||||
198 | static constexpr Integer MASKL(int places) { | ||||||
199 | if (places <= 0) { | ||||||
200 | return {}; | ||||||
201 | } else if (places >= bits) { | ||||||
202 | return MASKR(bits); | ||||||
203 | } else { | ||||||
204 | return MASKR(bits - places).NOT(); | ||||||
205 | } | ||||||
206 | } | ||||||
207 | |||||||
208 | // Right-justified mask (e.g., MASKR(1) == 1, MASKR(2) == 3, &c.) | ||||||
209 | static constexpr Integer MASKR(int places) { | ||||||
210 | Integer result{nullptr}; | ||||||
211 | int j{0}; | ||||||
212 | for (; j + 1 < parts && places >= partBits; ++j, places -= partBits) { | ||||||
213 | result.LEPart(j) = partMask; | ||||||
214 | } | ||||||
215 | if (places > 0) { | ||||||
216 | if (j + 1 < parts) { | ||||||
217 | result.LEPart(j++) = partMask >> (partBits - places); | ||||||
218 | } else if (j + 1 == parts) { | ||||||
219 | if (places >= topPartBits) { | ||||||
220 | result.LEPart(j++) = topPartMask; | ||||||
221 | } else { | ||||||
222 | result.LEPart(j++) = topPartMask >> (topPartBits - places); | ||||||
223 | } | ||||||
224 | } | ||||||
225 | } | ||||||
226 | for (; j < parts; ++j) { | ||||||
227 | result.LEPart(j) = 0; | ||||||
228 | } | ||||||
229 | return result; | ||||||
230 | } | ||||||
231 | |||||||
232 | static constexpr ValueWithOverflow Read( | ||||||
233 | const char *&pp, std::uint64_t base = 10, bool isSigned = false) { | ||||||
234 | Integer result; | ||||||
235 | bool overflow{false}; | ||||||
236 | const char *p{pp}; | ||||||
237 | while (*p == ' ' || *p == '\t') { | ||||||
238 | ++p; | ||||||
239 | } | ||||||
240 | bool negate{*p == '-'}; | ||||||
241 | if (negate || *p == '+') { | ||||||
242 | while (*++p == ' ' || *p == '\t') { | ||||||
243 | } | ||||||
244 | } | ||||||
245 | Integer radix{base}; | ||||||
246 | // This code makes assumptions about local contiguity in regions of the | ||||||
247 | // character set and only works up to base 36. These assumptions hold | ||||||
248 | // for all current combinations of surviving character sets (ASCII, UTF-8, | ||||||
249 | // EBCDIC) and the bases used in Fortran source and formatted I/O | ||||||
250 | // (viz., 2, 8, 10, & 16). But: management thought that a disclaimer | ||||||
251 | // might be needed here to warn future users of this code about these | ||||||
252 | // assumptions, so here you go, future programmer in some postapocalyptic | ||||||
253 | // hellscape, and best of luck with the inexorable killer robots. | ||||||
254 | for (; std::uint64_t digit = *p; ++p) { | ||||||
255 | if (digit >= '0' && digit <= '9' && digit < '0' + base) { | ||||||
256 | digit -= '0'; | ||||||
257 | } else if (base > 10 && digit >= 'A' && digit < 'A' + base - 10) { | ||||||
258 | digit -= 'A' - 10; | ||||||
259 | } else if (base > 10 && digit >= 'a' && digit < 'a' + base - 10) { | ||||||
260 | digit -= 'a' - 10; | ||||||
261 | } else { | ||||||
262 | break; | ||||||
263 | } | ||||||
264 | Product shifted{result.MultiplyUnsigned(radix)}; | ||||||
265 | overflow |= !shifted.upper.IsZero(); | ||||||
266 | ValueWithCarry next{shifted.lower.AddUnsigned(Integer{digit})}; | ||||||
267 | overflow |= next.carry; | ||||||
268 | result = next.value; | ||||||
269 | } | ||||||
270 | pp = p; | ||||||
271 | if (negate) { | ||||||
272 | result = result.Negate().value; | ||||||
273 | overflow |= isSigned && !result.IsNegative() && !result.IsZero(); | ||||||
274 | } else { | ||||||
275 | overflow |= isSigned && result.IsNegative(); | ||||||
276 | } | ||||||
277 | return {result, overflow}; | ||||||
278 | } | ||||||
279 | |||||||
280 | template <typename FROM> | ||||||
281 | static constexpr ValueWithOverflow ConvertUnsigned(const FROM &that) { | ||||||
282 | std::uint64_t field{that.ToUInt64()}; | ||||||
283 | ValueWithOverflow result{field, false}; | ||||||
284 | if constexpr (bits < 64) { | ||||||
285 | result.overflow = (field >> bits) != 0; | ||||||
286 | } | ||||||
287 | for (int j{64}; j < that.bits && !result.overflow; j += 64) { | ||||||
288 | field = that.SHIFTR(j).ToUInt64(); | ||||||
289 | if (bits <= j) { | ||||||
290 | result.overflow = field != 0; | ||||||
291 | } else { | ||||||
292 | result.value = result.value.IOR(Integer{field}.SHIFTL(j)); | ||||||
293 | if (bits < j + 64) { | ||||||
294 | result.overflow = (field >> (bits - j)) != 0; | ||||||
295 | } | ||||||
296 | } | ||||||
297 | } | ||||||
298 | return result; | ||||||
299 | } | ||||||
300 | |||||||
301 | template <typename FROM> | ||||||
302 | static constexpr ValueWithOverflow ConvertSigned(const FROM &that) { | ||||||
303 | ValueWithOverflow result{ConvertUnsigned(that)}; | ||||||
304 | if constexpr (bits > FROM::bits) { | ||||||
305 | if (that.IsNegative()) { | ||||||
306 | result.value = result.value.IOR(MASKL(bits - FROM::bits)); | ||||||
307 | } | ||||||
308 | result.overflow = false; | ||||||
309 | } else if constexpr (bits < FROM::bits) { | ||||||
310 | auto back{FROM::template ConvertSigned(result.value)}; | ||||||
311 | result.overflow = back.value.CompareUnsigned(that) != Ordering::Equal; | ||||||
312 | } | ||||||
313 | return result; | ||||||
314 | } | ||||||
315 | |||||||
316 | std::string UnsignedDecimal() const { | ||||||
317 | if constexpr (bits < 4) { | ||||||
318 | char digit = '0' + ToUInt64(); | ||||||
319 | return {digit}; | ||||||
320 | } else if (IsZero()) { | ||||||
321 | return {'0'}; | ||||||
322 | } else { | ||||||
323 | QuotientWithRemainder qr{DivideUnsigned(10)}; | ||||||
324 | char digit = '0' + qr.remainder.ToUInt64(); | ||||||
325 | if (qr.quotient.IsZero()) { | ||||||
326 | return {digit}; | ||||||
327 | } else { | ||||||
328 | return qr.quotient.UnsignedDecimal() + digit; | ||||||
329 | } | ||||||
330 | } | ||||||
331 | } | ||||||
332 | |||||||
333 | std::string SignedDecimal() const { | ||||||
334 | if (IsNegative()) { | ||||||
335 | return std::string{'-'} + Negate().value.UnsignedDecimal(); | ||||||
336 | } else { | ||||||
337 | return UnsignedDecimal(); | ||||||
338 | } | ||||||
339 | } | ||||||
340 | |||||||
341 | // Omits a leading "0x". | ||||||
342 | std::string Hexadecimal() const { | ||||||
343 | std::string result; | ||||||
344 | int digits{(bits + 3) >> 2}; | ||||||
345 | for (int j{0}; j < digits; ++j) { | ||||||
346 | int pos{(digits - 1 - j) * 4}; | ||||||
347 | char nybble = IBITS(pos, 4).ToUInt64(); | ||||||
348 | if (nybble != 0 || !result.empty() || j + 1 == digits) { | ||||||
349 | char digit = '0' + nybble; | ||||||
350 | if (digit > '9') { | ||||||
351 | digit += 'a' - ('9' + 1); | ||||||
352 | } | ||||||
353 | result += digit; | ||||||
354 | } | ||||||
355 | } | ||||||
356 | return result; | ||||||
357 | } | ||||||
358 | |||||||
359 | static constexpr int DIGITS{bits - 1}; // don't count the sign bit | ||||||
360 | static constexpr Integer HUGE() { return MASKR(bits - 1); } | ||||||
361 | static constexpr Integer Least() { return MASKL(1); } | ||||||
362 | static constexpr int RANGE{// in the sense of SELECTED_INT_KIND | ||||||
363 | // This magic value is LOG10(2.)*1E12. | ||||||
364 | static_cast<int>(((bits - 1) * 301029995664) / 1000000000000)}; | ||||||
365 | |||||||
366 | constexpr bool IsZero() const { | ||||||
367 | for (int j{0}; j < parts; ++j) { | ||||||
368 | if (part_[j] != 0) { | ||||||
369 | return false; | ||||||
370 | } | ||||||
371 | } | ||||||
372 | return true; | ||||||
373 | } | ||||||
374 | |||||||
375 | constexpr bool IsNegative() const { | ||||||
376 | return (LEPart(parts - 1) >> (topPartBits - 1)) & 1; | ||||||
377 | } | ||||||
378 | |||||||
379 | constexpr Ordering CompareToZeroSigned() const { | ||||||
380 | if (IsNegative()) { | ||||||
381 | return Ordering::Less; | ||||||
382 | } else if (IsZero()) { | ||||||
383 | return Ordering::Equal; | ||||||
384 | } else { | ||||||
385 | return Ordering::Greater; | ||||||
386 | } | ||||||
387 | } | ||||||
388 | |||||||
389 | // Count the number of contiguous most-significant bit positions | ||||||
390 | // that are clear. | ||||||
391 | constexpr int LEADZ() const { | ||||||
392 | if (LEPart(parts - 1) != 0) { | ||||||
393 | int lzbc{common::LeadingZeroBitCount(LEPart(parts - 1))}; | ||||||
394 | return lzbc - extraTopPartBits; | ||||||
395 | } | ||||||
396 | int upperZeroes{topPartBits}; | ||||||
397 | for (int j{1}; j < parts; ++j) { | ||||||
398 | if (Part p{LEPart(parts - 1 - j)}) { | ||||||
399 | int lzbc{common::LeadingZeroBitCount(p)}; | ||||||
400 | return upperZeroes + lzbc - extraPartBits; | ||||||
401 | } | ||||||
402 | upperZeroes += partBits; | ||||||
403 | } | ||||||
404 | return bits; | ||||||
405 | } | ||||||
406 | |||||||
407 | // Count the number of bit positions that are set. | ||||||
408 | constexpr int POPCNT() const { | ||||||
409 | int count{0}; | ||||||
410 | for (int j{0}; j < parts; ++j) { | ||||||
411 | count += common::BitPopulationCount(part_[j]); | ||||||
412 | } | ||||||
413 | return count; | ||||||
414 | } | ||||||
415 | |||||||
416 | // True when POPCNT is odd. | ||||||
417 | constexpr bool POPPAR() const { return POPCNT() & 1; } | ||||||
418 | |||||||
419 | constexpr int TRAILZ() const { | ||||||
420 | auto minus1{AddUnsigned(MASKR(bits))}; // { x-1, carry = x > 0 } | ||||||
421 | if (!minus1.carry) { | ||||||
422 | return bits; // was zero | ||||||
423 | } else { | ||||||
424 | // x ^ (x-1) has all bits set at and below original least-order set bit. | ||||||
425 | return IEOR(minus1.value).POPCNT() - 1; | ||||||
426 | } | ||||||
427 | } | ||||||
428 | |||||||
429 | constexpr bool BTEST(int pos) const { | ||||||
430 | if (pos < 0 || pos >= bits) { | ||||||
431 | return false; | ||||||
432 | } else { | ||||||
433 | return (LEPart(pos / partBits) >> (pos % partBits)) & 1; | ||||||
434 | } | ||||||
435 | } | ||||||
436 | |||||||
437 | constexpr Ordering CompareUnsigned(const Integer &y) const { | ||||||
438 | for (int j{parts}; j-- > 0;) { | ||||||
439 | if (LEPart(j) > y.LEPart(j)) { | ||||||
440 | return Ordering::Greater; | ||||||
441 | } | ||||||
442 | if (LEPart(j) < y.LEPart(j)) { | ||||||
443 | return Ordering::Less; | ||||||
444 | } | ||||||
445 | } | ||||||
446 | return Ordering::Equal; | ||||||
447 | } | ||||||
448 | |||||||
449 | constexpr bool BGE(const Integer &y) const { | ||||||
450 | return CompareUnsigned(y) != Ordering::Less; | ||||||
451 | } | ||||||
452 | constexpr bool BGT(const Integer &y) const { | ||||||
453 | return CompareUnsigned(y) == Ordering::Greater; | ||||||
454 | } | ||||||
455 | constexpr bool BLE(const Integer &y) const { return !BGT(y); } | ||||||
456 | constexpr bool BLT(const Integer &y) const { return !BGE(y); } | ||||||
457 | |||||||
458 | constexpr Ordering CompareSigned(const Integer &y) const { | ||||||
459 | bool isNegative{IsNegative()}; | ||||||
460 | if (isNegative != y.IsNegative()) { | ||||||
461 | return isNegative ? Ordering::Less : Ordering::Greater; | ||||||
462 | } | ||||||
463 | return CompareUnsigned(y); | ||||||
464 | } | ||||||
465 | |||||||
466 | template <typename UINT = std::uint64_t> constexpr UINT ToUInt() const { | ||||||
467 | UINT n{LEPart(0)}; | ||||||
468 | std::size_t filled{partBits}; | ||||||
469 | constexpr std::size_t maxBits{CHAR_BIT8 * sizeof n}; | ||||||
470 | for (int j{1}; filled
| ||||||
471 | n |= UINT{LEPart(j)} << filled; | ||||||
| |||||||
472 | } | ||||||
473 | return n; | ||||||
474 | } | ||||||
475 | |||||||
476 | template <typename SINT = std::int64_t, typename UINT = std::uint64_t> | ||||||
477 | constexpr SINT ToSInt() const { | ||||||
478 | SINT n = ToUInt<UINT>(); | ||||||
479 | constexpr std::size_t maxBits{CHAR_BIT8 * sizeof n}; | ||||||
480 | if constexpr (bits < maxBits) { | ||||||
481 | n |= -(n >> (bits - 1)) << bits; | ||||||
482 | } | ||||||
483 | return n; | ||||||
484 | } | ||||||
485 | |||||||
486 | constexpr std::uint64_t ToUInt64() const { return ToUInt<std::uint64_t>(); } | ||||||
487 | |||||||
488 | constexpr std::int64_t ToInt64() const { | ||||||
489 | return ToSInt<std::int64_t, std::uint64_t>(); | ||||||
490 | } | ||||||
491 | |||||||
492 | // Ones'-complement (i.e., C's ~) | ||||||
493 | constexpr Integer NOT() const { | ||||||
494 | Integer result{nullptr}; | ||||||
495 | for (int j{0}; j < parts; ++j) { | ||||||
496 | result.SetLEPart(j, ~LEPart(j)); | ||||||
497 | } | ||||||
498 | return result; | ||||||
499 | } | ||||||
500 | |||||||
501 | // Two's-complement negation (-x = ~x + 1). | ||||||
502 | // An overflow flag accompanies the result, and will be true when the | ||||||
503 | // operand is the most negative signed number (MASKL(1)). | ||||||
504 | constexpr ValueWithOverflow Negate() const { | ||||||
505 | Integer result{nullptr}; | ||||||
506 | Part carry{1}; | ||||||
507 | for (int j{0}; j + 1 < parts; ++j) { | ||||||
508 | Part newCarry{LEPart(j) == 0 && carry}; | ||||||
509 | result.SetLEPart(j, ~LEPart(j) + carry); | ||||||
510 | carry = newCarry; | ||||||
511 | } | ||||||
512 | Part top{LEPart(parts - 1)}; | ||||||
513 | result.SetLEPart(parts - 1, ~top + carry); | ||||||
514 | bool overflow{top != 0 && result.LEPart(parts - 1) == top}; | ||||||
515 | return {result, overflow}; | ||||||
516 | } | ||||||
517 | |||||||
518 | constexpr ValueWithOverflow ABS() const { | ||||||
519 | if (IsNegative()) { | ||||||
520 | return Negate(); | ||||||
521 | } else { | ||||||
522 | return {*this, false}; | ||||||
523 | } | ||||||
524 | } | ||||||
525 | |||||||
526 | // Shifts the operand left when the count is positive, right when negative. | ||||||
527 | // Vacated bit positions are filled with zeroes. | ||||||
528 | constexpr Integer ISHFT(int count) const { | ||||||
529 | if (count < 0) { | ||||||
530 | return SHIFTR(-count); | ||||||
531 | } else { | ||||||
532 | return SHIFTL(count); | ||||||
533 | } | ||||||
534 | } | ||||||
535 | |||||||
536 | // Left shift with zero fill. | ||||||
537 | constexpr Integer SHIFTL(int count) const { | ||||||
538 | if (count <= 0) { | ||||||
539 | return *this; | ||||||
540 | } else { | ||||||
541 | Integer result{nullptr}; | ||||||
542 | int shiftParts{count / partBits}; | ||||||
543 | int bitShift{count - partBits * shiftParts}; | ||||||
544 | int j{parts - 1}; | ||||||
545 | if (bitShift == 0) { | ||||||
546 | for (; j >= shiftParts; --j) { | ||||||
547 | result.SetLEPart(j, LEPart(j - shiftParts)); | ||||||
548 | } | ||||||
549 | for (; j >= 0; --j) { | ||||||
550 | result.LEPart(j) = 0; | ||||||
551 | } | ||||||
552 | } else { | ||||||
553 | for (; j > shiftParts; --j) { | ||||||
554 | result.SetLEPart(j, | ||||||
555 | ((LEPart(j - shiftParts) << bitShift) | | ||||||
556 | (LEPart(j - shiftParts - 1) >> (partBits - bitShift)))); | ||||||
557 | } | ||||||
558 | if (j == shiftParts) { | ||||||
559 | result.SetLEPart(j, LEPart(0) << bitShift); | ||||||
560 | --j; | ||||||
561 | } | ||||||
562 | for (; j >= 0; --j) { | ||||||
563 | result.LEPart(j) = 0; | ||||||
564 | } | ||||||
565 | } | ||||||
566 | return result; | ||||||
567 | } | ||||||
568 | } | ||||||
569 | |||||||
570 | // Circular shift of a field of least-significant bits. The least-order | ||||||
571 | // "size" bits are shifted circularly in place by "count" positions; | ||||||
572 | // the shift is leftward if count is nonnegative, rightward otherwise. | ||||||
573 | // Higher-order bits are unchanged. | ||||||
574 | constexpr Integer ISHFTC(int count, int size = bits) const { | ||||||
575 | if (count == 0 || size <= 0) { | ||||||
576 | return *this; | ||||||
577 | } | ||||||
578 | if (size > bits) { | ||||||
579 | size = bits; | ||||||
580 | } | ||||||
581 | count %= size; | ||||||
582 | if (count == 0) { | ||||||
583 | return *this; | ||||||
584 | } | ||||||
585 | int middleBits{size - count}, leastBits{count}; | ||||||
586 | if (count < 0) { | ||||||
587 | middleBits = -count; | ||||||
588 | leastBits = size + count; | ||||||
589 | } | ||||||
590 | if (size == bits) { | ||||||
591 | return SHIFTL(leastBits).IOR(SHIFTR(middleBits)); | ||||||
592 | } | ||||||
593 | Integer unchanged{IAND(MASKL(bits - size))}; | ||||||
594 | Integer middle{IAND(MASKR(middleBits)).SHIFTL(leastBits)}; | ||||||
595 | Integer least{SHIFTR(middleBits).IAND(MASKR(leastBits))}; | ||||||
596 | return unchanged.IOR(middle).IOR(least); | ||||||
597 | } | ||||||
598 | |||||||
599 | // Double shifts, aka shifts with specific fill. | ||||||
600 | constexpr Integer SHIFTLWithFill(const Integer &fill, int count) const { | ||||||
601 | if (count <= 0) { | ||||||
602 | return *this; | ||||||
603 | } else if (count >= 2 * bits) { | ||||||
604 | return {}; | ||||||
605 | } else if (count > bits) { | ||||||
606 | return fill.SHIFTL(count - bits); | ||||||
607 | } else if (count == bits) { | ||||||
608 | return fill; | ||||||
609 | } else { | ||||||
610 | return SHIFTL(count).IOR(fill.SHIFTR(bits - count)); | ||||||
611 | } | ||||||
612 | } | ||||||
613 | |||||||
614 | constexpr Integer SHIFTRWithFill(const Integer &fill, int count) const { | ||||||
615 | if (count <= 0) { | ||||||
616 | return *this; | ||||||
617 | } else if (count >= 2 * bits) { | ||||||
618 | return {}; | ||||||
619 | } else if (count > bits) { | ||||||
620 | return fill.SHIFTR(count - bits); | ||||||
621 | } else if (count == bits) { | ||||||
622 | return fill; | ||||||
623 | } else { | ||||||
624 | return SHIFTR(count).IOR(fill.SHIFTL(bits - count)); | ||||||
625 | } | ||||||
626 | } | ||||||
627 | |||||||
628 | constexpr Integer DSHIFTL(const Integer &fill, int count) const { | ||||||
629 | // DSHIFTL(I,J) shifts I:J left; the second argument is the right fill. | ||||||
630 | return SHIFTLWithFill(fill, count); | ||||||
631 | } | ||||||
632 | |||||||
633 | constexpr Integer DSHIFTR(const Integer &value, int count) const { | ||||||
634 | // DSHIFTR(I,J) shifts I:J right; the *first* argument is the left fill. | ||||||
635 | return value.SHIFTRWithFill(*this, count); | ||||||
636 | } | ||||||
637 | |||||||
638 | // Vacated upper bits are filled with zeroes. | ||||||
639 | constexpr Integer SHIFTR(int count) const { | ||||||
640 | if (count <= 0) { | ||||||
641 | return *this; | ||||||
642 | } else { | ||||||
643 | Integer result{nullptr}; | ||||||
644 | int shiftParts{count / partBits}; | ||||||
645 | int bitShift{count - partBits * shiftParts}; | ||||||
646 | int j{0}; | ||||||
647 | if (bitShift == 0) { | ||||||
648 | for (; j + shiftParts < parts; ++j) { | ||||||
649 | result.LEPart(j) = LEPart(j + shiftParts); | ||||||
650 | } | ||||||
651 | for (; j < parts; ++j) { | ||||||
652 | result.LEPart(j) = 0; | ||||||
653 | } | ||||||
654 | } else { | ||||||
655 | for (; j + shiftParts + 1 < parts; ++j) { | ||||||
656 | result.SetLEPart(j, | ||||||
657 | (LEPart(j + shiftParts) >> bitShift) | | ||||||
658 | (LEPart(j + shiftParts + 1) << (partBits - bitShift))); | ||||||
659 | } | ||||||
660 | if (j + shiftParts + 1 == parts) { | ||||||
661 | result.LEPart(j++) = LEPart(parts - 1) >> bitShift; | ||||||
662 | } | ||||||
663 | for (; j < parts; ++j) { | ||||||
664 | result.LEPart(j) = 0; | ||||||
665 | } | ||||||
666 | } | ||||||
667 | return result; | ||||||
668 | } | ||||||
669 | } | ||||||
670 | |||||||
671 | // Be advised, an arithmetic (sign-filling) right shift is not | ||||||
672 | // the same as a division by a power of two in all cases. | ||||||
673 | constexpr Integer SHIFTA(int count) const { | ||||||
674 | if (count <= 0) { | ||||||
675 | return *this; | ||||||
676 | } else if (IsNegative()) { | ||||||
677 | return SHIFTR(count).IOR(MASKL(count)); | ||||||
678 | } else { | ||||||
679 | return SHIFTR(count); | ||||||
680 | } | ||||||
681 | } | ||||||
682 | |||||||
683 | // Clears a single bit. | ||||||
684 | constexpr Integer IBCLR(int pos) const { | ||||||
685 | if (pos < 0 || pos >= bits) { | ||||||
686 | return *this; | ||||||
687 | } else { | ||||||
688 | Integer result{*this}; | ||||||
689 | result.LEPart(pos / partBits) &= ~(Part{1} << (pos % partBits)); | ||||||
690 | return result; | ||||||
691 | } | ||||||
692 | } | ||||||
693 | |||||||
694 | // Sets a single bit. | ||||||
695 | constexpr Integer IBSET(int pos) const { | ||||||
696 | if (pos < 0 || pos >= bits) { | ||||||
697 | return *this; | ||||||
698 | } else { | ||||||
699 | Integer result{*this}; | ||||||
700 | result.LEPart(pos / partBits) |= Part{1} << (pos % partBits); | ||||||
701 | return result; | ||||||
702 | } | ||||||
703 | } | ||||||
704 | |||||||
705 | // Extracts a field. | ||||||
706 | constexpr Integer IBITS(int pos, int size) const { | ||||||
707 | return SHIFTR(pos).IAND(MASKR(size)); | ||||||
708 | } | ||||||
709 | |||||||
710 | constexpr Integer IAND(const Integer &y) const { | ||||||
711 | Integer result{nullptr}; | ||||||
712 | for (int j{0}; j < parts; ++j) { | ||||||
713 | result.LEPart(j) = LEPart(j) & y.LEPart(j); | ||||||
714 | } | ||||||
715 | return result; | ||||||
716 | } | ||||||
717 | |||||||
718 | constexpr Integer IOR(const Integer &y) const { | ||||||
719 | Integer result{nullptr}; | ||||||
720 | for (int j{0}; j < parts; ++j) { | ||||||
721 | result.LEPart(j) = LEPart(j) | y.LEPart(j); | ||||||
722 | } | ||||||
723 | return result; | ||||||
724 | } | ||||||
725 | |||||||
726 | constexpr Integer IEOR(const Integer &y) const { | ||||||
727 | Integer result{nullptr}; | ||||||
728 | for (int j{0}; j < parts; ++j) { | ||||||
729 | result.LEPart(j) = LEPart(j) ^ y.LEPart(j); | ||||||
730 | } | ||||||
731 | return result; | ||||||
732 | } | ||||||
733 | |||||||
734 | constexpr Integer MERGE_BITS(const Integer &y, const Integer &mask) const { | ||||||
735 | return IAND(mask).IOR(y.IAND(mask.NOT())); | ||||||
736 | } | ||||||
737 | |||||||
738 | constexpr Integer MAX(const Integer &y) const { | ||||||
739 | if (CompareSigned(y) == Ordering::Less) { | ||||||
740 | return y; | ||||||
741 | } else { | ||||||
742 | return *this; | ||||||
743 | } | ||||||
744 | } | ||||||
745 | |||||||
746 | constexpr Integer MIN(const Integer &y) const { | ||||||
747 | if (CompareSigned(y) == Ordering::Less) { | ||||||
748 | return *this; | ||||||
749 | } else { | ||||||
750 | return y; | ||||||
751 | } | ||||||
752 | } | ||||||
753 | |||||||
754 | // Unsigned addition with carry. | ||||||
755 | constexpr ValueWithCarry AddUnsigned( | ||||||
756 | const Integer &y, bool carryIn = false) const { | ||||||
757 | Integer sum{nullptr}; | ||||||
758 | BigPart carry{carryIn}; | ||||||
759 | for (int j{0}; j + 1 < parts; ++j) { | ||||||
760 | carry += LEPart(j); | ||||||
761 | carry += y.LEPart(j); | ||||||
762 | sum.SetLEPart(j, carry); | ||||||
763 | carry >>= partBits; | ||||||
764 | } | ||||||
765 | carry += LEPart(parts - 1); | ||||||
766 | carry += y.LEPart(parts - 1); | ||||||
767 | sum.SetLEPart(parts - 1, carry); | ||||||
768 | return {sum, carry > topPartMask}; | ||||||
769 | } | ||||||
770 | |||||||
771 | constexpr ValueWithOverflow AddSigned(const Integer &y) const { | ||||||
772 | bool isNegative{IsNegative()}; | ||||||
773 | bool sameSign{isNegative == y.IsNegative()}; | ||||||
774 | ValueWithCarry sum{AddUnsigned(y)}; | ||||||
775 | bool overflow{sameSign && sum.value.IsNegative() != isNegative}; | ||||||
776 | return {sum.value, overflow}; | ||||||
777 | } | ||||||
778 | |||||||
779 | constexpr ValueWithOverflow SubtractSigned(const Integer &y) const { | ||||||
780 | bool isNegative{IsNegative()}; | ||||||
781 | bool sameSign{isNegative == y.IsNegative()}; | ||||||
782 | ValueWithCarry diff{AddUnsigned(y.Negate().value)}; | ||||||
783 | bool overflow{!sameSign && diff.value.IsNegative() != isNegative}; | ||||||
784 | return {diff.value, overflow}; | ||||||
785 | } | ||||||
786 | |||||||
787 | // DIM(X,Y)=MAX(X-Y, 0) | ||||||
788 | constexpr ValueWithOverflow DIM(const Integer &y) const { | ||||||
789 | if (CompareSigned(y) != Ordering::Greater) { | ||||||
790 | return {}; | ||||||
791 | } else { | ||||||
792 | return SubtractSigned(y); | ||||||
793 | } | ||||||
794 | } | ||||||
795 | |||||||
796 | constexpr ValueWithOverflow SIGN(bool toNegative) const { | ||||||
797 | if (toNegative == IsNegative()) { | ||||||
798 | return {*this, false}; | ||||||
799 | } else if (toNegative) { | ||||||
800 | return Negate(); | ||||||
801 | } else { | ||||||
802 | return ABS(); | ||||||
803 | } | ||||||
804 | } | ||||||
805 | |||||||
806 | constexpr ValueWithOverflow SIGN(const Integer &sign) const { | ||||||
807 | return SIGN(sign.IsNegative()); | ||||||
808 | } | ||||||
809 | |||||||
810 | constexpr Product MultiplyUnsigned(const Integer &y) const { | ||||||
811 | Part product[2 * parts]{}; // little-endian full product | ||||||
812 | for (int j{0}; j < parts; ++j) { | ||||||
813 | if (Part xpart{LEPart(j)}) { | ||||||
814 | for (int k{0}; k < parts; ++k) { | ||||||
815 | if (Part ypart{y.LEPart(k)}) { | ||||||
816 | BigPart xy{xpart}; | ||||||
817 | xy *= ypart; | ||||||
818 | #if defined __GNUC__4 && __GNUC__4 < 8 | ||||||
819 | // && to < (2 * parts) was added to avoid GCC < 8 build failure on | ||||||
820 | // -Werror=array-bounds. This can be removed if -Werror is disable. | ||||||
821 | for (int to{j + k}; xy != 0 && to < (2 * parts); ++to) { | ||||||
822 | #else | ||||||
823 | for (int to{j + k}; xy != 0; ++to) { | ||||||
824 | #endif | ||||||
825 | xy += product[to]; | ||||||
826 | product[to] = xy & partMask; | ||||||
827 | xy >>= partBits; | ||||||
828 | } | ||||||
829 | } | ||||||
830 | } | ||||||
831 | } | ||||||
832 | } | ||||||
833 | Integer upper{nullptr}, lower{nullptr}; | ||||||
834 | for (int j{0}; j < parts; ++j) { | ||||||
835 | lower.LEPart(j) = product[j]; | ||||||
836 | upper.LEPart(j) = product[j + parts]; | ||||||
837 | } | ||||||
838 | if constexpr (topPartBits < partBits) { | ||||||
839 | upper = upper.SHIFTL(partBits - topPartBits); | ||||||
840 | upper.LEPart(0) |= lower.LEPart(parts - 1) >> topPartBits; | ||||||
841 | lower.LEPart(parts - 1) &= topPartMask; | ||||||
842 | } | ||||||
843 | return {upper, lower}; | ||||||
844 | } | ||||||
845 | |||||||
846 | constexpr Product MultiplySigned(const Integer &y) const { | ||||||
847 | bool yIsNegative{y.IsNegative()}; | ||||||
848 | Integer absy{y}; | ||||||
849 | if (yIsNegative) { | ||||||
850 | absy = y.Negate().value; | ||||||
851 | } | ||||||
852 | bool isNegative{IsNegative()}; | ||||||
853 | Integer absx{*this}; | ||||||
854 | if (isNegative) { | ||||||
855 | absx = Negate().value; | ||||||
856 | } | ||||||
857 | Product product{absx.MultiplyUnsigned(absy)}; | ||||||
858 | if (isNegative != yIsNegative) { | ||||||
859 | product.lower = product.lower.NOT(); | ||||||
860 | product.upper = product.upper.NOT(); | ||||||
861 | Integer one{1}; | ||||||
862 | auto incremented{product.lower.AddUnsigned(one)}; | ||||||
863 | product.lower = incremented.value; | ||||||
864 | if (incremented.carry) { | ||||||
865 | product.upper = product.upper.AddUnsigned(one).value; | ||||||
866 | } | ||||||
867 | } | ||||||
868 | return product; | ||||||
869 | } | ||||||
870 | |||||||
871 | constexpr QuotientWithRemainder DivideUnsigned(const Integer &divisor) const { | ||||||
872 | if (divisor.IsZero()) { | ||||||
873 | return {MASKR(bits), Integer{}, true, false}; // overflow to max value | ||||||
874 | } | ||||||
875 | int bitsDone{LEADZ()}; | ||||||
876 | Integer top{SHIFTL(bitsDone)}; | ||||||
877 | Integer quotient, remainder; | ||||||
878 | for (; bitsDone < bits; ++bitsDone) { | ||||||
879 | auto doubledTop{top.AddUnsigned(top)}; | ||||||
880 | top = doubledTop.value; | ||||||
881 | remainder = remainder.AddUnsigned(remainder, doubledTop.carry).value; | ||||||
882 | bool nextBit{remainder.CompareUnsigned(divisor) != Ordering::Less}; | ||||||
883 | quotient = quotient.AddUnsigned(quotient, nextBit).value; | ||||||
884 | if (nextBit) { | ||||||
885 | remainder = remainder.SubtractSigned(divisor).value; | ||||||
886 | } | ||||||
887 | } | ||||||
888 | return {quotient, remainder, false, false}; | ||||||
889 | } | ||||||
890 | |||||||
891 | // A nonzero remainder has the sign of the dividend, i.e., it computes | ||||||
892 | // the MOD intrinsic (X-INT(X/Y)*Y), not MODULO (which is below). | ||||||
893 | // 8/5 = 1r3; -8/5 = -1r-3; 8/-5 = -1r3; -8/-5 = 1r-3 | ||||||
894 | constexpr QuotientWithRemainder DivideSigned(Integer divisor) const { | ||||||
895 | bool dividendIsNegative{IsNegative()}; | ||||||
896 | bool negateQuotient{dividendIsNegative}; | ||||||
897 | Ordering divisorOrdering{divisor.CompareToZeroSigned()}; | ||||||
898 | if (divisorOrdering == Ordering::Less) { | ||||||
899 | negateQuotient = !negateQuotient; | ||||||
900 | auto negated{divisor.Negate()}; | ||||||
901 | if (negated.overflow) { | ||||||
902 | // divisor was (and is) the most negative number | ||||||
903 | if (CompareUnsigned(divisor) == Ordering::Equal) { | ||||||
904 | return {MASKR(1), Integer{}, false, bits <= 1}; | ||||||
905 | } else { | ||||||
906 | return {Integer{}, *this, false, false}; | ||||||
907 | } | ||||||
908 | } | ||||||
909 | divisor = negated.value; | ||||||
910 | } else if (divisorOrdering == Ordering::Equal) { | ||||||
911 | // division by zero | ||||||
912 | if (dividendIsNegative) { | ||||||
913 | return {MASKL(1), Integer{}, true, false}; | ||||||
914 | } else { | ||||||
915 | return {MASKR(bits - 1), Integer{}, true, false}; | ||||||
916 | } | ||||||
917 | } | ||||||
918 | Integer dividend{*this}; | ||||||
919 | if (dividendIsNegative) { | ||||||
920 | auto negated{Negate()}; | ||||||
921 | if (negated.overflow) { | ||||||
922 | // Dividend was (and remains) the most negative number. | ||||||
923 | // See whether the original divisor was -1 (if so, it's 1 now). | ||||||
924 | if (divisorOrdering == Ordering::Less && | ||||||
925 | divisor.CompareUnsigned(Integer{1}) == Ordering::Equal) { | ||||||
926 | // most negative number / -1 is the sole overflow case | ||||||
927 | return {*this, Integer{}, false, true}; | ||||||
928 | } | ||||||
929 | } else { | ||||||
930 | dividend = negated.value; | ||||||
931 | } | ||||||
932 | } | ||||||
933 | // Overflow is not possible, and both the dividend and divisor | ||||||
934 | // are now positive. | ||||||
935 | QuotientWithRemainder result{dividend.DivideUnsigned(divisor)}; | ||||||
936 | if (negateQuotient) { | ||||||
937 | result.quotient = result.quotient.Negate().value; | ||||||
938 | } | ||||||
939 | if (dividendIsNegative) { | ||||||
940 | result.remainder = result.remainder.Negate().value; | ||||||
941 | } | ||||||
942 | return result; | ||||||
943 | } | ||||||
944 | |||||||
945 | // Result has the sign of the divisor argument. | ||||||
946 | // 8 mod 5 = 3; -8 mod 5 = 2; 8 mod -5 = -2; -8 mod -5 = -3 | ||||||
947 | constexpr ValueWithOverflow MODULO(const Integer &divisor) const { | ||||||
948 | bool negativeDivisor{divisor.IsNegative()}; | ||||||
949 | bool distinctSigns{IsNegative() != negativeDivisor}; | ||||||
950 | QuotientWithRemainder divided{DivideSigned(divisor)}; | ||||||
951 | if (distinctSigns && !divided.remainder.IsZero()) { | ||||||
952 | return {divided.remainder.AddUnsigned(divisor).value, divided.overflow}; | ||||||
953 | } else { | ||||||
954 | return {divided.remainder, divided.overflow}; | ||||||
955 | } | ||||||
956 | } | ||||||
957 | |||||||
958 | constexpr PowerWithErrors Power(const Integer &exponent) const { | ||||||
959 | PowerWithErrors result{1, false, false, false}; | ||||||
960 | if (exponent.IsZero()) { | ||||||
961 | // x**0 -> 1, including the case 0**0, which is not defined specifically | ||||||
962 | // in F'18 afaict; however, other Fortrans tested all produce 1, not 0, | ||||||
963 | // apart from nagfor, which stops with an error at runtime. | ||||||
964 | // Ada, APL, C's pow(), Haskell, Julia, MATLAB, and R all produce 1 too. | ||||||
965 | // F'77 explicitly states that 0**0 is mathematically undefined and | ||||||
966 | // therefore prohibited. | ||||||
967 | result.zeroToZero = IsZero(); | ||||||
968 | } else if (exponent.IsNegative()) { | ||||||
969 | if (IsZero()) { | ||||||
970 | result.divisionByZero = true; | ||||||
971 | result.power = MASKR(bits - 1); | ||||||
972 | } else if (CompareSigned(Integer{1}) == Ordering::Equal) { | ||||||
973 | result.power = *this; // 1**x -> 1 | ||||||
974 | } else if (CompareSigned(Integer{-1}) == Ordering::Equal) { | ||||||
975 | if (exponent.BTEST(0)) { | ||||||
976 | result.power = *this; // (-1)**x -> -1 if x is odd | ||||||
977 | } | ||||||
978 | } else { | ||||||
979 | result.power.Clear(); // j**k -> 0 if |j| > 1 and k < 0 | ||||||
980 | } | ||||||
981 | } else { | ||||||
982 | Integer shifted{*this}; | ||||||
983 | Integer pow{exponent}; | ||||||
984 | int nbits{bits - pow.LEADZ()}; | ||||||
985 | for (int j{0}; j < nbits; ++j) { | ||||||
986 | if (pow.BTEST(j)) { | ||||||
987 | Product product{result.power.MultiplySigned(shifted)}; | ||||||
988 | result.power = product.lower; | ||||||
989 | result.overflow |= product.SignedMultiplicationOverflowed(); | ||||||
990 | } | ||||||
991 | if (j + 1 < nbits) { | ||||||
992 | Product squared{shifted.MultiplySigned(shifted)}; | ||||||
993 | result.overflow |= squared.SignedMultiplicationOverflowed(); | ||||||
994 | shifted = squared.lower; | ||||||
995 | } | ||||||
996 | } | ||||||
997 | } | ||||||
998 | return result; | ||||||
999 | } | ||||||
1000 | |||||||
1001 | private: | ||||||
1002 | // A private constructor, selected by the use of nullptr, | ||||||
1003 | // that is used by member functions when it would be a waste | ||||||
1004 | // of time to initialize parts_[]. | ||||||
1005 | constexpr Integer(std::nullptr_t) {} | ||||||
1006 | |||||||
1007 | // Accesses parts in little-endian order. | ||||||
1008 | constexpr const Part &LEPart(int part) const { | ||||||
1009 | if constexpr (littleEndian
| ||||||
1010 | return part_[part]; | ||||||
1011 | } else { | ||||||
1012 | return part_[parts - 1 - part]; | ||||||
1013 | } | ||||||
1014 | } | ||||||
1015 | |||||||
1016 | constexpr Part &LEPart(int part) { | ||||||
1017 | if constexpr (littleEndian) { | ||||||
1018 | return part_[part]; | ||||||
1019 | } else { | ||||||
1020 | return part_[parts - 1 - part]; | ||||||
1021 | } | ||||||
1022 | } | ||||||
1023 | |||||||
1024 | constexpr void SetLEPart(int part, Part x) { | ||||||
1025 | LEPart(part) = x & PartMask(part); | ||||||
1026 | } | ||||||
1027 | |||||||
1028 | static constexpr Part PartMask(int part) { | ||||||
1029 | return part == parts - 1 ? topPartMask : partMask; | ||||||
1030 | } | ||||||
1031 | |||||||
1032 | constexpr void Clear() { | ||||||
1033 | for (int j{0}; j < parts; ++j) { | ||||||
1034 | part_[j] = 0; | ||||||
1035 | } | ||||||
1036 | } | ||||||
1037 | |||||||
1038 | Part part_[parts]{}; | ||||||
1039 | }; | ||||||
1040 | |||||||
1041 | extern template class Integer<8>; | ||||||
1042 | extern template class Integer<16>; | ||||||
1043 | extern template class Integer<32>; | ||||||
1044 | extern template class Integer<64>; | ||||||
1045 | extern template class Integer<80>; | ||||||
1046 | extern template class Integer<128>; | ||||||
1047 | } // namespace Fortran::evaluate::value | ||||||
1048 | #endif // FORTRAN_EVALUATE_INTEGER_H_ |