1 //===-- include/flang/Evaluate/rounding-bits.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_ROUNDING_BITS_H_
10 #define FORTRAN_EVALUATE_ROUNDING_BITS_H_
11 
12 // A helper class used by Real<> to determine rounding of rational results
13 // to floating-point values.  Bits lost from intermediate computations by
14 // being shifted rightward are accumulated in instances of this class.
15 
16 namespace Fortran::evaluate::value {
17 
18 class RoundingBits {
19 public:
20   constexpr RoundingBits(
21       bool guard = false, bool round = false, bool sticky = false)
22       : guard_{guard}, round_{round}, sticky_{sticky} {}
23 
24   template <typename FRACTION>
RoundingBits(const FRACTION & fraction,int rshift)25   constexpr RoundingBits(const FRACTION &fraction, int rshift) {
26     if (rshift > 0 && rshift < fraction.bits + 1) {
27       guard_ = fraction.BTEST(rshift - 1);
28     }
29     if (rshift > 1 && rshift < fraction.bits + 2) {
30       round_ = fraction.BTEST(rshift - 2);
31     }
32     if (rshift > 2) {
33       if (rshift >= fraction.bits + 2) {
34         sticky_ = !fraction.IsZero();
35       } else {
36         auto mask{fraction.MASKR(rshift - 2)};
37         sticky_ = !fraction.IAND(mask).IsZero();
38       }
39     }
40   }
41 
guard()42   constexpr bool guard() const { return guard_; }
round()43   constexpr bool round() const { return round_; }
sticky()44   constexpr bool sticky() const { return sticky_; }
empty()45   constexpr bool empty() const { return !(guard_ | round_ | sticky_); }
46 
Negate()47   constexpr bool Negate() {
48     bool carry{!sticky_};
49     if (carry) {
50       carry = !round_;
51     } else {
52       round_ = !round_;
53     }
54     if (carry) {
55       carry = !guard_;
56     } else {
57       guard_ = !guard_;
58     }
59     return carry;
60   }
61 
ShiftLeft()62   constexpr bool ShiftLeft() {
63     bool oldGuard{guard_};
64     guard_ = round_;
65     round_ = sticky_;
66     return oldGuard;
67   }
68 
ShiftRight(bool newGuard)69   constexpr void ShiftRight(bool newGuard) {
70     sticky_ |= round_;
71     round_ = guard_;
72     guard_ = newGuard;
73   }
74 
75   // Determines whether a value should be rounded by increasing its
76   // fraction, given a rounding mode and a summary of the lost bits.
MustRound(Rounding rounding,bool isNegative,bool isOdd)77   constexpr bool MustRound(
78       Rounding rounding, bool isNegative, bool isOdd) const {
79     bool round{false}; // to dodge bogus g++ warning about missing return
80     switch (rounding.mode) {
81     case common::RoundingMode::TiesToEven:
82       round = guard_ && (round_ | sticky_ | isOdd);
83       break;
84     case common::RoundingMode::ToZero:
85       break;
86     case common::RoundingMode::Down:
87       round = isNegative && !empty();
88       break;
89     case common::RoundingMode::Up:
90       round = !isNegative && !empty();
91       break;
92     case common::RoundingMode::TiesAwayFromZero:
93       round = guard_;
94       break;
95     }
96     return round;
97   }
98 
99 private:
100   bool guard_{false}; // 0.5 * ulp (unit in lowest place)
101   bool round_{false}; // 0.25 * ulp
102   bool sticky_{false}; // true if any lesser-valued bit would be set
103 };
104 } // namespace Fortran::evaluate::value
105 #endif // FORTRAN_EVALUATE_ROUNDING_BITS_H_
106