xref: /freebsd/contrib/llvm-project/libc/src/__support/FPUtil/cast.h (revision bb722a7d0f1642bff6487f943ad0427799a6e5bf)
1*bb722a7dSDimitry Andric //===-- Conversion between floating-point types -----------------*- C++ -*-===//
2*bb722a7dSDimitry Andric //
3*bb722a7dSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*bb722a7dSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*bb722a7dSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*bb722a7dSDimitry Andric //
7*bb722a7dSDimitry Andric //===----------------------------------------------------------------------===//
8*bb722a7dSDimitry Andric 
9*bb722a7dSDimitry Andric #ifndef LLVM_LIBC_SRC___SUPPORT_FPUTIL_CAST_H
10*bb722a7dSDimitry Andric #define LLVM_LIBC_SRC___SUPPORT_FPUTIL_CAST_H
11*bb722a7dSDimitry Andric 
12*bb722a7dSDimitry Andric #include "FPBits.h"
13*bb722a7dSDimitry Andric #include "dyadic_float.h"
14*bb722a7dSDimitry Andric #include "hdr/fenv_macros.h"
15*bb722a7dSDimitry Andric #include "src/__support/CPP/algorithm.h"
16*bb722a7dSDimitry Andric #include "src/__support/CPP/type_traits.h"
17*bb722a7dSDimitry Andric #include "src/__support/macros/properties/types.h"
18*bb722a7dSDimitry Andric 
19*bb722a7dSDimitry Andric namespace LIBC_NAMESPACE::fputil {
20*bb722a7dSDimitry Andric 
21*bb722a7dSDimitry Andric // TODO: Add optimization for known good targets with fast
22*bb722a7dSDimitry Andric // float to float16 conversion:
23*bb722a7dSDimitry Andric // https://github.com/llvm/llvm-project/issues/133517
24*bb722a7dSDimitry Andric template <typename OutType, typename InType>
25*bb722a7dSDimitry Andric LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_floating_point_v<OutType> &&
26*bb722a7dSDimitry Andric                                            cpp::is_floating_point_v<InType>,
27*bb722a7dSDimitry Andric                                        OutType>
cast(InType x)28*bb722a7dSDimitry Andric cast(InType x) {
29*bb722a7dSDimitry Andric   // Casting to the same type is a no-op.
30*bb722a7dSDimitry Andric   if constexpr (cpp::is_same_v<InType, OutType>)
31*bb722a7dSDimitry Andric     return x;
32*bb722a7dSDimitry Andric 
33*bb722a7dSDimitry Andric   // bfloat16 is always defined (for now)
34*bb722a7dSDimitry Andric   if constexpr (cpp::is_same_v<OutType, bfloat16> ||
35*bb722a7dSDimitry Andric                 cpp::is_same_v<InType, bfloat16>
36*bb722a7dSDimitry Andric #if defined(LIBC_TYPES_HAS_FLOAT16) && !defined(__LIBC_USE_FLOAT16_CONVERSION)
37*bb722a7dSDimitry Andric                 || cpp::is_same_v<OutType, float16> ||
38*bb722a7dSDimitry Andric                 cpp::is_same_v<InType, float16>
39*bb722a7dSDimitry Andric #endif
40*bb722a7dSDimitry Andric   ) {
41*bb722a7dSDimitry Andric     using InFPBits = FPBits<InType>;
42*bb722a7dSDimitry Andric     using InStorageType = typename InFPBits::StorageType;
43*bb722a7dSDimitry Andric     using OutFPBits = FPBits<OutType>;
44*bb722a7dSDimitry Andric     using OutStorageType = typename OutFPBits::StorageType;
45*bb722a7dSDimitry Andric 
46*bb722a7dSDimitry Andric     InFPBits x_bits(x);
47*bb722a7dSDimitry Andric 
48*bb722a7dSDimitry Andric     if (x_bits.is_nan()) {
49*bb722a7dSDimitry Andric       if (x_bits.is_signaling_nan()) {
50*bb722a7dSDimitry Andric         raise_except_if_required(FE_INVALID);
51*bb722a7dSDimitry Andric         return OutFPBits::quiet_nan().get_val();
52*bb722a7dSDimitry Andric       }
53*bb722a7dSDimitry Andric 
54*bb722a7dSDimitry Andric       InStorageType x_mant = x_bits.get_mantissa();
55*bb722a7dSDimitry Andric       if (InFPBits::FRACTION_LEN > OutFPBits::FRACTION_LEN)
56*bb722a7dSDimitry Andric         x_mant >>= InFPBits::FRACTION_LEN - OutFPBits::FRACTION_LEN;
57*bb722a7dSDimitry Andric       return OutFPBits::quiet_nan(x_bits.sign(),
58*bb722a7dSDimitry Andric                                   static_cast<OutStorageType>(x_mant))
59*bb722a7dSDimitry Andric           .get_val();
60*bb722a7dSDimitry Andric     }
61*bb722a7dSDimitry Andric 
62*bb722a7dSDimitry Andric     if (x_bits.is_inf())
63*bb722a7dSDimitry Andric       return OutFPBits::inf(x_bits.sign()).get_val();
64*bb722a7dSDimitry Andric 
65*bb722a7dSDimitry Andric     constexpr size_t MAX_FRACTION_LEN =
66*bb722a7dSDimitry Andric         cpp::max(OutFPBits::FRACTION_LEN, InFPBits::FRACTION_LEN);
67*bb722a7dSDimitry Andric     DyadicFloat<cpp::bit_ceil(MAX_FRACTION_LEN)> xd(x);
68*bb722a7dSDimitry Andric     return xd.template as<OutType, /*ShouldSignalExceptions=*/true>();
69*bb722a7dSDimitry Andric   }
70*bb722a7dSDimitry Andric 
71*bb722a7dSDimitry Andric   return static_cast<OutType>(x);
72*bb722a7dSDimitry Andric }
73*bb722a7dSDimitry Andric 
74*bb722a7dSDimitry Andric } // namespace LIBC_NAMESPACE::fputil
75*bb722a7dSDimitry Andric 
76*bb722a7dSDimitry Andric #endif // LLVM_LIBC_SRC___SUPPORT_FPUTIL_CAST_H
77