/* Provides common mathematical or analysis functions. * * These functions are all PRIVATE to ta-lib and should * never be called directly by the library users. */ #ifndef TA_UTILITY_H #define TA_UTILITY_H #ifndef TA_FUNC_H #include "ta_func.h" #endif #ifndef TA_GLOBAL_H #include "ta_global.h" #endif /* FMA runtime CPU dispatch (PR #96): mark fused indicators with target_clones so * a portable baseline build (e.g. a manylinux wheel) dispatches to a hardware-fma * clone at load — no -mfma, no SIGILL on pre-2013 CPUs. glibc-only: target_clones * needs GNU ifunc, which musl has in NO version (Alpine gcc hard-errors), so * musl/macOS/MSVC fall through to plain software fma(). Do NOT relax to __linux__ * (breaks the musllinux build; guarded by the nightly `musl-build` job). * -ffp-contract=off keeps the clones bit-exact with each other and the * Rust/Java backends. */ #if defined( __x86_64__ ) && defined( __GLIBC__ ) && defined( __GNUC__ ) && !defined( __clang__ ) #define TA_FMA_MULTIVERSION __attribute__((target_clones("default","fma"))) /* A fused body, inlined into each clone of its caller: a clone runs its * callees at the ISA they were compiled for. */ #define TA_FMA_STEP_INLINE __inline__ __attribute__((always_inline)) /* The open tier's frames around one fused body, picked per call by * TA_FMA_AVAILABLE. Its seams are private and a target_clones symbol is * exported whatever its visibility, so they dispatch by hand. */ #define TA_FMA_OPEN_CLONE __attribute__((target("fma"))) #define TA_FMA_OPEN_PLAIN __attribute__((cold,noinline)) #define TA_FMA_AVAILABLE __builtin_cpu_supports("fma") #else #define TA_FMA_MULTIVERSION #define TA_FMA_STEP_INLINE #define TA_FMA_OPEN_CLONE #define TA_FMA_OPEN_PLAIN #define TA_FMA_AVAILABLE 0 #endif /* The open tier's two frames, each with the body inlined at a constant stride, * kept out of line and out of interprocedural analysis so their code depends * only on that body: a fact the argument checks prove, inlined or propagated, * reshuffles the whole loop. */ #if defined( __GNUC__ ) #if defined( __has_attribute ) #if __has_attribute( noipa ) #define TA_OPEN_BODY __attribute__((noipa)) #endif #endif #ifndef TA_OPEN_BODY #define TA_OPEN_BODY __attribute__((noinline)) #endif #define TA_OPEN_INLINE __inline__ __attribute__((always_inline)) #elif defined( _MSC_VER ) #define TA_OPEN_BODY __declspec(noinline) #define TA_OPEN_INLINE __forceinline #else #define TA_OPEN_BODY #define TA_OPEN_INLINE #endif /* Provides an equivalent to standard "math.h" functions. */ #define std_floor floor #define std_ceil ceil #define std_fabs fabs #define std_atan atan #define std_cos cos #define std_sin sin #define std_sqrt sqrt #define std_tanh tanh #define std_tan tan #define std_sinh sinh #define std_log10 log10 #define std_log log #define std_exp exp #define std_cosh cosh #define std_asin asin #define std_acos acos /* TA_UNROLL(n) - ask the compiler to unroll the loop on the next line n times. * * Purely advisory: it never changes what is computed, and a compiler that * ignores it emits exactly the code it emits today. Used on the expired-extreme * rescan of the rolling min/max functions, whose loop body is two instructions, * so loop control is a large share of its cost. * * Why a hint in the source and not a build flag: neither GCC nor Clang unrolls * these loops on its own at any -O level (the trip count is only known at run * time, and Clang's runtime unroller is off by default -- -funroll-loops does * not turn it on), and our build flags never reach the downstream projects that * compile the src/ta_func sources straight out of the release tarball. * * Enabled only where it was measured to pay (rolling min/max, random-walk * inputs, medians over a code-layout sweep): * * GCC x86-64 -6% to -8% enabled * Apple clang arm64 -27% (L1/L2-resident), -1% (larger working set) enabled * Clang x86-64 -1% to +5% NOT enabled -- no gain, often a loss * * Clang on x86-64 already emits a different (partly branchless) shape for these * loops, and unrolling it is at best neutral. Re-measure before widening this; * an unroll factor is a per-target tuning constant, not a portable truth. */ #if defined(__clang__) && defined(__aarch64__) #define TA_UNROLL_STR1(x) #x #define TA_UNROLL_STR(x) TA_UNROLL_STR1(x) #define TA_UNROLL(n) _Pragma(TA_UNROLL_STR(clang loop unroll_count(n))) #elif defined(__GNUC__) && !defined(__clang__) && (__GNUC__ >= 8) #define TA_UNROLL_STR1(x) #x #define TA_UNROLL_STR(x) TA_UNROLL_STR1(x) #define TA_UNROLL(n) _Pragma(TA_UNROLL_STR(GCC unroll n)) #else #define TA_UNROLL(n) #endif /* TA_UNLIKELY(c) - c, asserted rarely true: the guarded block is laid out of line. */ #if defined(__GNUC__) || defined(__clang__) #define TA_UNLIKELY(c) __builtin_expect(!!(c), 0) #else #define TA_UNLIKELY(c) (c) #endif /* TA_VMATH_MAP - the loop of a function that is one math-library call per element: * * for( i = first, outIdx = 0; i <= last; i += 1, outIdx += 1 ) { OUT = fn(IN); } * * OUT is an lvalue written in terms of outIdx, IN an expression in terms of i, and * first <= last. * * On Apple Silicon macOS the loop hands the values to vForce, the array routines * of the Accelerate framework. A result may then differ from fn() in the last * bits, in the sign of a zero and in the bits of a NaN; the one-value tiers * (Update, Peek) keep fn(). * * Keep every element one function of one value: every length goes through the * same routine, never through fn() or another kernel for a short range, or a * result would depend on where its range starts. * * vForce computes in blocks of eight, and one value in a block can change * another's result (measured: a signalling NaN does, four places away). It is * therefore given the scratch only, and the scratch slots after the last value, * up to a whole block, hold an ordinary value, never what the stack held. * * vForce writes its whole count from the slot it is given, so it runs only where * OUT's slots are consecutive, or one slot for every outIdx (a sink): any other * layout keeps the plain loop. Into a sink only the last element is computed: Open * runs this loop with a stride of 0 and reads the sink afterwards. Without that * Open is a pass over the whole history that overruns the sink. * * The routines are bound at run time, never linked: linking the framework adds * its load to the start of every program that links this library, whatever it * calls. TA_Initialize binds them all; a loop still binds its own at first use, * so a caller that skipped TA_Initialize gets the same values. A routine that * cannot be bound leaves the plain loop, whose values no tolerance tells from the * kernel's: TA_GetRuntimeInfo reports it. */ #define TA_VMATH_PLAIN( fn, i, first, last, outIdx, OUT, IN ) \ for( i = first, outIdx = 0; i <= last; i += 1, outIdx += 1 ) \ { \ OUT = fn(IN); \ } #if TA_VMATH_KERNEL #define TA_VMATH_SCRATCH 128 #define TA_VMATH_MAP( fn, i, first, last, outIdx, OUT, IN ) \ { \ static TA_VMathRoutine ta_vmath_bound; \ const double *ta_vmath_slot0, *ta_vmath_slot1; \ TA_VMathRoutine ta_vmath_vv = __atomic_load_n( &ta_vmath_bound, __ATOMIC_RELAXED ); \ if( !ta_vmath_vv ) \ { \ ta_vmath_vv = TA_VMathBind( "vv" #fn ); \ __atomic_store_n( &ta_vmath_bound, ta_vmath_vv, __ATOMIC_RELAXED ); \ } \ outIdx = 1; \ ta_vmath_slot1 = &(OUT); \ outIdx = 0; \ ta_vmath_slot0 = &(OUT); \ if( ta_vmath_vv == TA_VMathUnbound || ( ta_vmath_slot1 != ta_vmath_slot0 && ta_vmath_slot1 != ta_vmath_slot0 + 1 ) ) \ { \ TA_VMATH_PLAIN( fn, i, first, last, outIdx, OUT, IN ) \ } \ else \ { \ double ta_vmath_in[TA_VMATH_SCRATCH]; \ i = first; \ if( ta_vmath_slot1 == ta_vmath_slot0 ) \ { \ i = last; \ outIdx = last - first; \ } \ while( i <= last ) \ { \ int ta_vmath_n = last - i + 1, ta_vmath_k; \ if( ta_vmath_n > TA_VMATH_SCRATCH ) ta_vmath_n = TA_VMATH_SCRATCH; \ for( ta_vmath_k = 0; ta_vmath_k < ta_vmath_n; ta_vmath_k += 1, i += 1 ) \ ta_vmath_in[ta_vmath_k] = IN; \ for( ; ta_vmath_k & 7; ta_vmath_k += 1 ) \ ta_vmath_in[ta_vmath_k] = 0.5; \ ta_vmath_vv( &(OUT), ta_vmath_in, &ta_vmath_n ); \ outIdx += ta_vmath_n; \ } \ } \ } #else #define TA_VMATH_MAP( fn, i, first, last, outIdx, OUT, IN ) \ TA_VMATH_PLAIN( fn, i, first, last, outIdx, OUT, IN ) #endif /* Rounding macro for doubles. Works only with positive numbers. */ #define round_pos(x) (std_floor((x)+0.5)) /* Rounding macro for doubles. Works only with negative numbers. */ #define round_neg(x) (std_ceil((x)-0.5)) /* Rounding with a precision of 2 digit after the dot */ #define round_pos_2(x) ((std_floor((x*100.0)+0.5))/100.0) #define round_neg_2(x) ((std_ceil((x*100.0)-0.5))/100.0) /* In the context of TA-Lib, floating point are often * compared within an acceptable error range. * * As an example,a TA oscillator ranging from 0 to 100 can * fairly be considered equal if their difference is less * than 0.000001. * * Ranging around zero also allows to work around limit * cases where floating point minimal step (EPSILON) causes * unexpected cummulative effect (ending with "negative zero" * being one example). * * FLT_EPSILON == 1.192092896e-07 for float type on intel with msvc. * DBL_EPSILON == 2.2204460492503131e-016 for the double type. * * Warning: These macro are not intended as "general purpose" floating * point comparison. TA_REAL_EQ is not even transitive. The "ep" parameter * must be carefully choosen to work in the domain of the tested values. * Do a search on Google for a more generalize algo. * * Bounds are inclusive: below half a ULP of "v", v-ep and v+ep round back to * v, and strict bounds would then reject bit-identical operands. */ #define TA_EPSILON (0.00000000000001) #define TA_REAL_EQ(x,v,ep) (((v-ep)<=x)&&(x<=(v+ep))) #define TA_IS_ZERO(v) (((-TA_EPSILON) (b)) ? (a) : (b)) #endif /* A running `a = a < b ? a : b` or `a = a > b ? a : b`. On ARM64 a double pair * is the one instruction the target has for it, which a compiler may not pick * for the ternary: the two differ on a NaN operand and on the sign of a zero * result. Every other target and type keeps the ternary. */ #if defined(__aarch64__) && (defined(__GNUC__) || defined(__clang__)) #define TA_MIN_LT(a, b) __builtin_choose_expr( \ __builtin_types_compatible_p(__typeof__((a) + (b)), double), \ __builtin_fmin((a), (b)), (((a) < (b)) ? (a) : (b))) #define TA_MAX_GT(a, b) __builtin_choose_expr( \ __builtin_types_compatible_p(__typeof__((a) + (b)), double), \ __builtin_fmax((a), (b)), (((a) > (b)) ? (a) : (b))) #else #define TA_MIN_LT(a, b) (((a) < (b)) ? (a) : (b)) #define TA_MAX_GT(a, b) (((a) > (b)) ? (a) : (b)) #endif /* `a > b ? x : 0.0` (KEEP) and `a > b ? 0.0 : x` (ZERO), and the same with `<`. * With gcc on x86 a compare mask in asm, which gcc cannot turn back into the * branch it often makes of the ternary. Same bits as the ternary: the mask is * false on a NaN and the zero is +0.0. `%v` and `%d0` give the VEX form in a * function compiled for AVX, a target_clones or target("fma") one included, as * long as the mask is inlined into it: an SSE instruction after 256-bit code * stalls until the upper halves are saved. clang rejects both, and keeps the * ternary. */ #if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) && \ !defined(__NVCOMPILER) && defined(__SSE2_MATH__) && (defined(__x86_64__) || defined(__i386__)) #define TA_ZERO_PICK_ASM 1 static __inline__ __attribute__((always_inline)) double ta_zero_pick( double lo, double hi, double x, int keep ) { double m = lo; __asm__( "%vcmpltsd {%1, %d0|%d0, %1}" : "+x"(m) : "x"(hi) ); if( keep ) __asm__( "%vandpd {%1, %d0|%d0, %1}" : "+x"(m) : "x"(x) ); else __asm__( "%vandnpd {%1, %d0|%d0, %1}" : "+x"(m) : "x"(x) ); return m; } #define TA_KEEP_IF_GT(a, b, x) ta_zero_pick( (b), (a), (x), 1 ) #define TA_KEEP_IF_LT(a, b, x) ta_zero_pick( (a), (b), (x), 1 ) #define TA_ZERO_IF_GT(a, b, x) ta_zero_pick( (b), (a), (x), 0 ) #define TA_ZERO_IF_LT(a, b, x) ta_zero_pick( (a), (b), (x), 0 ) #else #define TA_KEEP_IF_GT(a, b, x) (((a) > (b)) ? (x) : 0.0) #define TA_KEEP_IF_LT(a, b, x) (((a) < (b)) ? (x) : 0.0) #define TA_ZERO_IF_GT(a, b, x) (((a) > (b)) ? 0.0 : (x)) #define TA_ZERO_IF_LT(a, b, x) (((a) < (b)) ? 0.0 : (x)) #endif /* `c >= o ? c : o` and `c >= o ? o : c` as maxsd/minsd, which a candle's colour * test, a coin flip on real prices, cannot be compiled back into a branch from. * Same bits but on a NaN operand (the operand order is load-bearing). */ #if defined( __SSE2__ ) || defined( _M_X64 ) || ( defined( _M_IX86_FP ) && _M_IX86_FP >= 2 ) #include #define TA_COLOR_MAX(c, o) _mm_cvtsd_f64( _mm_max_sd( _mm_set_sd( (double)(o) ), _mm_set_sd( (double)(c) ) ) ) #define TA_COLOR_MIN(c, o) _mm_cvtsd_f64( _mm_min_sd( _mm_set_sd( (double)(c) ), _mm_set_sd( (double)(o) ) ) ) #else #define TA_COLOR_MAX(c, o) ( (c) >= (o) ? (double)(c) : (double)(o) ) #define TA_COLOR_MIN(c, o) ( (c) >= (o) ? (double)(o) : (double)(c) ) #endif /* Candlestick macros (used by candlestick functions, where the parameters are always called inClose, inOpen, etc. * Don't use i++ or func(i) with these macros ! */ /* The subtraction operands are widened to double so these compute in double even * when the arrays are const float[] (the TA_S_ bodies) — a no-op for the double * variants, PR #33's "double throughout" for the single-precision ones (#138). * The >= tests need no cast: float->double is order-preserving. */ #define TA_REALBODY(IDX) ( std_fabs( (double)inClose[IDX] - (double)inOpen[IDX] ) ) #define TA_UPPERSHADOW(IDX) ( (double)inHigh[IDX] - TA_COLOR_MAX( inClose[IDX], inOpen[IDX] ) ) #define TA_LOWERSHADOW(IDX) ( TA_COLOR_MIN( inClose[IDX], inOpen[IDX] ) - (double)inLow[IDX] ) #define TA_HIGHLOWRANGE(IDX) ( (double)inHigh[IDX] - (double)inLow[IDX] ) #define TA_CANDLECOLOR(IDX) ( inClose[IDX] >= inOpen[IDX] ? 1 : -1 ) #define TA_CANDLERANGETYPE(SET) (TA_Globals->candleSettings[TA_##SET].rangeType) #define TA_CANDLEAVGPERIOD(SET) (TA_Globals->candleSettings[TA_##SET].avgPeriod) #define TA_CANDLEFACTOR(SET) (TA_Globals->candleSettings[TA_##SET].factor) /* The _OF forms take the setting's values, so a generated batch loop can read * them into locals once: the int output store may alias TA_Globals, which keeps * the compiler from hoisting the reads itself. */ #define TA_CANDLERANGE_OF(RANGETYPE,IDX) \ ( (RANGETYPE) == TA_RangeType_RealBody ? TA_REALBODY(IDX) : \ ( (RANGETYPE) == TA_RangeType_HighLow ? TA_HIGHLOWRANGE(IDX) : \ ( (RANGETYPE) == TA_RangeType_Shadows ? TA_UPPERSHADOW(IDX) + TA_LOWERSHADOW(IDX) : \ 0 ) ) ) #define TA_CANDLEAVERAGE_OF(RANGETYPE,AVGPERIOD,FACTOR,SUM,IDX) \ ( (FACTOR) \ * ( (AVGPERIOD) != 0.0? SUM / (AVGPERIOD) : TA_CANDLERANGE_OF(RANGETYPE,IDX) ) \ / ( (RANGETYPE) == TA_RangeType_Shadows ? 2.0 : 1.0 ) \ ) #define TA_CANDLERANGE(SET,IDX) TA_CANDLERANGE_OF(TA_CANDLERANGETYPE(SET),IDX) #define TA_CANDLEAVERAGE(SET,SUM,IDX) \ TA_CANDLEAVERAGE_OF(TA_CANDLERANGETYPE(SET),TA_CANDLEAVGPERIOD(SET),TA_CANDLEFACTOR(SET),SUM,IDX) #define TA_REALBODYGAPUP(IDX2,IDX1) ( min(inOpen[IDX2],inClose[IDX2]) > max(inOpen[IDX1],inClose[IDX1]) ) #define TA_REALBODYGAPDOWN(IDX2,IDX1) ( max(inOpen[IDX2],inClose[IDX2]) < min(inOpen[IDX1],inClose[IDX1]) ) #define TA_CANDLEGAPUP(IDX2,IDX1) ( inLow[IDX2] > inHigh[IDX1] ) #define TA_CANDLEGAPDOWN(IDX2,IDX1) ( inHigh[IDX2] < inLow[IDX1] ) /* Scalar-argument candle macros for the generated streaming API. * These MUST mirror TA_CANDLERANGE/TA_CANDLEAVERAGE above exactly (same * operations in the same order) with the bar's OHLC supplied as scalar * expressions instead of array reads — the stream transition has no input * arrays. Any change to the batch macros must be mirrored here, or the * streams lose bit-exactness with batch on the affected range type. */ #define TA_STREAM_REALBODY(O,H,L,C) ( std_fabs( (double)(C) - (double)(O) ) ) #define TA_STREAM_UPPERSHADOW(O,H,L,C) ( (double)(H) - TA_COLOR_MAX( (C), (O) ) ) #define TA_STREAM_LOWERSHADOW(O,H,L,C) ( TA_COLOR_MIN( (C), (O) ) - (double)(L) ) #define TA_STREAM_HIGHLOWRANGE(O,H,L,C) ( (double)(H) - (double)(L) ) #define TA_STREAM_CANDLERANGE(SET,O,H,L,C) \ ( TA_CANDLERANGETYPE(SET) == TA_RangeType_RealBody ? TA_STREAM_REALBODY(O,H,L,C) : \ ( TA_CANDLERANGETYPE(SET) == TA_RangeType_HighLow ? TA_STREAM_HIGHLOWRANGE(O,H,L,C) : \ ( TA_CANDLERANGETYPE(SET) == TA_RangeType_Shadows ? TA_STREAM_UPPERSHADOW(O,H,L,C) + TA_STREAM_LOWERSHADOW(O,H,L,C) : \ 0 ) ) ) #define TA_STREAM_CANDLEAVERAGE(SET,SUM,O,H,L,C) \ ( TA_CANDLEFACTOR(SET) \ * ( TA_CANDLEAVGPERIOD(SET) != 0.0? (SUM) / TA_CANDLEAVGPERIOD(SET) : TA_STREAM_CANDLERANGE(SET,O,H,L,C) ) \ / ( TA_CANDLERANGETYPE(SET) == TA_RangeType_Shadows ? 2.0 : 1.0 ) \ ) #endif