FLANG
binary-floating-point.h
1//===-- include/flang/Decimal/binary-floating-point.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_DECIMAL_BINARY_FLOATING_POINT_H_
10#define FORTRAN_DECIMAL_BINARY_FLOATING_POINT_H_
11
12// Access and manipulate the fields of an IEEE-754 binary
13// floating-point value via a generalized template.
14
15#include "flang/Common/api-attrs.h"
16#include "flang/Common/real.h"
17#include "flang/Common/uint128.h"
18#include "flang/Runtime/freestanding-tools.h"
19#include <climits>
20#include <cstring>
21
22namespace Fortran::decimal {
23
24enum FortranRounding {
25 RoundNearest, /* RN and RP */
26 RoundUp, /* RU */
27 RoundDown, /* RD */
28 RoundToZero, /* RZ - no rounding */
29 RoundCompatible, /* RC: like RN, but ties go away from 0 */
30};
31
32template <int BINARY_PRECISION> class BinaryFloatingPointNumber {
33public:
34 RT_OFFLOAD_VAR_GROUP_BEGIN
35 static constexpr common::RealCharacteristics realChars{BINARY_PRECISION};
36 static constexpr int binaryPrecision{BINARY_PRECISION};
37 static constexpr int bits{realChars.bits};
38 static constexpr int isImplicitMSB{realChars.isImplicitMSB};
39 static constexpr int significandBits{realChars.significandBits};
40 static constexpr int exponentBits{realChars.exponentBits};
41 static constexpr int exponentBias{realChars.exponentBias};
42 static constexpr int maxExponent{realChars.maxExponent};
43 static constexpr int decimalPrecision{realChars.decimalPrecision};
44 static constexpr int decimalRange{realChars.decimalRange};
45 static constexpr int maxDecimalConversionDigits{
46 realChars.maxDecimalConversionDigits};
47
48 using RawType = common::HostUnsignedIntType<bits>;
49 static_assert(CHAR_BIT * sizeof(RawType) >= bits);
50 static constexpr RawType significandMask{(RawType{1} << significandBits) - 1};
51
52 constexpr RT_API_ATTRS BinaryFloatingPointNumber() {} // zero
53 RT_OFFLOAD_VAR_GROUP_END
54 constexpr BinaryFloatingPointNumber(
55 const BinaryFloatingPointNumber &that) = default;
56 constexpr BinaryFloatingPointNumber(
57 BinaryFloatingPointNumber &&that) = default;
58 constexpr BinaryFloatingPointNumber &operator=(
59 const BinaryFloatingPointNumber &that) = default;
60 constexpr BinaryFloatingPointNumber &operator=(
61 BinaryFloatingPointNumber &&that) = default;
62 constexpr explicit RT_API_ATTRS BinaryFloatingPointNumber(RawType raw)
63 : raw_{raw} {}
64
65 RT_API_ATTRS RawType raw() const { return raw_; }
66
67 template <typename A>
68 explicit constexpr RT_API_ATTRS BinaryFloatingPointNumber(A x) {
69 static_assert(sizeof raw_ <= sizeof x);
70 runtime::memcpy(reinterpret_cast<void *>(&raw_),
71 reinterpret_cast<const void *>(&x), sizeof raw_);
72 }
73
74 constexpr RT_API_ATTRS int BiasedExponent() const {
75 return static_cast<int>(
76 (raw_ >> significandBits) & ((1 << exponentBits) - 1));
77 }
78 constexpr RT_API_ATTRS int UnbiasedExponent() const {
79 int biased{BiasedExponent()};
80 return biased - exponentBias + (biased == 0);
81 }
82 constexpr RT_API_ATTRS RawType Significand() const {
83 return raw_ & significandMask;
84 }
85 constexpr RT_API_ATTRS RawType Fraction() const {
86 RawType sig{Significand()};
87 if (isImplicitMSB && BiasedExponent() > 0) {
88 sig |= RawType{1} << significandBits;
89 }
90 return sig;
91 }
92
93 constexpr RT_API_ATTRS bool IsZero() const {
94 return (raw_ & ((RawType{1} << (bits - 1)) - 1)) == 0;
95 }
96 constexpr RT_API_ATTRS bool IsNaN() const {
97 auto expo{BiasedExponent()};
98 auto sig{Significand()};
99 if constexpr (bits == 80) { // x87
100 if (expo == maxExponent) {
101 return sig != (significandMask >> 1) + 1;
102 } else {
103 return expo != 0 && !(sig & (RawType{1} << (significandBits - 1)));
104 ;
105 }
106 } else {
107 return expo == maxExponent && sig != 0;
108 }
109 }
110 constexpr RT_API_ATTRS bool IsInfinite() const {
111 if constexpr (bits == 80) { // x87
112 return BiasedExponent() == maxExponent &&
113 Significand() == ((significandMask >> 1) + 1);
114 } else {
115 return BiasedExponent() == maxExponent && Significand() == 0;
116 }
117 }
118 constexpr RT_API_ATTRS bool IsMaximalFiniteMagnitude() const {
119 return BiasedExponent() == maxExponent - 1 &&
120 Significand() == significandMask;
121 }
122 constexpr RT_API_ATTRS bool IsNegative() const {
123 return ((raw_ >> (bits - 1)) & 1) != 0;
124 }
125
126 constexpr RT_API_ATTRS void Negate() { raw_ ^= RawType{1} << (bits - 1); }
127
128 // For calculating the nearest neighbors of a floating-point value
129 constexpr RT_API_ATTRS void Previous() {
130 RemoveExplicitMSB();
131 --raw_;
132 InsertExplicitMSB();
133 }
134 constexpr RT_API_ATTRS void Next() {
135 RemoveExplicitMSB();
136 ++raw_;
137 InsertExplicitMSB();
138 }
139
140 static constexpr RT_API_ATTRS BinaryFloatingPointNumber Infinity(
141 bool isNegative) {
142 RawType result{RawType{maxExponent} << significandBits};
143 if (isNegative) {
144 result |= RawType{1} << (bits - 1);
145 }
146 return BinaryFloatingPointNumber{result};
147 }
148
149 // Returns true when the result is exact
150 constexpr RT_API_ATTRS bool RoundToBits(
151 int keepBits, enum FortranRounding mode) {
152 if (IsNaN() || IsInfinite() || keepBits >= binaryPrecision) {
153 return true;
154 }
155 int lostBits{keepBits < binaryPrecision ? binaryPrecision - keepBits : 0};
156 RawType lostMask{static_cast<RawType>((RawType{1} << lostBits) - 1)};
157 if (RawType lost{static_cast<RawType>(raw_ & lostMask)}; lost != 0) {
158 bool increase{false};
159 switch (mode) {
160 case RoundNearest:
161 if (lost >> (lostBits - 1) != 0) { // >= tie
162 if ((lost & (lostMask >> 1)) != 0) {
163 increase = true; // > tie
164 } else {
165 increase = ((raw_ >> lostBits) & 1) != 0; // tie to even
166 }
167 }
168 break;
169 case RoundUp:
170 increase = !IsNegative();
171 break;
172 case RoundDown:
173 increase = IsNegative();
174 break;
175 case RoundToZero:
176 break;
177 case RoundCompatible:
178 increase = lost >> (lostBits - 1) != 0; // >= tie
179 break;
180 }
181 if (increase) {
182 raw_ |= lostMask;
183 Next();
184 }
185 return false; // inexact
186 } else {
187 return true; // exact
188 }
189 }
190
191private:
192 constexpr RT_API_ATTRS void RemoveExplicitMSB() {
193 if constexpr (!isImplicitMSB) {
194 raw_ = (raw_ & (significandMask >> 1)) | ((raw_ & ~significandMask) >> 1);
195 }
196 }
197 constexpr RT_API_ATTRS void InsertExplicitMSB() {
198 if constexpr (!isImplicitMSB) {
199 constexpr RawType mask{significandMask >> 1};
200 raw_ = (raw_ & mask) | ((raw_ & ~mask) << 1);
201 if (BiasedExponent() > 0) {
202 raw_ |= RawType{1} << (significandBits - 1);
203 }
204 }
205 }
206
207 RawType raw_{0};
208};
209} // namespace Fortran::decimal
210#endif