bevy/crates/bevy_math/src/ops.rs
Zachary Harrold a8b9c945c7
Add no_std Support to bevy_math (#15810)
# Objective

- Contributes to #15460

## Solution

- Added two new features, `std` (default) and `alloc`, gating `std` and
`alloc` behind them respectively.
- Added missing `f32` functions to `std_ops` as required. These `f32`
methods have been added to the `clippy.toml` deny list to aid in
`no_std` development.

## Testing

- CI
- `cargo clippy -p bevy_math --no-default-features --features libm
--target "x86_64-unknown-none"`
- `cargo test -p bevy_math --no-default-features --features libm`
- `cargo test -p bevy_math --no-default-features --features "libm,
alloc"`
- `cargo test -p bevy_math --no-default-features --features "libm,
alloc, std"`
- `cargo test -p bevy_math --no-default-features --features "std"`

## Notes

The following items require the `alloc` feature to be enabled:

- `CubicBSpline`
- `CubicBezier`
- `CubicCardinalSpline`
- `CubicCurve`
- `CubicGenerator`
- `CubicHermite`
- `CubicNurbs`
- `CyclicCubicGenerator`
- `RationalCurve`
- `RationalGenerator`
- `BoxedPolygon`
- `BoxedPolyline2d`
- `BoxedPolyline3d`
- `SampleCurve`
- `SampleAutoCurve`
- `UnevenSampleCurve`
- `UnevenSampleAutoCurve`
- `EvenCore`
- `UnevenCore`
- `ChunkedUnevenCore`

This requirement could be relaxed in certain cases, but I had erred on
the side of gating rather than modifying. Since `no_std` is a new set of
platforms we are adding support to, and the `alloc` feature is enabled
by default, this is not a breaking change.

---------

Co-authored-by: Benjamin Brienen <benjamin.brienen@outlook.com>
Co-authored-by: Matty <2975848+mweatherley@users.noreply.github.com>
Co-authored-by: Joona Aalto <jondolf.dev@gmail.com>
2024-12-03 17:14:51 +00:00

622 lines
18 KiB
Rust

//! This mod re-exports the correct versions of floating-point operations with
//! unspecified precision in the standard library depending on whether the `libm`
//! crate feature is enabled.
//!
//! All the functions here are named according to their versions in the standard
//! library.
//!
//! It also provides `no_std` compatible alternatives to certain floating-point
//! operations which are not provided in the [`core`] library.
#![allow(dead_code)]
#![allow(clippy::disallowed_methods)]
// Note: There are some Rust methods with unspecified precision without a `libm`
// equivalent:
// - `f32::powi` (integer powers)
// - `f32::log` (logarithm with specified base)
// - `f32::abs_sub` (actually unsure if `libm` has this, but don't use it regardless)
//
// Additionally, the following nightly API functions are not presently integrated
// into this, but they would be candidates once standardized:
// - `f32::gamma`
// - `f32::ln_gamma`
#[cfg(not(feature = "libm"))]
mod std_ops {
/// Raises a number to a floating point power.
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn powf(x: f32, y: f32) -> f32 {
f32::powf(x, y)
}
/// Returns `e^(self)`, (the exponential function).
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn exp(x: f32) -> f32 {
f32::exp(x)
}
/// Returns `2^(self)`.
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn exp2(x: f32) -> f32 {
f32::exp2(x)
}
/// Returns the natural logarithm of the number.
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn ln(x: f32) -> f32 {
f32::ln(x)
}
/// Returns the base 2 logarithm of the number.
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn log2(x: f32) -> f32 {
f32::log2(x)
}
/// Returns the base 10 logarithm of the number.
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn log10(x: f32) -> f32 {
f32::log10(x)
}
/// Returns the cube root of a number.
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn cbrt(x: f32) -> f32 {
f32::cbrt(x)
}
/// Compute the distance between the origin and a point `(x, y)` on the Euclidean plane.
/// Equivalently, compute the length of the hypotenuse of a right-angle triangle with other sides having length `x.abs()` and `y.abs()`.
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn hypot(x: f32, y: f32) -> f32 {
f32::hypot(x, y)
}
/// Computes the sine of a number (in radians).
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn sin(x: f32) -> f32 {
f32::sin(x)
}
/// Computes the cosine of a number (in radians).
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn cos(x: f32) -> f32 {
f32::cos(x)
}
/// Computes the tangent of a number (in radians).
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn tan(x: f32) -> f32 {
f32::tan(x)
}
/// Computes the arcsine of a number. Return value is in radians in
/// the range [-pi/2, pi/2] or NaN if the number is outside the range
/// [-1, 1].
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn asin(x: f32) -> f32 {
f32::asin(x)
}
/// Computes the arccosine of a number. Return value is in radians in
/// the range [0, pi] or NaN if the number is outside the range
/// [-1, 1].
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn acos(x: f32) -> f32 {
f32::acos(x)
}
/// Computes the arctangent of a number. Return value is in radians in the
/// range [-pi/2, pi/2];
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn atan(x: f32) -> f32 {
f32::atan(x)
}
/// Computes the four quadrant arctangent of `self` (`y`) and `other` (`x`) in radians.
///
/// * `x = 0`, `y = 0`: `0`
/// * `x >= 0`: `arctan(y/x)` -> `[-pi/2, pi/2]`
/// * `y >= 0`: `arctan(y/x) + pi` -> `(pi/2, pi]`
/// * `y < 0`: `arctan(y/x) - pi` -> `(-pi, -pi/2)`
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn atan2(x: f32, y: f32) -> f32 {
f32::atan2(x, y)
}
/// Simultaneously computes the sine and cosine of the number, `x`. Returns
/// `(sin(x), cos(x))`.
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn sin_cos(x: f32) -> (f32, f32) {
f32::sin_cos(x)
}
/// Returns `e^(self) - 1` in a way that is accurate even if the
/// number is close to zero.
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn exp_m1(x: f32) -> f32 {
f32::exp_m1(x)
}
/// Returns `ln(1+n)` (natural logarithm) more accurately than if
/// the operations were performed separately.
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn ln_1p(x: f32) -> f32 {
f32::ln_1p(x)
}
/// Hyperbolic sine function.
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn sinh(x: f32) -> f32 {
f32::sinh(x)
}
/// Hyperbolic cosine function.
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn cosh(x: f32) -> f32 {
f32::cosh(x)
}
/// Hyperbolic tangent function.
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn tanh(x: f32) -> f32 {
f32::tanh(x)
}
/// Inverse hyperbolic sine function.
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn asinh(x: f32) -> f32 {
f32::asinh(x)
}
/// Inverse hyperbolic cosine function.
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn acosh(x: f32) -> f32 {
f32::acosh(x)
}
/// Inverse hyperbolic tangent function.
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn atanh(x: f32) -> f32 {
f32::atanh(x)
}
}
#[cfg(feature = "libm")]
mod libm_ops {
/// Raises a number to a floating point power.
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn powf(x: f32, y: f32) -> f32 {
libm::powf(x, y)
}
/// Returns `e^(self)`, (the exponential function).
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn exp(x: f32) -> f32 {
libm::expf(x)
}
/// Returns `2^(self)`.
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn exp2(x: f32) -> f32 {
libm::exp2f(x)
}
/// Returns the natural logarithm of the number.
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn ln(x: f32) -> f32 {
// This isn't documented in `libm` but this is actually the base e logarithm.
libm::logf(x)
}
/// Returns the base 2 logarithm of the number.
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn log2(x: f32) -> f32 {
libm::log2f(x)
}
/// Returns the base 10 logarithm of the number.
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn log10(x: f32) -> f32 {
libm::log10f(x)
}
/// Returns the cube root of a number.
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn cbrt(x: f32) -> f32 {
libm::cbrtf(x)
}
/// Compute the distance between the origin and a point `(x, y)` on the Euclidean plane.
///
/// Equivalently, compute the length of the hypotenuse of a right-angle triangle with other sides having length `x.abs()` and `y.abs()`.
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn hypot(x: f32, y: f32) -> f32 {
libm::hypotf(x, y)
}
/// Computes the sine of a number (in radians).
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn sin(x: f32) -> f32 {
libm::sinf(x)
}
/// Computes the cosine of a number (in radians).
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn cos(x: f32) -> f32 {
libm::cosf(x)
}
/// Computes the tangent of a number (in radians).
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn tan(x: f32) -> f32 {
libm::tanf(x)
}
/// Computes the arcsine of a number. Return value is in radians in
/// the range [-pi/2, pi/2] or NaN if the number is outside the range
/// [-1, 1].
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn asin(x: f32) -> f32 {
libm::asinf(x)
}
/// Computes the arccosine of a number. Return value is in radians in
/// Hyperbolic tangent function.
///
/// Precision is specified when the `libm` feature is enabled.
/// the range [0, pi] or NaN if the number is outside the range
/// [-1, 1].
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn acos(x: f32) -> f32 {
libm::acosf(x)
}
/// Computes the arctangent of a number. Return value is in radians in the
/// range [-pi/2, pi/2];
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn atan(x: f32) -> f32 {
libm::atanf(x)
}
/// Computes the four quadrant arctangent of `self` (`y`) and `other` (`x`) in radians.
///
/// * `x = 0`, `y = 0`: `0`
/// * `x >= 0`: `arctan(y/x)` -> `[-pi/2, pi/2]`
/// * `y >= 0`: `arctan(y/x) + pi` -> `(pi/2, pi]`
/// * `y < 0`: `arctan(y/x) - pi` -> `(-pi, -pi/2)`
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn atan2(x: f32, y: f32) -> f32 {
libm::atan2f(x, y)
}
/// Simultaneously computes the sine and cosine of the number, `x`. Returns
/// `(sin(x), cos(x))`.
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn sin_cos(x: f32) -> (f32, f32) {
libm::sincosf(x)
}
/// Returns `e^(self) - 1` in a way that is accurate even if the
/// number is close to zero.
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn exp_m1(x: f32) -> f32 {
libm::expm1f(x)
}
/// Returns `ln(1+n)` (natural logarithm) more accurately than if
/// the operations were performed separately.
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn ln_1p(x: f32) -> f32 {
libm::log1pf(x)
}
/// Hyperbolic sine function.
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn sinh(x: f32) -> f32 {
libm::sinhf(x)
}
/// Hyperbolic cosine function.
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn cosh(x: f32) -> f32 {
libm::coshf(x)
}
/// Hyperbolic tangent function.
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn tanh(x: f32) -> f32 {
libm::tanhf(x)
}
/// Inverse hyperbolic sine function.
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn asinh(x: f32) -> f32 {
libm::asinhf(x)
}
/// Inverse hyperbolic cosine function.
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn acosh(x: f32) -> f32 {
libm::acoshf(x)
}
/// Inverse hyperbolic tangent function.
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn atanh(x: f32) -> f32 {
libm::atanhf(x)
}
}
#[cfg(all(feature = "libm", not(feature = "std")))]
mod libm_ops_for_no_std {
//! Provides standardized names for [`f32`] operations which may not be
//! supported on `no_std` platforms.
//! On `no_std` platforms, this forwards to the implementations provided
//! by [`libm`].
/// Calculates the least nonnegative remainder of `self (mod rhs)`.
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn rem_euclid(x: f32, y: f32) -> f32 {
let result = libm::remainderf(x, y);
// libm::remainderf has a range of -y/2 to +y/2
if result < 0. {
result + y
} else {
result
}
}
/// Computes the absolute value of x.
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn abs(x: f32) -> f32 {
libm::fabsf(x)
}
/// Returns the square root of a number.
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn sqrt(x: f32) -> f32 {
libm::sqrtf(x)
}
/// Returns a number composed of the magnitude of `x` and the sign of `y`.
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn copysign(x: f32, y: f32) -> f32 {
libm::copysignf(x, y)
}
/// Returns the nearest integer to `x`. If a value is half-way between two integers, round away from `0.0`.
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn round(x: f32) -> f32 {
libm::roundf(x)
}
/// Returns the largest integer less than or equal to `x`.
///
/// Precision is specified when the `libm` feature is enabled.
#[inline(always)]
pub fn floor(x: f32) -> f32 {
libm::floorf(x)
}
/// Returns the fractional part of `x`.
///
/// This function always returns the precise result.
#[inline(always)]
pub fn fract(x: f32) -> f32 {
libm::modff(x).0
}
}
#[cfg(feature = "std")]
mod std_ops_for_no_std {
//! Provides standardized names for [`f32`] operations which may not be
//! supported on `no_std` platforms.
//! On `std` platforms, this forwards directly to the implementations provided
//! by [`std`].
/// Calculates the least nonnegative remainder of `x (mod y)`.
///
/// The result of this operation is guaranteed to be the rounded infinite-precision result.
#[inline(always)]
pub fn rem_euclid(x: f32, y: f32) -> f32 {
f32::rem_euclid(x, y)
}
/// Computes the absolute value of x.
///
/// This function always returns the precise result.
#[inline(always)]
pub fn abs(x: f32) -> f32 {
f32::abs(x)
}
/// Returns the square root of a number.
///
/// The result of this operation is guaranteed to be the rounded infinite-precision result.
/// It is specified by IEEE 754 as `squareRoot` and guaranteed not to change.
#[inline(always)]
pub fn sqrt(x: f32) -> f32 {
f32::sqrt(x)
}
/// Returns a number composed of the magnitude of `x` and the sign of `y`.
///
/// Equal to `x` if the sign of `x` and `y` are the same, otherwise equal to `-x`. If `x` is a
/// `NaN`, then a `NaN` with the sign bit of `y` is returned. Note, however, that conserving the
/// sign bit on `NaN` across arithmetical operations is not generally guaranteed.
#[inline(always)]
pub fn copysign(x: f32, y: f32) -> f32 {
f32::copysign(x, y)
}
/// Returns the nearest integer to `x`. If a value is half-way between two integers, round away from `0.0`.
///
/// This function always returns the precise result.
#[inline(always)]
pub fn round(x: f32) -> f32 {
f32::round(x)
}
/// Returns the largest integer less than or equal to `x`.
///
/// This function always returns the precise result.
#[inline(always)]
pub fn floor(x: f32) -> f32 {
f32::floor(x)
}
/// Returns the fractional part of `x`.
///
/// This function always returns the precise result.
#[inline(always)]
pub fn fract(x: f32) -> f32 {
f32::fract(x)
}
}
#[cfg(feature = "libm")]
pub use libm_ops::*;
#[cfg(not(feature = "libm"))]
pub use std_ops::*;
#[cfg(feature = "std")]
pub use std_ops_for_no_std::*;
#[cfg(all(feature = "libm", not(feature = "std")))]
pub use libm_ops_for_no_std::*;
#[cfg(all(not(feature = "libm"), not(feature = "std")))]
compile_error!("Either the `libm` feature or the `std` feature must be enabled.");
/// This extension trait covers shortfall in determinacy from the lack of a `libm` counterpart
/// to `f32::powi`. Use this for the common small exponents.
pub trait FloatPow {
/// Squares the f32
fn squared(self) -> Self;
/// Cubes the f32
fn cubed(self) -> Self;
}
impl FloatPow for f32 {
#[inline]
fn squared(self) -> Self {
self * self
}
#[inline]
fn cubed(self) -> Self {
self * self * self
}
}