269 lines
7.9 KiB
C
269 lines
7.9 KiB
C
//
|
|
// m3_math_utils.h
|
|
//
|
|
// Created by Volodymyr Shymanksyy on 8/10/19.
|
|
// Copyright © 2019 Volodymyr Shymanskyy. All rights reserved.
|
|
//
|
|
|
|
#ifndef m3_math_utils_h
|
|
#define m3_math_utils_h
|
|
|
|
#include "m3_core.h"
|
|
|
|
#include <limits.h>
|
|
|
|
#if defined(M3_COMPILER_MSVC)
|
|
|
|
#include <intrin.h>
|
|
|
|
#define __builtin_popcount __popcnt
|
|
|
|
static inline
|
|
int __builtin_ctz(uint32_t x) {
|
|
unsigned long ret;
|
|
_BitScanForward(&ret, x);
|
|
return (int)ret;
|
|
}
|
|
|
|
static inline
|
|
int __builtin_clz(uint32_t x) {
|
|
unsigned long ret;
|
|
_BitScanReverse(&ret, x);
|
|
return (int)(31 ^ ret);
|
|
}
|
|
|
|
|
|
|
|
#ifdef _WIN64
|
|
|
|
#define __builtin_popcountll __popcnt64
|
|
|
|
static inline
|
|
int __builtin_ctzll(uint64_t value) {
|
|
unsigned long ret;
|
|
_BitScanForward64(&ret, value);
|
|
return (int)ret;
|
|
}
|
|
|
|
static inline
|
|
int __builtin_clzll(uint64_t value) {
|
|
unsigned long ret;
|
|
_BitScanReverse64(&ret, value);
|
|
return (int)(63 ^ ret);
|
|
}
|
|
|
|
#else // _WIN64
|
|
|
|
#define __builtin_popcountll(x) (__popcnt((x) & 0xFFFFFFFF) + __popcnt((x) >> 32))
|
|
|
|
static inline
|
|
int __builtin_ctzll(uint64_t value) {
|
|
//if (value == 0) return 64; // Note: ctz(0) result is undefined anyway
|
|
uint32_t msh = (uint32_t)(value >> 32);
|
|
uint32_t lsh = (uint32_t)(value & 0xFFFFFFFF);
|
|
if (lsh != 0) return __builtin_ctz(lsh);
|
|
return 32 + __builtin_ctz(msh);
|
|
}
|
|
|
|
static inline
|
|
int __builtin_clzll(uint64_t value) {
|
|
//if (value == 0) return 64; // Note: clz(0) result is undefined anyway
|
|
uint32_t msh = (uint32_t)(value >> 32);
|
|
uint32_t lsh = (uint32_t)(value & 0xFFFFFFFF);
|
|
if (msh != 0) return __builtin_clz(msh);
|
|
return 32 + __builtin_clz(lsh);
|
|
}
|
|
|
|
#endif // _WIN64
|
|
|
|
#endif // defined(M3_COMPILER_MSVC)
|
|
|
|
|
|
// TODO: not sure why, signbit is actually defined in math.h
|
|
#if (defined(ESP8266) || defined(ESP32)) && !defined(signbit)
|
|
#define signbit(__x) \
|
|
((sizeof(__x) == sizeof(float)) ? __signbitf(__x) : __signbitd(__x))
|
|
#endif
|
|
|
|
#if defined(__AVR__)
|
|
|
|
static inline
|
|
float rintf( float arg ) {
|
|
union { float f; uint32_t i; } u;
|
|
u.f = arg;
|
|
uint32_t ux = u.i & 0x7FFFFFFF;
|
|
if (M3_UNLIKELY(ux == 0 || ux > 0x5A000000)) {
|
|
return arg;
|
|
}
|
|
return (float)lrint(arg);
|
|
}
|
|
|
|
static inline
|
|
double rint( double arg ) {
|
|
union { double f; uint32_t i[2]; } u;
|
|
u.f = arg;
|
|
uint32_t ux = u.i[1] & 0x7FFFFFFF;
|
|
if (M3_UNLIKELY((ux == 0 && u.i[0] == 0) || ux > 0x433FFFFF)) {
|
|
return arg;
|
|
}
|
|
return (double)lrint(arg);
|
|
}
|
|
|
|
//TODO
|
|
static inline
|
|
uint64_t strtoull(const char* str, char** endptr, int base) {
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
/*
|
|
* Rotr, Rotl
|
|
*/
|
|
|
|
static inline
|
|
u32 rotl32(u32 n, unsigned c) {
|
|
const unsigned mask = CHAR_BIT * sizeof(n) - 1;
|
|
c &= mask & 31;
|
|
return (n << c) | (n >> ((-c) & mask));
|
|
}
|
|
|
|
static inline
|
|
u32 rotr32(u32 n, unsigned c) {
|
|
const unsigned mask = CHAR_BIT * sizeof(n) - 1;
|
|
c &= mask & 31;
|
|
return (n >> c) | (n << ((-c) & mask));
|
|
}
|
|
|
|
static inline
|
|
u64 rotl64(u64 n, unsigned c) {
|
|
const unsigned mask = CHAR_BIT * sizeof(n) - 1;
|
|
c &= mask & 63;
|
|
return (n << c) | (n >> ((-c) & mask));
|
|
}
|
|
|
|
static inline
|
|
u64 rotr64(u64 n, unsigned c) {
|
|
const unsigned mask = CHAR_BIT * sizeof(n) - 1;
|
|
c &= mask & 63;
|
|
return (n >> c) | (n << ((-c) & mask));
|
|
}
|
|
|
|
/*
|
|
* Integer Div, Rem
|
|
*/
|
|
|
|
#define OP_DIV_U(RES, A, B) \
|
|
if (M3_UNLIKELY(B == 0)) newTrap (m3Err_trapDivisionByZero); \
|
|
RES = A / B;
|
|
|
|
#define OP_REM_U(RES, A, B) \
|
|
if (M3_UNLIKELY(B == 0)) newTrap (m3Err_trapDivisionByZero); \
|
|
RES = A % B;
|
|
|
|
// 2's complement detection
|
|
#if (INT_MIN != -INT_MAX)
|
|
|
|
#define OP_DIV_S(RES, A, B, TYPE_MIN) \
|
|
if (M3_UNLIKELY(B == 0)) newTrap (m3Err_trapDivisionByZero); \
|
|
if (M3_UNLIKELY(B == -1 and A == TYPE_MIN)) { \
|
|
newTrap (m3Err_trapIntegerOverflow); \
|
|
} \
|
|
RES = A / B;
|
|
|
|
#define OP_REM_S(RES, A, B, TYPE_MIN) \
|
|
if (M3_UNLIKELY(B == 0)) newTrap (m3Err_trapDivisionByZero); \
|
|
if (M3_UNLIKELY(B == -1 and A == TYPE_MIN)) RES = 0; \
|
|
else RES = A % B;
|
|
|
|
#else
|
|
|
|
#define OP_DIV_S(RES, A, B, TYPE_MIN) OP_DIV_U(RES, A, B)
|
|
#define OP_REM_S(RES, A, B, TYPE_MIN) OP_REM_U(RES, A, B)
|
|
|
|
#endif
|
|
|
|
/*
|
|
* Trunc
|
|
*/
|
|
|
|
#define OP_TRUNC(RES, A, TYPE, RMIN, RMAX) \
|
|
if (M3_UNLIKELY(isnan(A))) { \
|
|
newTrap (m3Err_trapIntegerConversion); \
|
|
} \
|
|
if (M3_UNLIKELY(A <= RMIN or A >= RMAX)) { \
|
|
newTrap (m3Err_trapIntegerOverflow); \
|
|
} \
|
|
RES = (TYPE)A;
|
|
|
|
|
|
#define OP_I32_TRUNC_F32(RES, A) OP_TRUNC(RES, A, i32, -2147483904.0f, 2147483648.0f)
|
|
#define OP_U32_TRUNC_F32(RES, A) OP_TRUNC(RES, A, u32, -1.0f, 4294967296.0f)
|
|
#define OP_I32_TRUNC_F64(RES, A) OP_TRUNC(RES, A, i32, -2147483649.0 , 2147483648.0 )
|
|
#define OP_U32_TRUNC_F64(RES, A) OP_TRUNC(RES, A, u32, -1.0 , 4294967296.0 )
|
|
|
|
#define OP_I64_TRUNC_F32(RES, A) OP_TRUNC(RES, A, i64, -9223373136366403584.0f, 9223372036854775808.0f)
|
|
#define OP_U64_TRUNC_F32(RES, A) OP_TRUNC(RES, A, u64, -1.0f, 18446744073709551616.0f)
|
|
#define OP_I64_TRUNC_F64(RES, A) OP_TRUNC(RES, A, i64, -9223372036854777856.0 , 9223372036854775808.0 )
|
|
#define OP_U64_TRUNC_F64(RES, A) OP_TRUNC(RES, A, u64, -1.0 , 18446744073709551616.0 )
|
|
|
|
#define OP_TRUNC_SAT(RES, A, TYPE, RMIN, RMAX, IMIN, IMAX) \
|
|
if (M3_UNLIKELY(isnan(A))) { \
|
|
RES = 0; \
|
|
} else if (M3_UNLIKELY(A <= RMIN)) { \
|
|
RES = IMIN; \
|
|
} else if (M3_UNLIKELY(A >= RMAX)) { \
|
|
RES = IMAX; \
|
|
} else { \
|
|
RES = (TYPE)A; \
|
|
}
|
|
|
|
#define OP_I32_TRUNC_SAT_F32(RES, A) OP_TRUNC_SAT(RES, A, i32, -2147483904.0f, 2147483648.0f, INT32_MIN, INT32_MAX)
|
|
#define OP_U32_TRUNC_SAT_F32(RES, A) OP_TRUNC_SAT(RES, A, u32, -1.0f, 4294967296.0f, 0UL, UINT32_MAX)
|
|
#define OP_I32_TRUNC_SAT_F64(RES, A) OP_TRUNC_SAT(RES, A, i32, -2147483649.0 , 2147483648.0, INT32_MIN, INT32_MAX)
|
|
#define OP_U32_TRUNC_SAT_F64(RES, A) OP_TRUNC_SAT(RES, A, u32, -1.0 , 4294967296.0, 0UL, UINT32_MAX)
|
|
|
|
#define OP_I64_TRUNC_SAT_F32(RES, A) OP_TRUNC_SAT(RES, A, i64, -9223373136366403584.0f, 9223372036854775808.0f, INT64_MIN, INT64_MAX)
|
|
#define OP_U64_TRUNC_SAT_F32(RES, A) OP_TRUNC_SAT(RES, A, u64, -1.0f, 18446744073709551616.0f, 0ULL, UINT64_MAX)
|
|
#define OP_I64_TRUNC_SAT_F64(RES, A) OP_TRUNC_SAT(RES, A, i64, -9223372036854777856.0 , 9223372036854775808.0, INT64_MIN, INT64_MAX)
|
|
#define OP_U64_TRUNC_SAT_F64(RES, A) OP_TRUNC_SAT(RES, A, u64, -1.0 , 18446744073709551616.0, 0ULL, UINT64_MAX)
|
|
|
|
/*
|
|
* Min, Max
|
|
*/
|
|
|
|
#if d_m3HasFloat
|
|
|
|
#include <math.h>
|
|
|
|
static inline
|
|
f32 min_f32(f32 a, f32 b) {
|
|
if (M3_UNLIKELY(isnan(a) or isnan(b))) return NAN;
|
|
if (M3_UNLIKELY(a == 0 and a == b)) return signbit(a) ? a : b;
|
|
return a > b ? b : a;
|
|
}
|
|
|
|
static inline
|
|
f32 max_f32(f32 a, f32 b) {
|
|
if (M3_UNLIKELY(isnan(a) or isnan(b))) return NAN;
|
|
if (M3_UNLIKELY(a == 0 and a == b)) return signbit(a) ? b : a;
|
|
return a > b ? a : b;
|
|
}
|
|
|
|
static inline
|
|
f64 min_f64(f64 a, f64 b) {
|
|
if (M3_UNLIKELY(isnan(a) or isnan(b))) return NAN;
|
|
if (M3_UNLIKELY(a == 0 and a == b)) return signbit(a) ? a : b;
|
|
return a > b ? b : a;
|
|
}
|
|
|
|
static inline
|
|
f64 max_f64(f64 a, f64 b) {
|
|
if (M3_UNLIKELY(isnan(a) or isnan(b))) return NAN;
|
|
if (M3_UNLIKELY(a == 0 and a == b)) return signbit(a) ? b : a;
|
|
return a > b ? a : b;
|
|
}
|
|
#endif
|
|
|
|
#endif // m3_math_utils_h
|