Skip to content

Commit

Permalink
FMA familly with precision options
Browse files Browse the repository at this point in the history
  • Loading branch information
jtlap authored Oct 1, 2024
1 parent d0aed9c commit 73de402
Show file tree
Hide file tree
Showing 50 changed files with 815 additions and 162 deletions.
68 changes: 68 additions & 0 deletions include/eve/module/core.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,74 @@
//! @ingroup core
//! Core semantic modifiers
//!
//! Many core function semantics can be modified using decorator(s). The complete description of their effects can be found in
//! the proper documentation page of each implied function.
//!
//! They can be classified in the following way:
//! * general behaviour
//! - `raw`: indicates that the operation is performed to gain speed generally at the expanse of some accuracy
//! or/and proper treament of limting values.
//!
//! Concerned functions are eve::average, eve::diff_of_prod, eve::exponent, eve::frac, eve::frexp, eve::ifrexp,
//! eve::mantissa, eve::modf, eve::next, eve::prev, eve::rec, eve::rsqrt, eve::sqrt, eve::sum_of_prod, eve::trunc,
//! - `numeric` : indicates that the operation will aim to ignore Nans as possible.
//!
//! Concerned functions are eve::absmax, eve::absmin, eve::is_equal, eve::is_not_equal, eve::max, eve::maxabs,
//! eve::maxmag, eve::min, eve::minabs, eve::minmag, eve::minmax, eve::negabsmax, eve::negabsmin, eve::negmaxabs,
//! eve::negminabs,
//!
//! - `pedantic` : indicates that the operation will aim to follow existing **C++** standard.
//!
//! Concerned functions are eve::absmax, eve::absmin, eve::diff_of_prod, eve::dist, eve::fam, eve::fanm,
//! eve::fma, eve::fms, eve::fnma, eve::fnms, eve::frac, eve::frexp, eve::fsm, eve::fsnm, eve::ifrexp, eve::is_flint,
//! eve::is_negative, eve::is_not_flint, eve::is_not_infinite, eve::is_unit, eve::ldexp, eve::lerp, eve::manhattan, eve::max,
//! eve::maxabs, eve::maxmag, eve::min, eve::minabs, eve::minmag, eve::minmax, eve::modf, eve::negabsmax,
//! eve::negabsmin, eve::negmaxabs, eve::negminabs, eve::next, eve::nextafter, eve::prev, eve::rec, eve::reldist, eve::rsqrt, eve::signnz, eve::sum_of_prod,
//!
//! * integer roundings :
//!
//! These decorators can be used with the functions
//! eve::div, eve::rem, eve::round with floating or integral arguments
//! to choose the rounding to integer mode
//!
//! - `to_nearest`: troundint to nearest or even
//! - `downward`: rounding toward \f$-\infty\f$
//! - `upward`: rounding toward \f$+\infty\f$
//! - `toward_zero`: rounding toward zero
//!
//! All these decorators can be used with the functions eve::div, eve::rem, eve::round.
//!
//! * floating point roundings;
//!
//! - `lower`: the computed result of the floating operation is less than the mathematical exact value
//! - `upper`: the computed result of the floating operation is greater than the mathematical exact value
//! - `strict`: combined with lower or upper option strict ensures that the inequalities obtained are strict.
//!
//! These decorators can be used with the functions
//! eve::add, eve::average, eve::dec, eve::div, eve::fma, eve::fms, eve::inc,
//! eve::mul, eve::oneminus, eve::rec, eve::sqr, eve::sqrt, eve::sub.
//!
//! Also `lower` and `upper` (but not `strict`) can be used with all floating point constants.
//!
//! Except for average with integral typed inputs these decocators have no impact on integer calls.
//!
//! * Fuzzy
//!
//! - `almost`: allows some laxity on the predicate result or the integer rounding direction
//! - `definitely`: impose some rigidity on the predicate result or the integer rounding direction
//!
//! these two decorators can be used with the functions eve::ceil, eve::floor, eve::frac, eve::modf, eve::trunc,
//! `almost` with the predicates eve::is_equal, eve::is_greater_equal, eve::is_less_equal, eve::is_not_greater, eve::is_not_less,
//! `definitely` with the predicates eve::is_not_equal, eve::is_not_greater_equal, ieve::s_not_less_equal, eve::is_greater, eve::is_less,
//!
//! * saturation
//!
//! - saturated: the operations are executed with saturation which avoids overflow.
//!
//! This option can be used with eve::abs, eve::absmax, eve::absmin, eve::add, eve::bit_floor, eve::convert, eve::dec, eve::dist,
//! eve::div, eve::inc, eve::manhattan, eve::maxabs, eve::maxmag, eve::minabs, eve::minmag, eve::minus, eve::mul,
//! eve::negabsmax, eve::negabsmin, eve::negmaxabs, eve::negminabs, eve::next, eve::oneminus, eve::prev, eve::sqr, eve::sub
//!
//! @defgroup core_fma_family Fused multiply add family
//! @ingroup core
//! These functions implements accurate versions of the operations
Expand Down
7 changes: 4 additions & 3 deletions include/eve/module/core/regular/diff_of_prod.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,16 +92,17 @@ namespace eve
T const &a, T const &b,
T const &c, T const &d) noexcept
{
auto cd = c*d;
if constexpr(O::contains(raw))
{
return fms(a, b, cd);
T cd = mul(c, d);
return fms(a, b, cd);
}
else
{
auto cd = mul[o](c, d);
auto err = fnma[o](c, d, cd);
auto dop = fms[o](a, b, cd);
return add[is_finite(err)](dop, err);
return add[o][is_finite(err)](dop, err);
}
}
}
Expand Down
7 changes: 4 additions & 3 deletions include/eve/module/core/regular/dist.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
namespace eve
{
template<typename Options>
struct dist_t : elementwise_callable<dist_t, Options, saturated_option, pedantic_option>
struct dist_t : elementwise_callable<dist_t, Options, saturated_option, pedantic_option,
upper_option, lower_option, strict_option>
{
template<value T, value U>
requires(eve::same_lanes_or_scalar<T, U>)
Expand Down Expand Up @@ -89,9 +90,9 @@ namespace eve
namespace detail
{
template<value T, callable_options O>
constexpr T dist_(EVE_REQUIRES(cpu_), O const&, T a, T b)
constexpr T dist_(EVE_REQUIRES(cpu_), O const& o, T a, T b)
{
T d = eve::max(a, b) - eve::min(a, b);
T d = sub[o](eve::max(a, b), eve::min(a, b));
if constexpr(O::contains(saturated) && signed_integral_value<T>)
return if_else(is_ltz(d), valmax(eve::as(d)), d);
else if constexpr(O::contains(pedantic) && floating_value<T>)
Expand Down
3 changes: 2 additions & 1 deletion include/eve/module/core/regular/fam.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
namespace eve
{
template<typename Options>
struct fam_t : strict_elementwise_callable<fam_t, Options, pedantic_option, promote_option>
struct fam_t : strict_elementwise_callable<fam_t, Options, pedantic_option, promote_option,
lower_option, upper_option, strict_option>
{
template<eve::value T,eve::value U,eve::value V>
requires(Options::contains(promote))
Expand Down
3 changes: 2 additions & 1 deletion include/eve/module/core/regular/fanm.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
namespace eve
{
template<typename Options>
struct fanm_t : strict_elementwise_callable<fanm_t, Options, pedantic_option, promote_option>
struct fanm_t : strict_elementwise_callable<fanm_t, Options, pedantic_option, promote_option,
lower_option, upper_option, strict_option>
{
template<eve::value T,eve::value U,eve::value V>
requires(Options::contains(promote))
Expand Down
12 changes: 11 additions & 1 deletion include/eve/module/core/regular/fms.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
namespace eve
{
template<typename Options>
struct fms_t : strict_elementwise_callable<fms_t, Options, pedantic_option, promote_option>
struct fms_t : strict_elementwise_callable<fms_t, Options, pedantic_option, promote_option,
upper_option, lower_option, strict_option>
{
template<eve::value T,eve::value U,eve::value V>
requires(Options::contains(promote))
Expand Down Expand Up @@ -86,6 +87,15 @@ namespace eve
# include <eve/module/core/regular/impl/simd/x86/fms.hpp>
#endif

#if defined(EVE_INCLUDE_POWERPC_HEADER)
# include <eve/module/core/regular/impl/simd/ppc/fms.hpp>
#endif


#if defined(EVE_INCLUDE_ARM_HEADER)
# include <eve/module/core/regular/impl/simd/arm/neon/fms.hpp>
#endif

#if defined(EVE_INCLUDE_SVE_HEADER)
# include <eve/module/core/regular/impl/simd/arm/sve/fms.hpp>
#endif
3 changes: 2 additions & 1 deletion include/eve/module/core/regular/fnma.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
namespace eve
{
template<typename Options>
struct fnma_t : strict_elementwise_callable<fnma_t, Options, pedantic_option, promote_option>
struct fnma_t : strict_elementwise_callable<fnma_t, Options, pedantic_option, promote_option,
lower_option, upper_option, strict_option>
{
template<eve::value T,eve::value U,eve::value V>
requires(Options::contains(promote))
Expand Down
9 changes: 7 additions & 2 deletions include/eve/module/core/regular/fnms.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
namespace eve
{
template<typename Options>
struct fnms_t : strict_elementwise_callable<fnms_t, Options, pedantic_option, promote_option>
struct fnms_t : strict_elementwise_callable<fnms_t, Options, pedantic_option, promote_option,
lower_option, upper_option, strict_option>
{
template<eve::value T,eve::value U,eve::value V>
requires(Options::contains(promote))
Expand Down Expand Up @@ -92,7 +93,11 @@ namespace eve
template<typename T, typename U, typename V, callable_options O>
EVE_FORCEINLINE constexpr auto fnms_(EVE_REQUIRES(cpu_), O const& o, T const& a, U const& b, V const& c)
{
return minus(fma[o](a, b, c));
if constexpr(O::contains(upper) || O::contains(lower))
{
return fma[o](a, minus(b), minus(c));
}
else return minus(fma[o](a, b, c));
}

}
Expand Down
3 changes: 2 additions & 1 deletion include/eve/module/core/regular/fsm.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
namespace eve
{
template<typename Options>
struct fsm_t : strict_elementwise_callable<fsm_t, Options, pedantic_option, promote_option>
struct fsm_t : strict_elementwise_callable<fsm_t, Options, pedantic_option, promote_option,
lower_option, upper_option, strict_option>
{
template<eve::value T,eve::value U,eve::value V>
requires(Options::contains(promote))
Expand Down
3 changes: 2 additions & 1 deletion include/eve/module/core/regular/fsnm.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
namespace eve
{
template<typename Options>
struct fsnm_t : strict_elementwise_callable<fsnm_t, Options, pedantic_option, promote_option>
struct fsnm_t : strict_elementwise_callable<fsnm_t, Options, pedantic_option, promote_option,
lower_option, upper_option, strict_option>
{
template<value T, value U, value V>
requires(Options::contains(promote))
Expand Down
4 changes: 2 additions & 2 deletions include/eve/module/core/regular/impl/fam.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,8 @@ namespace eve::detail
return fam(a, b, c);
}
}
// REGULAR ---------------------
// REGULAR UPPER LOWER---------------------
else
return fma(b, c, a);
return fma[o](b, c, a);
}
}
30 changes: 28 additions & 2 deletions include/eve/module/core/regular/impl/fms.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@
#include <eve/forward.hpp>
#include <eve/module/core/regular/bit_cast.hpp>
#include <eve/module/core/regular/convert.hpp>
#include <eve/module/core/regular/is_ltz.hpp>
#include <eve/module/core/regular/is_gtz.hpp>
#include <eve/module/core/regular/next.hpp>
#include <eve/module/core/regular/prev.hpp>
#include <eve/module/core/regular/fma.hpp>
#include <eve/module/core/regular/three_fma.hpp>
#include <eve/traits/as_integer.hpp>
#include <eve/traits/common_value.hpp>
#include <cmath>
Expand Down Expand Up @@ -66,9 +72,29 @@ namespace eve::detail
template<typename T, callable_options O>
EVE_FORCEINLINE constexpr auto fms_(EVE_REQUIRES(cpu_), O const& o, T const& a, T const& b, T const& c)
{
// UPPER LOWER ---------------------
if constexpr(floating_value<T> && (O::contains(upper) || O::contains(lower)))
{
if constexpr(O::contains(strict) )
{
auto r = eve::fms[o.drop(lower, upper)](a, b, c);
if constexpr(O::contains(lower))
return eve::prev(r);
else
return eve::next(r);
}
else
{
auto [r, e, e1] = eve::three_fma(a, b, -c);
if constexpr(O::contains(lower))
return eve::prev[eve::is_ltz(e+e1)](r);
else
return eve::next[eve::is_gtz(e+e1)](r);
}
}
// PROMOTE ---------------------
// We promote before going pedantic in case it changes the behavior
if constexpr(O::contains(promote)) return fms[o.drop(promote)](a,b,c);
else if constexpr(O::contains(promote)) return fms[o.drop(promote)](a,b,c);
// PEDANTIC ---------------------
else if constexpr(O::contains(pedantic))
{
Expand All @@ -92,7 +118,7 @@ namespace eve::detail
constexpr auto tgt = as<as_integer_t<T, unsigned>>{};
return bit_cast(fms(bit_cast(a,tgt), bit_cast(b,tgt), bit_cast(c,tgt)), as<T>());
}
else return fms(a, b, c);
else return fma(a, b, -c);
}
// REGULAR ---------------------
else return a * b - c;
Expand Down
24 changes: 24 additions & 0 deletions include/eve/module/core/regular/impl/simd/arm/neon/fms.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//==================================================================================================
/*
EVE - Expressive Vector Engine
Copyright : EVE Project Contributors
SPDX-License-Identifier: BSL-1.0
*/
//==================================================================================================
#pragma once

#include <eve/concept/value.hpp>
#include <eve/detail/abi.hpp>
#include <eve/forward.hpp>

namespace eve::detail
{
template<typename T, typename N, callable_options O>
EVE_FORCEINLINE wide<T, N>
fms_(EVE_REQUIRES(neon128_), O const& o, wide<T, N> const& a, wide<T, N> const& b, wide<T, N> const& c) noexcept
requires arm_abi<abi_t<T, N>>
{
return fma[o](a, b, -c);

}
}
10 changes: 6 additions & 4 deletions include/eve/module/core/regular/impl/simd/arm/neon/fnma.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,23 @@ namespace eve::detail
template<arithmetic_scalar_value T, typename N, callable_options O>
EVE_FORCEINLINE wide<T, N>
fnma_(EVE_REQUIRES(neon128_),
O const&,
O const& o,
wide<T, N> const &v0,
wide<T, N> const &v1,
wide<T, N> const &v2) noexcept requires arm_abi<abi_t<T, N>>
{
constexpr auto cat = categorize<wide<T, N>>();

if constexpr( cat == category::float32x2 ) return vfms_f32(v2, v1, v0);
if constexpr(O::contains(lower) || O::contains(upper))
return fnma.behavior(cpu_{}, o, v0, v1, v2);
else if constexpr( cat == category::float32x2 ) return vfms_f32(v2, v1, v0);
else if constexpr( cat == category::float32x4 ) return vfmsq_f32(v2, v1, v0);
else if constexpr( current_api >= asimd )
{
if constexpr( cat == category::float64x1 ) return vfms_f64(v2, v1, v0);
else if constexpr( cat == category::float64x2 ) return vfmsq_f64(v2, v1, v0);
else return fma(-v0, v1, v2);
else return fma[o](-v0, v1, v2);
}
else return fma(-v0, v1, v2);
else return fma[o](-v0, v1, v2);
}
}
26 changes: 17 additions & 9 deletions include/eve/module/core/regular/impl/simd/arm/sve/fam.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,30 @@ namespace eve::detail
{
template<typename T, typename N, callable_options O>
requires sve_abi<abi_t<T, N>>
EVE_FORCEINLINE wide<T, N> fam_(EVE_REQUIRES(sve_), O const&, wide<T, N> a, wide<T, N> b, wide<T, N> c) noexcept
EVE_FORCEINLINE wide<T, N> fam_(EVE_REQUIRES(sve_), O const& opts, wide<T, N> a, wide<T, N> b, wide<T, N> c) noexcept
{
// We don't care about PEDANTIC as this is a proper FMA.
// We don't care about PROMOTE as we only accept similar types.
return svmla_x(sve_true<T>(), a, b, c);
if constexpr(O::contains(lower) || O::contains(upper))
return fam.behavior(cpu_{}, opts, a, b, c);
else
return svmla_x(sve_true<T>(), a, b, c);
}

template<conditional_expr C, typename T, typename N, callable_options O>
requires sve_abi<abi_t<T, N>>
EVE_FORCEINLINE wide<T, N> fam_(EVE_REQUIRES(sve_), C cond, O const&, wide<T,N> a, wide<T,N> b, wide<T,N> c) noexcept
EVE_FORCEINLINE wide<T, N> fam_(EVE_REQUIRES(sve_), C cond, O const& opts, wide<T,N> a, wide<T,N> b, wide<T,N> c) noexcept
{
// We don't care about PEDANTIC as this is a proper FMA.
// We don't care about PROMOTE as we only accept similar types.
[[maybe_unused]] auto const alt = alternative(cond, a, as(a));
if constexpr( C::is_complete ) return alt;
else if constexpr( !C::has_alternative ) return svmla_m(cond.mask(as<T>{}), a, b, c);
else return if_else(cond, eve::fam(a, b, c), alt);
if constexpr(O::contains(lower) || O::contains(upper))
return fam.behavior(cpu_{}, opts, a, b, c);
else
{
// We don't care about PEDANTIC as this is a proper FMA.
// We don't care about PROMOTE as we only accept similar types.
[[maybe_unused]] auto const alt = alternative(cond, a, as(a));
if constexpr( C::is_complete ) return alt;
else if constexpr( !C::has_alternative ) return svmla_m(cond.mask(as<T>{}), a, b, c);
else return if_else(cond, eve::fam(a, b, c), alt);
}
}
}
Loading

0 comments on commit 73de402

Please sign in to comment.