mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-23 01:21:42 +00:00
775 lines
29 KiB
C++
775 lines
29 KiB
C++
// Copyright (c) FIRST and other WPILib contributors.
|
|
// Open Source Software; you can modify and/or share it under the terms of
|
|
// the WPILib BSD license file in the root directory of this project.
|
|
|
|
// Copyright (c) 2016 Nic Holthaus
|
|
//
|
|
// The MIT License (MIT)
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
// SOFTWARE.
|
|
|
|
#pragma once
|
|
|
|
#include <cmath>
|
|
|
|
#include <wpi/math>
|
|
|
|
#include "units/angle.h"
|
|
#include "units/base.h"
|
|
#include "units/dimensionless.h"
|
|
|
|
//----------------------------------
|
|
// UNIT-ENABLED CMATH FUNCTIONS
|
|
//----------------------------------
|
|
|
|
/**
|
|
* @brief namespace for unit-enabled versions of the `<cmath>` library
|
|
* @details Includes trigonometric functions, exponential/log functions,
|
|
* rounding functions, etc.
|
|
* @sa See `unit_t` for more information on unit type containers.
|
|
*/
|
|
namespace units::math {
|
|
//----------------------------------
|
|
// TRIGONOMETRIC FUNCTIONS
|
|
//----------------------------------
|
|
|
|
/**
|
|
* @ingroup UnitMath
|
|
* @brief Compute cosine
|
|
* @details The input value can be in any unit of angle, including radians or
|
|
* degrees.
|
|
* @tparam AngleUnit any `unit_t` type of `category::angle_unit`.
|
|
* @param[in] angle angle to compute the cosine of
|
|
* @returns Returns the cosine of <i>angle</i>
|
|
*/
|
|
#if !defined(DISABLE_PREDEFINED_UNITS) || defined(ENABLE_PREDEFINED_ANGLE_UNITS)
|
|
template <class AngleUnit>
|
|
dimensionless::scalar_t cos(const AngleUnit angle) noexcept {
|
|
static_assert(
|
|
traits::is_angle_unit<AngleUnit>::value,
|
|
"Type `AngleUnit` must be a unit of angle derived from `unit_t`.");
|
|
return dimensionless::scalar_t(
|
|
std::cos(angle.template convert<angle::radian>()()));
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* @ingroup UnitMath
|
|
* @brief Compute sine
|
|
* @details The input value can be in any unit of angle, including radians or
|
|
* degrees.
|
|
* @tparam AngleUnit any `unit_t` type of `category::angle_unit`.
|
|
* @param[in] angle angle to compute the since of
|
|
* @returns Returns the sine of <i>angle</i>
|
|
*/
|
|
#if !defined(DISABLE_PREDEFINED_UNITS) || defined(ENABLE_PREDEFINED_ANGLE_UNITS)
|
|
template <class AngleUnit>
|
|
dimensionless::scalar_t sin(const AngleUnit angle) noexcept {
|
|
static_assert(
|
|
traits::is_angle_unit<AngleUnit>::value,
|
|
"Type `AngleUnit` must be a unit of angle derived from `unit_t`.");
|
|
return dimensionless::scalar_t(
|
|
std::sin(angle.template convert<angle::radian>()()));
|
|
}
|
|
#endif
|
|
/**
|
|
* @ingroup UnitMath
|
|
* @brief Compute tangent
|
|
* @details The input value can be in any unit of angle, including radians or
|
|
* degrees.
|
|
* @tparam AngleUnit any `unit_t` type of `category::angle_unit`.
|
|
* @param[in] angle angle to compute the tangent of
|
|
* @returns Returns the tangent of <i>angle</i>
|
|
*/
|
|
#if !defined(DISABLE_PREDEFINED_UNITS) || defined(ENABLE_PREDEFINED_ANGLE_UNITS)
|
|
template <class AngleUnit>
|
|
dimensionless::scalar_t tan(const AngleUnit angle) noexcept {
|
|
static_assert(
|
|
traits::is_angle_unit<AngleUnit>::value,
|
|
"Type `AngleUnit` must be a unit of angle derived from `unit_t`.");
|
|
return dimensionless::scalar_t(
|
|
std::tan(angle.template convert<angle::radian>()()));
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* @ingroup UnitMath
|
|
* @brief Compute arc cosine
|
|
* @details Returns the principal value of the arc cosine of x, expressed in
|
|
* radians.
|
|
* @param[in] x Value whose arc cosine is computed, in the interval [-1,+1].
|
|
* @returns Principal arc cosine of x, in the interval [0,pi] radians.
|
|
*/
|
|
#if !defined(DISABLE_PREDEFINED_UNITS) || defined(ENABLE_PREDEFINED_ANGLE_UNITS)
|
|
template <class ScalarUnit>
|
|
angle::radian_t acos(const ScalarUnit x) noexcept {
|
|
static_assert(
|
|
traits::is_dimensionless_unit<ScalarUnit>::value,
|
|
"Type `ScalarUnit` must be a dimensionless unit derived from `unit_t`.");
|
|
return angle::radian_t(std::acos(x()));
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* @ingroup UnitMath
|
|
* @brief Compute arc sine
|
|
* @details Returns the principal value of the arc sine of x, expressed in
|
|
* radians.
|
|
* @param[in] x Value whose arc sine is computed, in the interval [-1,+1].
|
|
* @returns Principal arc sine of x, in the interval [-pi/2,+pi/2] radians.
|
|
*/
|
|
#if !defined(DISABLE_PREDEFINED_UNITS) || defined(ENABLE_PREDEFINED_ANGLE_UNITS)
|
|
template <class ScalarUnit>
|
|
angle::radian_t asin(const ScalarUnit x) noexcept {
|
|
static_assert(
|
|
traits::is_dimensionless_unit<ScalarUnit>::value,
|
|
"Type `ScalarUnit` must be a dimensionless unit derived from `unit_t`.");
|
|
return angle::radian_t(std::asin(x()));
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* @ingroup UnitMath
|
|
* @brief Compute arc tangent
|
|
* @details Returns the principal value of the arc tangent of x, expressed in
|
|
* radians. Notice that because of the sign ambiguity, the function
|
|
* cannot determine with certainty in which quadrant the angle falls
|
|
* only by its tangent value. See atan2 for an alternative that takes a
|
|
* fractional argument instead.
|
|
* @tparam AngleUnit any `unit_t` type of `category::angle_unit`.
|
|
* @param[in] x Value whose arc tangent is computed, in the interval [-1,+1].
|
|
* @returns Principal arc tangent of x, in the interval [-pi/2,+pi/2] radians.
|
|
*/
|
|
#if !defined(DISABLE_PREDEFINED_UNITS) || defined(ENABLE_PREDEFINED_ANGLE_UNITS)
|
|
template <class ScalarUnit>
|
|
angle::radian_t atan(const ScalarUnit x) noexcept {
|
|
static_assert(
|
|
traits::is_dimensionless_unit<ScalarUnit>::value,
|
|
"Type `ScalarUnit` must be a dimensionless unit derived from `unit_t`.");
|
|
return angle::radian_t(std::atan(x()));
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* @ingroup UnitMath
|
|
* @brief Compute arc tangent with two parameters
|
|
* @details To compute the value, the function takes into account the sign of
|
|
* both arguments in order to determine the quadrant.
|
|
* @param[in] y y-component of the triangle expressed.
|
|
* @param[in] x x-component of the triangle expressed.
|
|
* @returns Returns the principal value of the arc tangent of <i>y/x</i>,
|
|
* expressed in radians.
|
|
*/
|
|
#if !defined(DISABLE_PREDEFINED_UNITS) || defined(ENABLE_PREDEFINED_ANGLE_UNITS)
|
|
template <class Y, class X>
|
|
angle::radian_t atan2(const Y y, const X x) noexcept {
|
|
static_assert(traits::is_dimensionless_unit<decltype(y / x)>::value,
|
|
"The quantity y/x must yield a dimensionless ratio.");
|
|
|
|
// X and Y could be different length units, so normalize them
|
|
return angle::radian_t(
|
|
std::atan2(y.template convert<
|
|
typename units::traits::unit_t_traits<X>::unit_type>()(),
|
|
x()));
|
|
}
|
|
#endif
|
|
|
|
//----------------------------------
|
|
// HYPERBOLIC TRIG FUNCTIONS
|
|
//----------------------------------
|
|
|
|
/**
|
|
* @ingroup UnitMath
|
|
* @brief Compute hyperbolic cosine
|
|
* @details The input value can be in any unit of angle, including radians or
|
|
* degrees.
|
|
* @tparam AngleUnit any `unit_t` type of `category::angle_unit`.
|
|
* @param[in] angle angle to compute the hyperbolic cosine of
|
|
* @returns Returns the hyperbolic cosine of <i>angle</i>
|
|
*/
|
|
#if !defined(DISABLE_PREDEFINED_UNITS) || defined(ENABLE_PREDEFINED_ANGLE_UNITS)
|
|
template <class AngleUnit>
|
|
dimensionless::scalar_t cosh(const AngleUnit angle) noexcept {
|
|
static_assert(
|
|
traits::is_angle_unit<AngleUnit>::value,
|
|
"Type `AngleUnit` must be a unit of angle derived from `unit_t`.");
|
|
return dimensionless::scalar_t(
|
|
std::cosh(angle.template convert<angle::radian>()()));
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* @ingroup UnitMath
|
|
* @brief Compute hyperbolic sine
|
|
* @details The input value can be in any unit of angle, including radians or
|
|
* degrees.
|
|
* @tparam AngleUnit any `unit_t` type of `category::angle_unit`.
|
|
* @param[in] angle angle to compute the hyperbolic sine of
|
|
* @returns Returns the hyperbolic sine of <i>angle</i>
|
|
*/
|
|
#if !defined(DISABLE_PREDEFINED_UNITS) || defined(ENABLE_PREDEFINED_ANGLE_UNITS)
|
|
template <class AngleUnit>
|
|
dimensionless::scalar_t sinh(const AngleUnit angle) noexcept {
|
|
static_assert(
|
|
traits::is_angle_unit<AngleUnit>::value,
|
|
"Type `AngleUnit` must be a unit of angle derived from `unit_t`.");
|
|
return dimensionless::scalar_t(
|
|
std::sinh(angle.template convert<angle::radian>()()));
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* @ingroup UnitMath
|
|
* @brief Compute hyperbolic tangent
|
|
* @details The input value can be in any unit of angle, including radians or
|
|
* degrees.
|
|
* @tparam AngleUnit any `unit_t` type of `category::angle_unit`.
|
|
* @param[in] angle angle to compute the hyperbolic tangent of
|
|
* @returns Returns the hyperbolic tangent of <i>angle</i>
|
|
*/
|
|
#if !defined(DISABLE_PREDEFINED_UNITS) || defined(ENABLE_PREDEFINED_ANGLE_UNITS)
|
|
template <class AngleUnit>
|
|
dimensionless::scalar_t tanh(const AngleUnit angle) noexcept {
|
|
static_assert(
|
|
traits::is_angle_unit<AngleUnit>::value,
|
|
"Type `AngleUnit` must be a unit of angle derived from `unit_t`.");
|
|
return dimensionless::scalar_t(
|
|
std::tanh(angle.template convert<angle::radian>()()));
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* @ingroup UnitMath
|
|
* @brief Compute arc hyperbolic cosine
|
|
* @details Returns the nonnegative arc hyperbolic cosine of x, expressed in
|
|
* radians.
|
|
* @param[in] x Value whose arc hyperbolic cosine is computed. If the argument
|
|
* is less than 1, a domain error occurs.
|
|
* @returns Nonnegative arc hyperbolic cosine of x, in the interval
|
|
* [0,+INFINITY] radians.
|
|
*/
|
|
#if !defined(DISABLE_PREDEFINED_UNITS) || defined(ENABLE_PREDEFINED_ANGLE_UNITS)
|
|
template <class ScalarUnit>
|
|
angle::radian_t acosh(const ScalarUnit x) noexcept {
|
|
static_assert(
|
|
traits::is_dimensionless_unit<ScalarUnit>::value,
|
|
"Type `ScalarUnit` must be a dimensionless unit derived from `unit_t`.");
|
|
return angle::radian_t(std::acosh(x()));
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* @ingroup UnitMath
|
|
* @brief Compute arc hyperbolic sine
|
|
* @details Returns the arc hyperbolic sine of x, expressed in radians.
|
|
* @param[in] x Value whose arc hyperbolic sine is computed.
|
|
* @returns Arc hyperbolic sine of x, in radians.
|
|
*/
|
|
#if !defined(DISABLE_PREDEFINED_UNITS) || defined(ENABLE_PREDEFINED_ANGLE_UNITS)
|
|
template <class ScalarUnit>
|
|
angle::radian_t asinh(const ScalarUnit x) noexcept {
|
|
static_assert(
|
|
traits::is_dimensionless_unit<ScalarUnit>::value,
|
|
"Type `ScalarUnit` must be a dimensionless unit derived from `unit_t`.");
|
|
return angle::radian_t(std::asinh(x()));
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* @ingroup UnitMath
|
|
* @brief Compute arc hyperbolic tangent
|
|
* @details Returns the arc hyperbolic tangent of x, expressed in radians.
|
|
* @param[in] x Value whose arc hyperbolic tangent is computed, in the interval
|
|
* [-1,+1]. If the argument is out of this interval, a domain error
|
|
* occurs. For values of -1 and +1, a pole error may occur.
|
|
* @returns units::angle::radian_t
|
|
*/
|
|
#if !defined(DISABLE_PREDEFINED_UNITS) || defined(ENABLE_PREDEFINED_ANGLE_UNITS)
|
|
template <class ScalarUnit>
|
|
angle::radian_t atanh(const ScalarUnit x) noexcept {
|
|
static_assert(
|
|
traits::is_dimensionless_unit<ScalarUnit>::value,
|
|
"Type `ScalarUnit` must be a dimensionless unit derived from `unit_t`.");
|
|
return angle::radian_t(std::atanh(x()));
|
|
}
|
|
#endif
|
|
|
|
//----------------------------------
|
|
// TRANSCENDENTAL FUNCTIONS
|
|
//----------------------------------
|
|
|
|
// it makes NO SENSE to put dimensioned units into a transcendental function,
|
|
// and if you think it does you are demonstrably wrong.
|
|
// https://en.wikipedia.org/wiki/Transcendental_function#Dimensional_analysis
|
|
|
|
/**
|
|
* @ingroup UnitMath
|
|
* @brief Compute exponential function
|
|
* @details Returns the base-e exponential function of x, which is e raised to
|
|
* the power x: ex.
|
|
* @param[in] x scalar value of the exponent.
|
|
* @returns Exponential value of x.
|
|
* If the magnitude of the result is too large to be represented by a
|
|
* value of the return type, the function returns HUGE_VAL (or
|
|
* HUGE_VALF or HUGE_VALL) with the proper sign, and an overflow range
|
|
* error occurs.
|
|
*/
|
|
template <class ScalarUnit>
|
|
dimensionless::scalar_t exp(const ScalarUnit x) noexcept {
|
|
static_assert(
|
|
traits::is_dimensionless_unit<ScalarUnit>::value,
|
|
"Type `ScalarUnit` must be a dimensionless unit derived from `unit_t`.");
|
|
return dimensionless::scalar_t(std::exp(x()));
|
|
}
|
|
|
|
/**
|
|
* @ingroup UnitMath
|
|
* @brief Compute natural logarithm
|
|
* @details Returns the natural logarithm of x.
|
|
* @param[in] x scalar value whose logarithm is calculated. If the argument is
|
|
* negative, a domain error occurs.
|
|
* @sa log10 for more common base-10 logarithms
|
|
* @returns Natural logarithm of x.
|
|
*/
|
|
template <class ScalarUnit>
|
|
dimensionless::scalar_t log(const ScalarUnit x) noexcept {
|
|
static_assert(
|
|
traits::is_dimensionless_unit<ScalarUnit>::value,
|
|
"Type `ScalarUnit` must be a dimensionless unit derived from `unit_t`.");
|
|
return dimensionless::scalar_t(std::log(x()));
|
|
}
|
|
|
|
/**
|
|
* @ingroup UnitMath
|
|
* @brief Compute common logarithm
|
|
* @details Returns the common (base-10) logarithm of x.
|
|
* @param[in] x Value whose logarithm is calculated. If the argument is
|
|
* negative, a domain error occurs.
|
|
* @returns Common logarithm of x.
|
|
*/
|
|
template <class ScalarUnit>
|
|
dimensionless::scalar_t log10(const ScalarUnit x) noexcept {
|
|
static_assert(
|
|
traits::is_dimensionless_unit<ScalarUnit>::value,
|
|
"Type `ScalarUnit` must be a dimensionless unit derived from `unit_t`.");
|
|
return dimensionless::scalar_t(std::log10(x()));
|
|
}
|
|
|
|
/**
|
|
* @ingroup UnitMath
|
|
* @brief Break into fractional and integral parts.
|
|
* @details The integer part is stored in the object pointed by intpart, and the
|
|
* fractional part is returned by the function. Both parts have the
|
|
* same sign as x.
|
|
* @param[in] x scalar value to break into parts.
|
|
* @param[in] intpart Pointer to an object (of the same type as x) where the
|
|
* integral part is stored with the same sign as x.
|
|
* @returns The fractional part of x, with the same sign.
|
|
*/
|
|
template <class ScalarUnit>
|
|
dimensionless::scalar_t modf(const ScalarUnit x, ScalarUnit* intpart) noexcept {
|
|
static_assert(
|
|
traits::is_dimensionless_unit<ScalarUnit>::value,
|
|
"Type `ScalarUnit` must be a dimensionless unit derived from `unit_t`.");
|
|
|
|
UNIT_LIB_DEFAULT_TYPE intp;
|
|
dimensionless::scalar_t fracpart =
|
|
dimensionless::scalar_t(std::modf(x(), &intp));
|
|
*intpart = intp;
|
|
return fracpart;
|
|
}
|
|
|
|
/**
|
|
* @ingroup UnitMath
|
|
* @brief Compute binary exponential function
|
|
* @details Returns the base-2 exponential function of x, which is 2 raised to
|
|
* the power x: 2^x. 2param[in] x Value of the exponent.
|
|
* @returns 2 raised to the power of x.
|
|
*/
|
|
template <class ScalarUnit>
|
|
dimensionless::scalar_t exp2(const ScalarUnit x) noexcept {
|
|
static_assert(
|
|
traits::is_dimensionless_unit<ScalarUnit>::value,
|
|
"Type `ScalarUnit` must be a dimensionless unit derived from `unit_t`.");
|
|
return dimensionless::scalar_t(std::exp2(x()));
|
|
}
|
|
|
|
/**
|
|
* @ingroup UnitMath
|
|
* @brief Compute exponential minus one
|
|
* @details Returns e raised to the power x minus one: e^x-1. For small
|
|
* magnitude values of x, expm1 may be more accurate than exp(x)-1.
|
|
* @param[in] x Value of the exponent.
|
|
* @returns e raised to the power of x, minus one.
|
|
*/
|
|
template <class ScalarUnit>
|
|
dimensionless::scalar_t expm1(const ScalarUnit x) noexcept {
|
|
static_assert(
|
|
traits::is_dimensionless_unit<ScalarUnit>::value,
|
|
"Type `ScalarUnit` must be a dimensionless unit derived from `unit_t`.");
|
|
return dimensionless::scalar_t(std::expm1(x()));
|
|
}
|
|
|
|
/**
|
|
* @ingroup UnitMath
|
|
* @brief Compute logarithm plus one
|
|
* @details Returns the natural logarithm of one plus x. For small magnitude
|
|
* values of x, logp1 may be more accurate than log(1+x).
|
|
* @param[in] x Value whose logarithm is calculated. If the argument is less
|
|
* than -1, a domain error occurs.
|
|
* @returns The natural logarithm of (1+x).
|
|
*/
|
|
template <class ScalarUnit>
|
|
dimensionless::scalar_t log1p(const ScalarUnit x) noexcept {
|
|
static_assert(
|
|
traits::is_dimensionless_unit<ScalarUnit>::value,
|
|
"Type `ScalarUnit` must be a dimensionless unit derived from `unit_t`.");
|
|
return dimensionless::scalar_t(std::log1p(x()));
|
|
}
|
|
|
|
/**
|
|
* @ingroup UnitMath
|
|
* @brief Compute binary logarithm
|
|
* @details Returns the binary (base-2) logarithm of x.
|
|
* @param[in] x Value whose logarithm is calculated. If the argument is
|
|
* negative, a domain error occurs.
|
|
* @returns The binary logarithm of x: log2x.
|
|
*/
|
|
template <class ScalarUnit>
|
|
dimensionless::scalar_t log2(const ScalarUnit x) noexcept {
|
|
static_assert(
|
|
traits::is_dimensionless_unit<ScalarUnit>::value,
|
|
"Type `ScalarUnit` must be a dimensionless unit derived from `unit_t`.");
|
|
return dimensionless::scalar_t(std::log2(x()));
|
|
}
|
|
|
|
//----------------------------------
|
|
// POWER FUNCTIONS
|
|
//----------------------------------
|
|
|
|
/* pow is implemented earlier in the library since a lot of the unit definitions
|
|
* depend on it */
|
|
|
|
/**
|
|
* @ingroup UnitMath
|
|
* @brief computes the square root of <i>value</i>
|
|
* @details Only implemented for linear_scale units.
|
|
* @param[in] value `unit_t` derived type to compute the square root of.
|
|
* @returns new unit_t, whose units are the square root of value's.
|
|
* E.g. if values had units of `square_meter`, then the return type
|
|
* will have units of `meter`.
|
|
* @note `sqrt` provides a _rational approximation_ of the square root of
|
|
* <i>value</i>. In some cases, _both_ the returned value _and_ conversion
|
|
* factor of the returned unit type may have errors no larger than
|
|
* `1e-10`.
|
|
*/
|
|
template <
|
|
class UnitType,
|
|
std::enable_if_t<units::traits::has_linear_scale<UnitType>::value, int> = 0>
|
|
inline auto sqrt(const UnitType& value) noexcept -> unit_t<
|
|
square_root<typename units::traits::unit_t_traits<UnitType>::unit_type>,
|
|
typename units::traits::unit_t_traits<UnitType>::underlying_type,
|
|
linear_scale> {
|
|
return unit_t<
|
|
square_root<typename units::traits::unit_t_traits<UnitType>::unit_type>,
|
|
typename units::traits::unit_t_traits<UnitType>::underlying_type,
|
|
linear_scale>(std::sqrt(value()));
|
|
}
|
|
|
|
/**
|
|
* @ingroup UnitMath
|
|
* @brief Computes the square root of the sum-of-squares of x and y.
|
|
* @details Only implemented for linear_scale units.
|
|
* @param[in] x unit_t type value
|
|
* @param[in] y unit_t type value
|
|
* @returns square root of the sum-of-squares of x and y in the same units as x.
|
|
*/
|
|
template <class UnitTypeLhs, class UnitTypeRhs,
|
|
std::enable_if_t<
|
|
units::traits::has_linear_scale<UnitTypeLhs, UnitTypeRhs>::value,
|
|
int> = 0>
|
|
inline UnitTypeLhs hypot(const UnitTypeLhs& x, const UnitTypeRhs& y) {
|
|
static_assert(traits::is_convertible_unit_t<UnitTypeLhs, UnitTypeRhs>::value,
|
|
"Parameters of hypot() function are not compatible units.");
|
|
return UnitTypeLhs(std::hypot(
|
|
x(),
|
|
y.template convert<
|
|
typename units::traits::unit_t_traits<UnitTypeLhs>::unit_type>()()));
|
|
}
|
|
|
|
//----------------------------------
|
|
// ROUNDING FUNCTIONS
|
|
//----------------------------------
|
|
|
|
/**
|
|
* @ingroup UnitMath
|
|
* @brief Round up value
|
|
* @details Rounds x upward, returning the smallest integral value that is not
|
|
* less than x.
|
|
* @param[in] x Unit value to round up.
|
|
* @returns The smallest integral value that is not less than x.
|
|
*/
|
|
template <class UnitType,
|
|
class = std::enable_if_t<traits::is_unit_t<UnitType>::value>>
|
|
UnitType ceil(const UnitType x) noexcept {
|
|
return UnitType(std::ceil(x()));
|
|
}
|
|
|
|
/**
|
|
* @ingroup UnitMath
|
|
* @brief Round down value
|
|
* @details Rounds x downward, returning the largest integral value that is not
|
|
* greater than x.
|
|
* @param[in] x Unit value to round down.
|
|
* @returns The value of x rounded downward.
|
|
*/
|
|
template <class UnitType,
|
|
class = std::enable_if_t<traits::is_unit_t<UnitType>::value>>
|
|
UnitType floor(const UnitType x) noexcept {
|
|
return UnitType(std::floor(x()));
|
|
}
|
|
|
|
/**
|
|
* @ingroup UnitMath
|
|
* @brief Compute remainder of division
|
|
* @details Returns the floating-point remainder of numer/denom (rounded towards
|
|
* zero).
|
|
* @param[in] numer Value of the quotient numerator.
|
|
* @param[in] denom Value of the quotient denominator.
|
|
* @returns The remainder of dividing the arguments.
|
|
*/
|
|
template <class UnitTypeLhs, class UnitTypeRhs,
|
|
class = std::enable_if_t<traits::is_unit_t<UnitTypeLhs>::value &&
|
|
traits::is_unit_t<UnitTypeRhs>::value>>
|
|
UnitTypeLhs fmod(const UnitTypeLhs numer, const UnitTypeRhs denom) noexcept {
|
|
static_assert(traits::is_convertible_unit_t<UnitTypeLhs, UnitTypeRhs>::value,
|
|
"Parameters of fmod() function are not compatible units.");
|
|
return UnitTypeLhs(std::fmod(
|
|
numer(),
|
|
denom.template convert<
|
|
typename units::traits::unit_t_traits<UnitTypeLhs>::unit_type>()()));
|
|
}
|
|
|
|
/**
|
|
* @ingroup UnitMath
|
|
* @brief Truncate value
|
|
* @details Rounds x toward zero, returning the nearest integral value that is
|
|
* not larger in magnitude than x. Effectively rounds towards 0.
|
|
* @param[in] x Value to truncate
|
|
* @returns The nearest integral value that is not larger in magnitude than x.
|
|
*/
|
|
template <class UnitType,
|
|
class = std::enable_if_t<traits::is_unit_t<UnitType>::value>>
|
|
UnitType trunc(const UnitType x) noexcept {
|
|
return UnitType(std::trunc(x()));
|
|
}
|
|
|
|
/**
|
|
* @ingroup UnitMath
|
|
* @brief Round to nearest
|
|
* @details Returns the integral value that is nearest to x, with halfway cases
|
|
* rounded away from zero.
|
|
* @param[in] x value to round.
|
|
* @returns The value of x rounded to the nearest integral.
|
|
*/
|
|
template <class UnitType,
|
|
class = std::enable_if_t<traits::is_unit_t<UnitType>::value>>
|
|
UnitType round(const UnitType x) noexcept {
|
|
return UnitType(std::round(x()));
|
|
}
|
|
|
|
//----------------------------------
|
|
// FLOATING POINT MANIPULATION
|
|
//----------------------------------
|
|
|
|
/**
|
|
* @ingroup UnitMath
|
|
* @brief Copy sign
|
|
* @details Returns a value with the magnitude and dimension of x, and the sign
|
|
* of y. Values x and y do not have to be compatible units.
|
|
* @param[in] x Value with the magnitude of the resulting value.
|
|
* @param[in] y Value with the sign of the resulting value.
|
|
* @returns value with the magnitude and dimension of x, and the sign of y.
|
|
*/
|
|
template <class UnitTypeLhs, class UnitTypeRhs,
|
|
class = std::enable_if_t<traits::is_unit_t<UnitTypeLhs>::value &&
|
|
traits::is_unit_t<UnitTypeRhs>::value>>
|
|
UnitTypeLhs copysign(const UnitTypeLhs x, const UnitTypeRhs y) noexcept {
|
|
return UnitTypeLhs(std::copysign(
|
|
x(), y())); // no need for conversion to get the correct sign.
|
|
}
|
|
|
|
/// Overload to copy the sign from a raw double
|
|
template <class UnitTypeLhs,
|
|
class = std::enable_if_t<traits::is_unit_t<UnitTypeLhs>::value>>
|
|
UnitTypeLhs copysign(const UnitTypeLhs x,
|
|
const UNIT_LIB_DEFAULT_TYPE y) noexcept {
|
|
return UnitTypeLhs(std::copysign(x(), y));
|
|
}
|
|
|
|
//----------------------------------
|
|
// MIN / MAX / DIFFERENCE
|
|
//----------------------------------
|
|
|
|
/**
|
|
* @ingroup UnitMath
|
|
* @brief Positive difference
|
|
* @details The function returns x-y if x>y, and zero otherwise, in the same
|
|
* units as x. Values x and y do not have to be the same type of units,
|
|
* but they do have to be compatible.
|
|
* @param[in] x Values whose difference is calculated.
|
|
* @param[in] y Values whose difference is calculated.
|
|
* @returns The positive difference between x and y.
|
|
*/
|
|
template <class UnitTypeLhs, class UnitTypeRhs,
|
|
class = std::enable_if_t<traits::is_unit_t<UnitTypeLhs>::value &&
|
|
traits::is_unit_t<UnitTypeRhs>::value>>
|
|
UnitTypeLhs fdim(const UnitTypeLhs x, const UnitTypeRhs y) noexcept {
|
|
static_assert(traits::is_convertible_unit_t<UnitTypeLhs, UnitTypeRhs>::value,
|
|
"Parameters of fdim() function are not compatible units.");
|
|
return UnitTypeLhs(std::fdim(
|
|
x(),
|
|
y.template convert<
|
|
typename units::traits::unit_t_traits<UnitTypeLhs>::unit_type>()()));
|
|
}
|
|
|
|
/**
|
|
* @ingroup UnitMath
|
|
* @brief Maximum value
|
|
* @details Returns the larger of its arguments: either x or y, in the same
|
|
* units as x. Values x and y do not have to be the same type of units,
|
|
* but they do have to be compatible.
|
|
* @param[in] x Values among which the function selects a maximum.
|
|
* @param[in] y Values among which the function selects a maximum.
|
|
* @returns The maximum numeric value of its arguments.
|
|
*/
|
|
template <class UnitTypeLhs, class UnitTypeRhs,
|
|
class = std::enable_if_t<traits::is_unit_t<UnitTypeLhs>::value &&
|
|
traits::is_unit_t<UnitTypeRhs>::value>>
|
|
UnitTypeLhs fmax(const UnitTypeLhs x, const UnitTypeRhs y) noexcept {
|
|
static_assert(traits::is_convertible_unit_t<UnitTypeLhs, UnitTypeRhs>::value,
|
|
"Parameters of fmax() function are not compatible units.");
|
|
return UnitTypeLhs(std::fmax(
|
|
x(),
|
|
y.template convert<
|
|
typename units::traits::unit_t_traits<UnitTypeLhs>::unit_type>()()));
|
|
}
|
|
|
|
/**
|
|
* @ingroup UnitMath
|
|
* @brief Minimum value
|
|
* @details Returns the smaller of its arguments: either x or y, in the same
|
|
* units as x. If one of the arguments in a NaN, the other is returned.
|
|
* Values x and y do not have to be the same type of units, but they do
|
|
* have to be compatible.
|
|
* @param[in] x Values among which the function selects a minimum.
|
|
* @param[in] y Values among which the function selects a minimum.
|
|
* @returns The minimum numeric value of its arguments.
|
|
*/
|
|
template <class UnitTypeLhs, class UnitTypeRhs,
|
|
class = std::enable_if_t<traits::is_unit_t<UnitTypeLhs>::value &&
|
|
traits::is_unit_t<UnitTypeRhs>::value>>
|
|
UnitTypeLhs fmin(const UnitTypeLhs x, const UnitTypeRhs y) noexcept {
|
|
static_assert(traits::is_convertible_unit_t<UnitTypeLhs, UnitTypeRhs>::value,
|
|
"Parameters of fmin() function are not compatible units.");
|
|
return UnitTypeLhs(std::fmin(
|
|
x(),
|
|
y.template convert<
|
|
typename units::traits::unit_t_traits<UnitTypeLhs>::unit_type>()()));
|
|
}
|
|
|
|
//----------------------------------
|
|
// OTHER FUNCTIONS
|
|
//----------------------------------
|
|
|
|
/**
|
|
* @ingroup UnitMath
|
|
* @brief Compute absolute value
|
|
* @details Returns the absolute value of x, i.e. |x|.
|
|
* @param[in] x Value whose absolute value is returned.
|
|
* @returns The absolute value of x.
|
|
*/
|
|
template <class UnitType,
|
|
class = std::enable_if_t<traits::is_unit_t<UnitType>::value>>
|
|
UnitType fabs(const UnitType x) noexcept {
|
|
return UnitType(std::fabs(x()));
|
|
}
|
|
|
|
/**
|
|
* @ingroup UnitMath
|
|
* @brief Compute absolute value
|
|
* @details Returns the absolute value of x, i.e. |x|.
|
|
* @param[in] x Value whose absolute value is returned.
|
|
* @returns The absolute value of x.
|
|
*/
|
|
template <class UnitType,
|
|
class = std::enable_if_t<traits::is_unit_t<UnitType>::value>>
|
|
UnitType abs(const UnitType x) noexcept {
|
|
return UnitType(std::fabs(x()));
|
|
}
|
|
|
|
/**
|
|
* @ingroup UnitMath
|
|
* @brief Multiply-add
|
|
* @details Returns x*y+z. The function computes the result without losing
|
|
* precision in any intermediate result. The resulting unit type is a
|
|
* compound unit of x* y.
|
|
* @param[in] x Values to be multiplied.
|
|
* @param[in] y Values to be multiplied.
|
|
* @param[in] z Value to be added.
|
|
* @returns The result of x*y+z
|
|
*/
|
|
template <class UnitTypeLhs, class UnitMultiply, class UnitAdd,
|
|
class = std::enable_if_t<traits::is_unit_t<UnitTypeLhs>::value &&
|
|
traits::is_unit_t<UnitMultiply>::value &&
|
|
traits::is_unit_t<UnitAdd>::value>>
|
|
auto fma(const UnitTypeLhs x, const UnitMultiply y, const UnitAdd z) noexcept
|
|
-> decltype(x * y) {
|
|
using resultType = decltype(x * y);
|
|
static_assert(
|
|
traits::is_convertible_unit_t<
|
|
compound_unit<
|
|
typename units::traits::unit_t_traits<UnitTypeLhs>::unit_type,
|
|
typename units::traits::unit_t_traits<UnitMultiply>::unit_type>,
|
|
typename units::traits::unit_t_traits<UnitAdd>::unit_type>::value,
|
|
"Unit types are not compatible.");
|
|
return resultType(std::fma(x(), y(), resultType(z)()));
|
|
}
|
|
|
|
/**
|
|
* Constrains theta to within the range (-pi, pi].
|
|
*
|
|
* @param theta Angle to normalize.
|
|
*/
|
|
constexpr units::radian_t NormalizeAngle(units::radian_t theta) {
|
|
units::radian_t pi(wpi::math::pi);
|
|
|
|
// Constrain theta to within (-3pi, pi)
|
|
const int n_pi_pos = (theta + pi) / 2.0 / pi;
|
|
theta = theta - units::radian_t{n_pi_pos * 2.0 * wpi::math::pi};
|
|
|
|
// Cut off the bottom half of the above range to constrain within
|
|
// (-pi, pi]
|
|
const int n_pi_neg = (theta - pi) / 2.0 / pi;
|
|
theta = theta - units::radian_t{n_pi_neg * 2.0 * wpi::math::pi};
|
|
|
|
return theta;
|
|
}
|
|
} // namespace units::math
|