mirror of
https://github.com/odin-lang/Odin.git
synced 2026-01-01 02:42:09 +00:00
211 lines
4.7 KiB
Odin
211 lines
4.7 KiB
Odin
package math
|
|
|
|
|
|
// pow returns x**y, the base-x exponential of y.
|
|
//
|
|
// Special cases are (in order):
|
|
//
|
|
// pow(x, ±0) = 1 for any x
|
|
// pow(1, y) = 1 for any y
|
|
// pow(x, 1) = x for any x
|
|
// pow(NaN, y) = NaN
|
|
// pow(x, NaN) = NaN
|
|
// pow(±0, y) = ±Inf for y an odd integer < 0
|
|
// pow(±0, -Inf) = +Inf
|
|
// pow(±0, +Inf) = +0
|
|
// pow(±0, y) = +Inf for finite y < 0 and not an odd integer
|
|
// pow(±0, y) = ±0 for y an odd integer > 0
|
|
// pow(±0, y) = +0 for finite y > 0 and not an odd integer
|
|
// pow(-1, ±Inf) = 1
|
|
// pow(x, +Inf) = +Inf for |x| > 1
|
|
// pow(x, -Inf) = +0 for |x| > 1
|
|
// pow(x, +Inf) = +0 for |x| < 1
|
|
// pow(x, -Inf) = +Inf for |x| < 1
|
|
// pow(+Inf, y) = +Inf for y > 0
|
|
// pow(+Inf, y) = +0 for y < 0
|
|
// pow(-Inf, y) = pow(-0, -y)
|
|
// pow(x, y) = NaN for finite x < 0 and finite non-integer y
|
|
//
|
|
// Special cases taken from FreeBSD's /usr/src/lib/msun/src/e_pow.c
|
|
// updated by IEEE Std. 754-2008 "Section 9.2.1 Special values".
|
|
@(require_results)
|
|
pow_f64 :: proc "contextless" (x, y: f64) -> f64 {
|
|
is_odd_int :: proc "contextless" (x: f64) -> bool {
|
|
if abs(x) >= (1<<53) {
|
|
return false
|
|
}
|
|
|
|
i, f := modf(x)
|
|
return f == 0 && (i64(i)&1 == 1)
|
|
}
|
|
|
|
switch {
|
|
case y == 0 || x == 1:
|
|
return 1.0
|
|
case y == 1:
|
|
return x
|
|
case is_nan(x) || is_nan(y):
|
|
return nan_f64()
|
|
case x == 0:
|
|
switch {
|
|
case y < 0:
|
|
if signbit(x) && is_odd_int(y) {
|
|
return inf_f64(-1)
|
|
}
|
|
return inf_f64(1)
|
|
case y > 0:
|
|
if signbit(x) && is_odd_int(y) {
|
|
return x
|
|
}
|
|
return 0.0
|
|
}
|
|
case is_inf(y, 0):
|
|
switch {
|
|
case x == -1:
|
|
return 1.0
|
|
case (abs(x) < 1) == is_inf(y, 1):
|
|
return 0.0
|
|
case:
|
|
return inf_f64(1)
|
|
}
|
|
case is_inf(x, 0):
|
|
if is_inf(x, -1) {
|
|
// pow(-0, -y)
|
|
return pow_f64(1.0/x, -y)
|
|
}
|
|
switch {
|
|
case y < 0:
|
|
return 0.0
|
|
case y > 0:
|
|
return inf_f64(1)
|
|
}
|
|
case y == 0.5:
|
|
return sqrt_f64(x)
|
|
case y == -0.5:
|
|
return 1.0 / sqrt_f64(x)
|
|
}
|
|
|
|
yi, yf := modf(abs(y))
|
|
if yf != 0 && x < 0 {
|
|
return nan_f64()
|
|
}
|
|
if yi >= 1<<63 {
|
|
// yi is a large even int that will lead to overflow (or underflow to 0)
|
|
// for all x except -1 (x == 1 was handled earlier)
|
|
switch {
|
|
case x == -1:
|
|
return 1.0
|
|
case (abs(x) < 1) == (y > 0):
|
|
return 0.0
|
|
case:
|
|
return inf_f64(1)
|
|
}
|
|
}
|
|
|
|
// ans = a1 * 2**ae (= 1 for now).
|
|
a1: f64 = 1
|
|
ae: int = 0
|
|
|
|
// ans *= x**yf
|
|
if yf != 0 {
|
|
if yf > 0.5 {
|
|
yf -= 1
|
|
yi += 1
|
|
}
|
|
a1 = exp(yf * ln(x))
|
|
}
|
|
|
|
// ans *= x**yi
|
|
// by multiplying in successive squarings
|
|
// of x according to bits of yi.
|
|
// accumulate powers of two into exp.
|
|
x1, xe := frexp(x)
|
|
for i := i64(yi); i != 0; i >>= 1 {
|
|
if xe < -1<<12 || 1<<12 < xe {
|
|
// catch xe before it overflows the left shift below
|
|
// Since i !=0 it has at least one bit still set, so ae will accumulate xe
|
|
// on at least one more iteration, ae += xe is a lower bound on ae
|
|
// the lower bound on ae exceeds the size of a f64 exp
|
|
// so the final call to ldexp will produce under/overflow (0/Inf)
|
|
ae += xe
|
|
break
|
|
}
|
|
if i&1 == 1 {
|
|
a1 *= x1
|
|
ae += xe
|
|
}
|
|
x1 *= x1
|
|
xe <<= 1
|
|
if x1 < .5 {
|
|
x1 += x1
|
|
xe -= 1
|
|
}
|
|
}
|
|
|
|
// ans = a1*2**ae
|
|
// if y < 0 { ans = 1 / ans }
|
|
// but in the opposite order
|
|
if y < 0 {
|
|
a1 = 1 / a1
|
|
ae = -ae
|
|
}
|
|
return ldexp(a1, ae)
|
|
}
|
|
|
|
|
|
@(require_results) pow_f16 :: proc "contextless" (x, power: f16) -> f16 { return f16(pow_f64(f64(x), f64(power))) }
|
|
@(require_results) pow_f32 :: proc "contextless" (x, power: f32) -> f32 { return f32(pow_f64(f64(x), f64(power))) }
|
|
|
|
|
|
|
|
exp_f64 :: proc "contextless" (x: f64) -> f64 {
|
|
LN2_HI :: 6.93147180369123816490e-01
|
|
LN2_LO :: 1.90821492927058770002e-10
|
|
LOG2_E :: 1.44269504088896338700e+00
|
|
|
|
OVERFLOW :: 7.09782712893383973096e+02
|
|
UNDERFLOW :: -7.45133219101941108420e+02
|
|
NEAR_ZERO :: 1.0 / (1 << 28) // 2**-28
|
|
|
|
// special cases
|
|
switch {
|
|
case is_nan(x) || is_inf(x, 1):
|
|
return x
|
|
case is_inf(x, -1):
|
|
return 0
|
|
case x > OVERFLOW:
|
|
return inf_f64(1)
|
|
case x < UNDERFLOW:
|
|
return 0
|
|
case -NEAR_ZERO < x && x < NEAR_ZERO:
|
|
return 1 + x
|
|
}
|
|
|
|
// reduce; computed as r = hi - lo for extra precision.
|
|
k: int
|
|
switch {
|
|
case x < 0:
|
|
k = int(LOG2_E*x - 0.5)
|
|
case x > 0:
|
|
k = int(LOG2_E*x + 0.5)
|
|
}
|
|
hi := x - f64(k)*LN2_HI
|
|
lo := f64(k) * LN2_LO
|
|
|
|
P1 :: 0h3FC5555555555555 // 1.66666666666666657415e-01
|
|
P2 :: 0hBF66C16C16BEBD93 // -2.77777777770155933842e-03
|
|
P3 :: 0h3F11566AAF25DE2C // 6.61375632143793436117e-05
|
|
P4 :: 0hBEBBBD41C5D26BF1 // -1.65339022054652515390e-06
|
|
P5 :: 0h3E66376972BEA4D0 // 4.13813679705723846039e-08
|
|
|
|
r := hi - lo
|
|
t := r * r
|
|
c := r - t*(P1+t*(P2+t*(P3+t*(P4+t*P5))))
|
|
y := 1 - ((lo - (r*c)/(2-c)) - hi)
|
|
return ldexp(y, k)
|
|
}
|
|
|
|
@(require_results) exp_f16 :: proc "contextless" (x: f16) -> f16 { return f16(exp_f64(f64(x))) }
|
|
@(require_results) exp_f32 :: proc "contextless" (x: f32) -> f32 { return f32(exp_f64(f64(x))) }
|
|
|