brintos

brintos / llvm-project-archived public Read only

0
0
Text · 7.6 KiB · b2e9d81 Raw
226 lines · c
1//===-- Add and subtract IEEE 754 floating-point numbers --------*- 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-exception6//7//===----------------------------------------------------------------------===//8 9#ifndef LLVM_LIBC_SRC___SUPPORT_FPUTIL_GENERIC_ADD_SUB_H10#define LLVM_LIBC_SRC___SUPPORT_FPUTIL_GENERIC_ADD_SUB_H11 12#include "hdr/fenv_macros.h"13#include "src/__support/CPP/algorithm.h"14#include "src/__support/CPP/bit.h"15#include "src/__support/CPP/type_traits.h"16#include "src/__support/FPUtil/BasicOperations.h"17#include "src/__support/FPUtil/FEnvImpl.h"18#include "src/__support/FPUtil/FPBits.h"19#include "src/__support/FPUtil/cast.h"20#include "src/__support/FPUtil/dyadic_float.h"21#include "src/__support/FPUtil/rounding_mode.h"22#include "src/__support/macros/attributes.h"23#include "src/__support/macros/config.h"24#include "src/__support/macros/optimization.h"25 26namespace LIBC_NAMESPACE_DECL {27namespace fputil::generic {28 29template <bool IsSub, typename OutType, typename InType>30LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<OutType> &&31                                 cpp::is_floating_point_v<InType> &&32                                 sizeof(OutType) <= sizeof(InType),33                             OutType>34add_or_sub(InType x, InType y) {35  using OutFPBits = FPBits<OutType>;36  using OutStorageType = typename OutFPBits::StorageType;37  using InFPBits = FPBits<InType>;38  using InStorageType = typename InFPBits::StorageType;39 40  constexpr int GUARD_BITS_LEN = 3;41  constexpr int RESULT_FRACTION_LEN = InFPBits::FRACTION_LEN + GUARD_BITS_LEN;42  constexpr int RESULT_MANTISSA_LEN = RESULT_FRACTION_LEN + 1;43 44  using DyadicFloat =45      DyadicFloat<cpp::bit_ceil(static_cast<size_t>(RESULT_MANTISSA_LEN))>;46 47  InFPBits x_bits(x);48  InFPBits y_bits(y);49 50  bool is_effectively_add = (x_bits.sign() == y_bits.sign()) != IsSub;51 52  if (LIBC_UNLIKELY(x_bits.is_inf_or_nan() || y_bits.is_inf_or_nan() ||53                    x_bits.is_zero() || y_bits.is_zero())) {54    if (x_bits.is_nan() || y_bits.is_nan()) {55      if (x_bits.is_signaling_nan() || y_bits.is_signaling_nan())56        raise_except_if_required(FE_INVALID);57 58      if (x_bits.is_quiet_nan()) {59        InStorageType x_payload = x_bits.get_mantissa();60        x_payload >>= InFPBits::FRACTION_LEN - OutFPBits::FRACTION_LEN;61        return OutFPBits::quiet_nan(x_bits.sign(),62                                    static_cast<OutStorageType>(x_payload))63            .get_val();64      }65 66      if (y_bits.is_quiet_nan()) {67        InStorageType y_payload = y_bits.get_mantissa();68        y_payload >>= InFPBits::FRACTION_LEN - OutFPBits::FRACTION_LEN;69        return OutFPBits::quiet_nan(y_bits.sign(),70                                    static_cast<OutStorageType>(y_payload))71            .get_val();72      }73 74      return OutFPBits::quiet_nan().get_val();75    }76 77    if (x_bits.is_inf()) {78      if (y_bits.is_inf()) {79        if (!is_effectively_add) {80          raise_except_if_required(FE_INVALID);81          return OutFPBits::quiet_nan().get_val();82        }83 84        return OutFPBits::inf(x_bits.sign()).get_val();85      }86 87      return OutFPBits::inf(x_bits.sign()).get_val();88    }89 90    if (y_bits.is_inf()) {91      if constexpr (IsSub)92        return OutFPBits::inf(y_bits.sign().negate()).get_val();93      else94        return OutFPBits::inf(y_bits.sign()).get_val();95    }96 97    if (x_bits.is_zero()) {98      if (y_bits.is_zero()) {99        switch (quick_get_round()) {100        case FE_DOWNWARD:101          return OutFPBits::zero(Sign::NEG).get_val();102        default:103          return OutFPBits::zero(Sign::POS).get_val();104        }105      }106 107      if constexpr (cpp::is_same_v<InType, bfloat16> &&108                    cpp::is_same_v<OutType, bfloat16>) {109        OutFPBits y_bits(y);110        if constexpr (IsSub)111          y_bits.set_sign(y_bits.sign().negate());112        return y_bits.get_val();113      } else {114 115        // volatile prevents Clang from converting tmp to OutType and then116        // immediately back to InType before negating it, resulting in double117        // rounding.118        volatile InType tmp = y;119        if constexpr (IsSub)120          tmp = -tmp;121        return cast<OutType>(tmp);122      }123    }124 125    if (y_bits.is_zero())126      return cast<OutType>(x);127  }128 129  InType x_abs = x_bits.abs().get_val();130  InType y_abs = y_bits.abs().get_val();131 132  if (x_abs == y_abs && !is_effectively_add) {133    switch (quick_get_round()) {134    case FE_DOWNWARD:135      return OutFPBits::zero(Sign::NEG).get_val();136    default:137      return OutFPBits::zero(Sign::POS).get_val();138    }139  }140 141  Sign result_sign = Sign::POS;142 143  if (x_abs > y_abs) {144    result_sign = x_bits.sign();145  } else if (x_abs < y_abs) {146    if (is_effectively_add)147      result_sign = y_bits.sign();148    else if (y_bits.is_pos())149      result_sign = Sign::NEG;150  } else if (is_effectively_add) {151    result_sign = x_bits.sign();152  }153 154  InFPBits max_bits(cpp::max(x_abs, y_abs));155  InFPBits min_bits(cpp::min(x_abs, y_abs));156 157  InStorageType result_mant;158 159  if (max_bits.is_subnormal()) {160    // min_bits must be subnormal too.161 162    if (is_effectively_add)163      result_mant = max_bits.get_mantissa() + min_bits.get_mantissa();164    else165      result_mant = max_bits.get_mantissa() - min_bits.get_mantissa();166 167    result_mant <<= GUARD_BITS_LEN;168  } else {169    InStorageType max_mant = static_cast<InStorageType>(170        max_bits.get_explicit_mantissa() << GUARD_BITS_LEN);171    InStorageType min_mant = static_cast<InStorageType>(172        min_bits.get_explicit_mantissa() << GUARD_BITS_LEN);173 174    int alignment = (max_bits.get_biased_exponent() - max_bits.is_normal()) -175                    (min_bits.get_biased_exponent() - min_bits.is_normal());176 177    InStorageType aligned_min_mant = static_cast<InStorageType>(178        min_mant >> cpp::min(alignment, RESULT_MANTISSA_LEN));179    bool aligned_min_mant_sticky;180 181    if (alignment <= GUARD_BITS_LEN)182      aligned_min_mant_sticky = false;183    else if (alignment > InFPBits::FRACTION_LEN + GUARD_BITS_LEN)184      aligned_min_mant_sticky = true;185    else186      aligned_min_mant_sticky =187          (static_cast<InStorageType>(188              min_mant << (InFPBits::STORAGE_LEN - alignment))) != 0;189 190    InStorageType min_mant_sticky =191        static_cast<InStorageType>(static_cast<int>(aligned_min_mant_sticky));192 193    if (is_effectively_add)194      result_mant = max_mant + (aligned_min_mant | min_mant_sticky);195    else196      result_mant = max_mant - (aligned_min_mant | min_mant_sticky);197  }198 199  int result_exp = max_bits.get_explicit_exponent() - RESULT_FRACTION_LEN;200  DyadicFloat result(result_sign, result_exp, result_mant);201  return result.template as<OutType, /*ShouldSignalExceptions=*/true>();202}203 204template <typename OutType, typename InType>205LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<OutType> &&206                                 cpp::is_floating_point_v<InType> &&207                                 sizeof(OutType) <= sizeof(InType),208                             OutType>209add(InType x, InType y) {210  return add_or_sub</*IsSub=*/false, OutType>(x, y);211}212 213template <typename OutType, typename InType>214LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<OutType> &&215                                 cpp::is_floating_point_v<InType> &&216                                 sizeof(OutType) <= sizeof(InType),217                             OutType>218sub(InType x, InType y) {219  return add_or_sub</*IsSub=*/true, OutType>(x, y);220}221 222} // namespace fputil::generic223} // namespace LIBC_NAMESPACE_DECL224 225#endif // LLVM_LIBC_SRC___SUPPORT_FPUTIL_GENERIC_ADD_SUB_H226