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