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