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