Loading...
Searching...
No Matches
juce_MathsFunctions.h
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/*
This file sets up some handy mathematical typdefs and functions.
*/
//==============================================================================
// Definitions for the int8, int16, int32, int64 and pointer_sized_int types.
using int8 = signed char;
using uint8 = unsigned char;
using int16 = signed short;
using uint16 = unsigned short;
using int32 = signed int;
using uint32 = unsigned int;
#if JUCE_MSVC
using int64 = __int64;
using uint64 = unsigned __int64;
#else
using int64 = long long;
using uint64 = unsigned long long;
#endif
#ifndef DOXYGEN
#define literal64bit(longLiteral) (longLiteral##LL)
#endif
#if JUCE_64BIT
using pointer_sized_int = int64;
using pointer_sized_uint = uint64;
#elif JUCE_MSVC
using pointer_sized_int = _W64 int;
using pointer_sized_uint = _W64 unsigned int;
#else
using pointer_sized_int = int;
using pointer_sized_uint = unsigned int;
#endif
#if JUCE_WINDOWS && ! JUCE_MINGW
using ssize_t = pointer_sized_int;
#endif
//==============================================================================
template <typename... Types>
void ignoreUnused (Types&&...) noexcept {}
template <typename Type, size_t N>
constexpr int numElementsInArray (Type (&)[N]) noexcept { return N; }
//==============================================================================
// Some useful maths functions that aren't always present with all compilers and build settings.
template <typename Type>
Type juce_hypot (Type a, Type b) noexcept
{
#if JUCE_MSVC
return static_cast<Type> (_hypot (a, b));
#else
return static_cast<Type> (hypot (a, b));
#endif
}
#ifndef DOXYGEN
template <>
inline float juce_hypot (float a, float b) noexcept
{
#if JUCE_MSVC
return _hypotf (a, b);
#else
return hypotf (a, b);
#endif
}
#endif
//==============================================================================
template <typename FloatType>
struct MathConstants
{
static constexpr FloatType pi = static_cast<FloatType> (3.141592653589793238L);
static constexpr FloatType twoPi = static_cast<FloatType> (2 * 3.141592653589793238L);
static constexpr FloatType halfPi = static_cast<FloatType> (3.141592653589793238L / 2);
static constexpr FloatType euler = static_cast<FloatType> (2.71828182845904523536L);
static constexpr FloatType sqrt2 = static_cast<FloatType> (1.4142135623730950488L);
};
#ifndef DOXYGEN
[[deprecated ("This is deprecated in favour of MathConstants<double>::pi.")]]
const constexpr double double_Pi = MathConstants<double>::pi;
[[deprecated ("This is deprecated in favour of MathConstants<float>::pi.")]]
const constexpr float float_Pi = MathConstants<float>::pi;
#endif
template <typename FloatType>
constexpr FloatType degreesToRadians (FloatType degrees) noexcept { return degrees * (MathConstants<FloatType>::pi / FloatType (180)); }
template <typename FloatType>
constexpr FloatType radiansToDegrees (FloatType radians) noexcept { return radians * (FloatType (180) / MathConstants<FloatType>::pi); }
//==============================================================================
template <typename NumericType>
bool juce_isfinite (NumericType value) noexcept
{
if constexpr (std::numeric_limits<NumericType>::has_infinity
|| std::numeric_limits<NumericType>::has_quiet_NaN
|| std::numeric_limits<NumericType>::has_signaling_NaN)
{
return std::isfinite (value);
}
else
{
ignoreUnused (value);
return true;
}
}
//==============================================================================
template <typename Type>
constexpr bool exactlyEqual (Type a, Type b)
{
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wfloat-equal")
return a == b;
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
}
template <typename Type>
class Tolerance
{
public:
Tolerance() = default;
[[nodiscard]] Tolerance withAbsolute (Type newAbsolute)
{
return withMember (*this, &Tolerance::absolute, std::abs (newAbsolute));
}
[[nodiscard]] Tolerance withRelative (Type newRelative)
{
return withMember (*this, &Tolerance::relative, std::abs (newRelative));
}
[[nodiscard]] Type getAbsolute() const { return absolute; }
[[nodiscard]] Type getRelative() const { return relative; }
private:
Type absolute{};
Type relative{};
};
template <typename Type>
static Tolerance<Type> absoluteTolerance (Type tolerance)
{
return Tolerance<Type>{}.withAbsolute (tolerance);
}
template <typename Type>
static Tolerance<Type> relativeTolerance (Type tolerance)
{
return Tolerance<Type>{}.withRelative (tolerance);
}
template <typename Type, std::enable_if_t<std::is_floating_point_v<Type>, int> = 0>
constexpr bool approximatelyEqual (Type a, Type b,
Tolerance<Type> tolerance = Tolerance<Type>{}
.withAbsolute (std::numeric_limits<Type>::min())
.withRelative (std::numeric_limits<Type>::epsilon()))
{
if (! (juce_isfinite (a) && juce_isfinite (b)))
return exactlyEqual (a, b);
const auto diff = std::abs (a - b);
return diff <= tolerance.getAbsolute()
|| diff <= tolerance.getRelative() * std::max (std::abs (a), std::abs (b));
}
template <typename Type, std::enable_if_t<! std::is_floating_point_v<Type>, int> = 0>
constexpr bool approximatelyEqual (Type a, Type b)
{
return a == b;
}
//==============================================================================
template <typename FloatType>
FloatType nextFloatUp (FloatType value) noexcept
{
return std::nextafter (value, std::numeric_limits<FloatType>::max());
}
template <typename FloatType>
FloatType nextFloatDown (FloatType value) noexcept
{
return std::nextafter (value, std::numeric_limits<FloatType>::lowest());
}
//==============================================================================
// Some indispensable min/max functions
template <typename Type>
constexpr Type jmax (Type a, Type b) { return a < b ? b : a; }
template <typename Type>
constexpr Type jmax (Type a, Type b, Type c) { return a < b ? (b < c ? c : b) : (a < c ? c : a); }
template <typename Type>
constexpr Type jmax (Type a, Type b, Type c, Type d) { return jmax (a, jmax (b, c, d)); }
template <typename Type>
constexpr Type jmin (Type a, Type b) { return b < a ? b : a; }
template <typename Type>
constexpr Type jmin (Type a, Type b, Type c) { return b < a ? (c < b ? c : b) : (c < a ? c : a); }
template <typename Type>
constexpr Type jmin (Type a, Type b, Type c, Type d) { return jmin (a, jmin (b, c, d)); }
template <typename Type>
constexpr Type jmap (Type value0To1, Type targetRangeMin, Type targetRangeMax)
{
return targetRangeMin + value0To1 * (targetRangeMax - targetRangeMin);
}
template <typename Type>
Type jmap (Type sourceValue, Type sourceRangeMin, Type sourceRangeMax, Type targetRangeMin, Type targetRangeMax)
{
jassert (! approximatelyEqual (sourceRangeMax, sourceRangeMin)); // mapping from a range of zero will produce NaN!
return targetRangeMin + ((targetRangeMax - targetRangeMin) * (sourceValue - sourceRangeMin)) / (sourceRangeMax - sourceRangeMin);
}
template <typename Type>
Type mapToLog10 (Type value0To1, Type logRangeMin, Type logRangeMax)
{
jassert (logRangeMin > 0);
jassert (logRangeMax > 0);
auto logMin = std::log10 (logRangeMin);
auto logMax = std::log10 (logRangeMax);
return std::pow ((Type) 10.0, value0To1 * (logMax - logMin) + logMin);
}
template <typename Type>
Type mapFromLog10 (Type valueInLogRange, Type logRangeMin, Type logRangeMax)
{
jassert (logRangeMin > 0);
jassert (logRangeMax > 0);
auto logMin = std::log10 (logRangeMin);
auto logMax = std::log10 (logRangeMax);
return (std::log10 (valueInLogRange) - logMin) / (logMax - logMin);
}
template <typename Type, typename Size>
Type findMinimum (const Type* data, Size numValues)
{
if (numValues <= 0)
return Type (0);
auto result = *data++;
while (--numValues > 0) // (> 0 rather than >= 0 because we've already taken the first sample)
{
auto v = *data++;
if (v < result)
result = v;
}
return result;
}
template <typename Type, typename Size>
Type findMaximum (const Type* values, Size numValues)
{
if (numValues <= 0)
return Type (0);
auto result = *values++;
while (--numValues > 0) // (> 0 rather than >= 0 because we've already taken the first sample)
{
auto v = *values++;
if (result < v)
result = v;
}
return result;
}
template <typename Type>
void findMinAndMax (const Type* values, int numValues, Type& lowest, Type& highest)
{
if (numValues <= 0)
{
lowest = Type (0);
highest = Type (0);
}
else
{
auto mn = *values++;
auto mx = mn;
while (--numValues > 0) // (> 0 rather than >= 0 because we've already taken the first sample)
{
auto v = *values++;
if (mx < v) mx = v;
if (v < mn) mn = v;
}
lowest = mn;
highest = mx;
}
}
//==============================================================================
template <typename Type>
Type jlimit (Type lowerLimit,
Type upperLimit,
Type valueToConstrain) noexcept
{
jassert (lowerLimit <= upperLimit); // if these are in the wrong order, results are unpredictable..
return valueToConstrain < lowerLimit ? lowerLimit
: (upperLimit < valueToConstrain ? upperLimit
: valueToConstrain);
}
template <typename Type1, typename Type2>
bool isPositiveAndBelow (Type1 valueToTest, Type2 upperLimit) noexcept
{
jassert (Type1() <= static_cast<Type1> (upperLimit)); // makes no sense to call this if the upper limit is itself below zero..
return Type1() <= valueToTest && valueToTest < static_cast<Type1> (upperLimit);
}
template <typename Type>
bool isPositiveAndBelow (int valueToTest, Type upperLimit) noexcept
{
jassert (upperLimit >= 0); // makes no sense to call this if the upper limit is itself below zero..
return static_cast<unsigned int> (valueToTest) < static_cast<unsigned int> (upperLimit);
}
template <typename Type1, typename Type2>
bool isPositiveAndNotGreaterThan (Type1 valueToTest, Type2 upperLimit) noexcept
{
jassert (Type1() <= static_cast<Type1> (upperLimit)); // makes no sense to call this if the upper limit is itself below zero..
return Type1() <= valueToTest && valueToTest <= static_cast<Type1> (upperLimit);
}
template <typename Type>
bool isPositiveAndNotGreaterThan (int valueToTest, Type upperLimit) noexcept
{
jassert (upperLimit >= 0); // makes no sense to call this if the upper limit is itself below zero..
return static_cast<unsigned int> (valueToTest) <= static_cast<unsigned int> (upperLimit);
}
template <typename Type>
bool isWithin (Type a, Type b, Type tolerance) noexcept
{
return std::abs (a - b) <= tolerance;
}
//==============================================================================
#if JUCE_MSVC
#pragma optimize ("t", off)
#ifndef __INTEL_COMPILER
#pragma float_control (precise, on, push)
#endif
#endif
template <typename FloatType>
int roundToInt (const FloatType value) noexcept
{
#ifdef __INTEL_COMPILER
#pragma float_control (precise, on, push)
#endif
union { int asInt[2]; double asDouble; } n;
n.asDouble = ((double) value) + 6755399441055744.0;
#if JUCE_BIG_ENDIAN
return n.asInt [1];
#else
return n.asInt [0];
#endif
}
inline int roundToInt (int value) noexcept
{
return value;
}
#if JUCE_MSVC
#ifndef __INTEL_COMPILER
#pragma float_control (pop)
#endif
#pragma optimize ("", on) // resets optimisations to the project defaults
#endif
inline int roundToIntAccurate (double value) noexcept
{
#ifdef __INTEL_COMPILER
#pragma float_control (pop)
#endif
return roundToInt (value + 1.5e-8);
}
//==============================================================================
template <typename FloatType>
unsigned int truncatePositiveToUnsignedInt (FloatType value) noexcept
{
jassert (value >= static_cast<FloatType> (0));
jassert (static_cast<FloatType> (value)
<= static_cast<FloatType> (std::numeric_limits<unsigned int>::max()));
return static_cast<unsigned int> (value);
}
//==============================================================================
template <typename IntegerType>
constexpr bool isPowerOfTwo (IntegerType value)
{
return (value & (value - 1)) == 0;
}
inline int nextPowerOfTwo (int n) noexcept
{
--n;
n |= (n >> 1);
n |= (n >> 2);
n |= (n >> 4);
n |= (n >> 8);
n |= (n >> 16);
return n + 1;
}
int findHighestSetBit (uint32 n) noexcept;
constexpr int countNumberOfBits (uint32 n) noexcept
{
n -= ((n >> 1) & 0x55555555);
n = (((n >> 2) & 0x33333333) + (n & 0x33333333));
n = (((n >> 4) + n) & 0x0f0f0f0f);
n += (n >> 8);
n += (n >> 16);
return (int) (n & 0x3f);
}
constexpr int countNumberOfBits (uint64 n) noexcept
{
return countNumberOfBits ((uint32) n) + countNumberOfBits ((uint32) (n >> 32));
}
template <typename IntegerType>
IntegerType negativeAwareModulo (IntegerType dividend, const IntegerType divisor) noexcept
{
jassert (divisor > 0);
dividend %= divisor;
return (dividend < 0) ? (dividend + divisor) : dividend;
}
template <typename NumericType>
inline constexpr NumericType square (NumericType n) noexcept
{
return n * n;
}
//==============================================================================
void writeLittleEndianBitsInBuffer (void* targetBuffer, uint32 startBit, uint32 numBits, uint32 value) noexcept;
uint32 readLittleEndianBitsInBuffer (const void* sourceBuffer, uint32 startBit, uint32 numBits) noexcept;
//==============================================================================
#if JUCE_INTEL || DOXYGEN
#define JUCE_UNDENORMALISE(x) { (x) += 0.1f; (x) -= 0.1f; }
#else
#define JUCE_UNDENORMALISE(x)
#endif
//==============================================================================
namespace TypeHelpers
{
template <typename Type> struct ParameterType { using type = const Type&; };
#ifndef DOXYGEN
template <typename Type> struct ParameterType <Type&> { using type = Type&; };
template <typename Type> struct ParameterType <Type*> { using type = Type*; };
template <> struct ParameterType <char> { using type = char; };
template <> struct ParameterType <unsigned char> { using type = unsigned char; };
template <> struct ParameterType <short> { using type = short; };
template <> struct ParameterType <unsigned short> { using type = unsigned short; };
template <> struct ParameterType <int> { using type = int; };
template <> struct ParameterType <unsigned int> { using type = unsigned int; };
template <> struct ParameterType <long> { using type = long; };
template <> struct ParameterType <unsigned long> { using type = unsigned long; };
template <> struct ParameterType <int64> { using type = int64; };
template <> struct ParameterType <uint64> { using type = uint64; };
template <> struct ParameterType <bool> { using type = bool; };
template <> struct ParameterType <float> { using type = float; };
template <> struct ParameterType <double> { using type = double; };
#endif
template <typename Type>
using SmallestFloatType = std::conditional_t<std::is_same_v<Type, double>, double, float>;
template <int bytes> struct UnsignedTypeWithSize {};
#ifndef DOXYGEN
template <> struct UnsignedTypeWithSize<1> { using type = uint8; };
template <> struct UnsignedTypeWithSize<2> { using type = uint16; };
template <> struct UnsignedTypeWithSize<4> { using type = uint32; };
template <> struct UnsignedTypeWithSize<8> { using type = uint64; };
#endif
}
//==============================================================================
#ifndef DOXYGEN
[[deprecated ("Use roundToInt instead.")]] inline int roundDoubleToInt (double value) noexcept { return roundToInt (value); }
[[deprecated ("Use roundToInt instead.")]] inline int roundFloatToInt (float value) noexcept { return roundToInt (value); }
[[deprecated ("Use std::abs() instead.")]] inline int64 abs64 (int64 n) noexcept { return std::abs (n); }
#endif
template <typename T>
constexpr auto toUnderlyingType (T t) -> std::enable_if_t<std::is_enum_v<T>, std::underlying_type_t<T>>
{
return static_cast<std::underlying_type_t<T>> (t);
}
} // namespace juce
juce::Tolerance
Definition juce_MathsFunctions.h:216
juce::Tolerance::withRelative
Tolerance withRelative(Type newRelative)
Definition juce_MathsFunctions.h:239
juce::Tolerance::withAbsolute
Tolerance withAbsolute(Type newAbsolute)
Definition juce_MathsFunctions.h:227
juce::TypeHelpers
Definition juce_MathsFunctions.h:740
juce::TypeHelpers::SmallestFloatType
std::conditional_t< std::is_same_v< Type, double >, double, float > SmallestFloatType
Definition juce_MathsFunctions.h:778
juce::TypeHelpers::ParameterType
Definition juce_MathsFunctions.h:752
juce::TypeHelpers::UnsignedTypeWithSize
Definition juce_MathsFunctions.h:785
juce::MathConstants
Definition juce_MathsFunctions.h:139
juce::MathConstants::halfPi
static constexpr FloatType halfPi
Definition juce_MathsFunctions.h:147
juce::MathConstants::twoPi
static constexpr FloatType twoPi
Definition juce_MathsFunctions.h:144
juce::MathConstants::sqrt2
static constexpr FloatType sqrt2
Definition juce_MathsFunctions.h:153
juce::MathConstants::pi
static constexpr FloatType pi
Definition juce_MathsFunctions.h:141
juce::MathConstants::euler
static constexpr FloatType euler
Definition juce_MathsFunctions.h:150