slang-math.h
#ifndef SLANG_CORE_MATH_H
#define SLANG_CORE_MATH_H
#include <cmath>
namespace Slang
{
// Some handy constants
// The largest positive (or negative) number
# define SLANG_HALF_MAX 65504.0f
// Smallest (denormalized) value. 1 / 2^24
# define SLANG_HALF_SUB_NORMAL_MIN (1.0f / 16777216.0f)
class Math
{
public:
// Use to fix type punning issues with strict aliasing
union FloatIntUnion
{
float fvalue;
int ivalue;
SLANG_FORCE_INLINE static FloatIntUnion makeFromInt(int i) { FloatIntUnion cast; cast.ivalue = i; return cast; }
SLANG_FORCE_INLINE static FloatIntUnion makeFromFloat(float f) { FloatIntUnion cast; cast.fvalue = f; return cast; }
};
union DoubleInt64Union
{
double dvalue;
int64_t ivalue;
SLANG_FORCE_INLINE static DoubleInt64Union makeFromInt64(int64_t i) { DoubleInt64Union cast; cast.ivalue = i; return cast; }
SLANG_FORCE_INLINE static DoubleInt64Union makeFromDouble(double d) { DoubleInt64Union cast; cast.dvalue = d; return cast; }
};
static const float Pi;
template <typename T>
static T Abs(T a)
{
return (a < 0) ? -a : a;
}
template<typename T>
static T Min(const T& v1, const T&v2)
{
return v1<v2?v1:v2;
}
template<typename T>
static T Max(const T& v1, const T&v2)
{
return v1>v2?v1:v2;
}
template<typename T>
static T Min(const T& v1, const T&v2, const T&v3)
{
return Min(v1, Min(v2, v3));
}
template<typename T>
static T Max(const T& v1, const T&v2, const T&v3)
{
return Max(v1, Max(v2, v3));
}
template<typename T>
static T Clamp(const T& val, const T& vmin, const T&vmax)
{
if (val < vmin) return vmin;
else if (val > vmax) return vmax;
else return val;
}
static inline int FastFloor(float x)
{
int i = (int)x;
return i - (i > x);
}
static inline int FastFloor(double x)
{
int i = (int)x;
return i - (i > x);
}
static inline int IsNaN(float x)
{
return std::isnan(x);
}
static inline int IsInf(float x)
{
return std::isinf(x);
}
static inline unsigned int Ones32(unsigned int x)
{
/* 32-bit recursive reduction using SWAR...
but first step is mapping 2-bit values
into sum of 2 1-bit values in sneaky way
*/
x -= ((x >> 1) & 0x55555555);
x = (((x >> 2) & 0x33333333) + (x & 0x33333333));
x = (((x >> 4) + x) & 0x0f0f0f0f);
x += (x >> 8);
x += (x >> 16);
return(x & 0x0000003f);
}
static inline unsigned int Log2Floor(unsigned int x)
{
x |= (x >> 1);
x |= (x >> 2);
x |= (x >> 4);
x |= (x >> 8);
x |= (x >> 16);
return(Ones32(x >> 1));
}
static inline unsigned int Log2Ceil(unsigned int x)
{
int y = (x & (x - 1));
y |= -y;
y >>= (32 - 1);
x |= (x >> 1);
x |= (x >> 2);
x |= (x >> 4);
x |= (x >> 8);
x |= (x >> 16);
return(Ones32(x >> 1) - y);
}
/*
static inline int Log2(float x)
{
unsigned int ix = (unsigned int&)x;
unsigned int exp = (ix >> 23) & 0xFF;
int log2 = (unsigned int)(exp) - 127;
return log2;
}
*/
static bool AreNearlyEqual(double a, double b, double epsilon)
{
// If they are equal then we are done
if (a == b)
{
return true;
}
const double absA = Abs(a);
const double absB = Abs(b);
const double diff = Abs(a - b);
// https://en.wikipedia.org/wiki/Double_precision_floating-point_format
const double minNormal = 2.2250738585072014e-308;
// Either a or b are very close to being zero, so doing relative comparison isn't really appropriate
if (a == 0.0 || b == 0.0 || (absA + absB < minNormal))
{
return diff < (epsilon * minNormal);
}
else
{
// Calculate a relative relative error
return diff < epsilon * (absA + absB);
}
}
template <typename T>
static T getLowestBit(T val)
{
return val & (-val);
}
};
inline int FloatAsInt(float val)
{
return Math::FloatIntUnion::makeFromFloat(val).ivalue;
}
inline float IntAsFloat(int val)
{
return Math::FloatIntUnion::makeFromInt(val).fvalue;
}
SLANG_FORCE_INLINE int64_t DoubleAsInt64(double val)
{
return Math::DoubleInt64Union::makeFromDouble(val).ivalue;
}
SLANG_FORCE_INLINE double Int64AsDouble(int64_t value)
{
return Math::DoubleInt64Union::makeFromInt64(value).dvalue;
}
inline unsigned short FloatToHalf(float val)
{
const auto x = FloatAsInt(val);
unsigned short bits = (x >> 16) & 0x8000;
unsigned short m = (x >> 12) & 0x07ff;
unsigned int e = (x >> 23) & 0xff;
if (e < 103)
return bits;
if (e > 142)
{
bits |= 0x7c00u;
bits |= e == 255 && (x & 0x007fffffu);
return bits;
}
if (e < 113)
{
m |= 0x0800u;
bits |= (m >> (114 - e)) + ((m >> (113 - e)) & 1);
return bits;
}
bits |= ((e - 112) << 10) | (m >> 1);
bits += m & 1;
return bits;
}
inline float HalfToFloat(unsigned short input)
{
static const auto magic = Math::FloatIntUnion::makeFromInt((127 + (127 - 15)) << 23);
static const auto was_infnan = Math::FloatIntUnion::makeFromInt((127 + 16) << 23);
Math::FloatIntUnion o;
o.ivalue = (input & 0x7fff) << 13; // exponent/mantissa bits
o.fvalue *= magic.fvalue; // exponent adjust
if (o.fvalue >= was_infnan.fvalue) // make sure Inf/NaN survive
o.ivalue |= 255 << 23;
o.ivalue |= (input & 0x8000) << 16; // sign bit
return o.fvalue;
}
class Random
{
private:
unsigned int seed;
public:
Random(int seed)
{
this->seed = seed;
}
int Next() // random between 0 and RandMax (currently 0x7fff)
{
return ((seed = ((seed << 12) + 150889L) % 714025) & 0x7fff);
}
int Next(int min, int max) // inclusive min, exclusive max
{
unsigned int a = ((seed = ((seed << 12) + 150889L) % 714025) & 0xFFFF);
unsigned int b = ((seed = ((seed << 12) + 150889L) % 714025) & 0xFFFF);
unsigned int r = (a << 16) + b;
return min + r % (max - min);
}
float NextFloat()
{
return ((Next() << 15) + Next()) / ((float)(1 << 30));
}
float NextFloat(float valMin, float valMax)
{
return valMin + (valMax - valMin) * NextFloat();
}
static int RandMax()
{
return 0x7fff;
}
};
}
#endif