Skip to content

Commit

Permalink
Merge pull request #2 from r3v4-onbloc/feature/additional-math-libraries
Browse files Browse the repository at this point in the history
feat: additional math libraries
  • Loading branch information
r3v4s authored Feb 7, 2023
2 parents 62e3d0a + 69ef2f7 commit 06ea99c
Show file tree
Hide file tree
Showing 5 changed files with 519 additions and 0 deletions.
43 changes: 43 additions & 0 deletions stdlibs/math/frexp.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package math

import (
imath "internal/math"
)

// Frexp breaks f into a normalized fraction
// and an integral power of two.
// It returns frac and exp satisfying f == frac × 2**exp,
// with the absolute value of frac in the interval [½, 1).
//
// Special cases are:
//
// Frexp(±0) = ±0, 0
// Frexp(±Inf) = ±Inf, 0
// Frexp(NaN) = NaN, 0
func Frexp(f float64) (frac float64, exp int) {
// if haveArchFrexp {
// return archFrexp(f)
// }
return frexp(f)
}

func frexp(f float64) (frac float64, exp int) {
// special cases
switch {
case f == 0:
return f, 0 // correctly return -0
case IsInf(f, 0) || IsNaN(f):
return f, 0
}
f, exp = normalize(f)
x := imath.Float64bits(f)
exp += int((x>>shift)&mask) - bias + 1
x &^= mask << shift
x |= (-1 + bias) << shift
frac = imath.Float64frombits(x)
return
}
129 changes: 129 additions & 0 deletions stdlibs/math/log.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package math

/*
Floating-point logarithm.
*/

// The original C code, the long comment, and the constants
// below are from FreeBSD's /usr/src/lib/msun/src/e_log.c
// and came with this notice. The go code is a simpler
// version of the original C.
//
// ====================================================
// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
//
// Developed at SunPro, a Sun Microsystems, Inc. business.
// Permission to use, copy, modify, and distribute this
// software is freely granted, provided that this notice
// is preserved.
// ====================================================
//
// __ieee754_log(x)
// Return the logarithm of x
//
// Method :
// 1. Argument Reduction: find k and f such that
// x = 2**k * (1+f),
// where sqrt(2)/2 < 1+f < sqrt(2) .
//
// 2. Approximation of log(1+f).
// Let s = f/(2+f) ; based on log(1+f) = log(1+s) - log(1-s)
// = 2s + 2/3 s**3 + 2/5 s**5 + .....,
// = 2s + s*R
// We use a special Reme algorithm on [0,0.1716] to generate
// a polynomial of degree 14 to approximate R. The maximum error
// of this polynomial approximation is bounded by 2**-58.45. In
// other words,
// 2 4 6 8 10 12 14
// R(z) ~ L1*s +L2*s +L3*s +L4*s +L5*s +L6*s +L7*s
// (the values of L1 to L7 are listed in the program) and
// | 2 14 | -58.45
// | L1*s +...+L7*s - R(z) | <= 2
// | |
// Note that 2s = f - s*f = f - hfsq + s*hfsq, where hfsq = f*f/2.
// In order to guarantee error in log below 1ulp, we compute log by
// log(1+f) = f - s*(f - R) (if f is not too large)
// log(1+f) = f - (hfsq - s*(hfsq+R)). (better accuracy)
//
// 3. Finally, log(x) = k*Ln2 + log(1+f).
// = k*Ln2_hi+(f-(hfsq-(s*(hfsq+R)+k*Ln2_lo)))
// Here Ln2 is split into two floating point number:
// Ln2_hi + Ln2_lo,
// where n*Ln2_hi is always exact for |n| < 2000.
//
// Special cases:
// log(x) is NaN with signal if x < 0 (including -INF) ;
// log(+INF) is +INF; log(0) is -INF with signal;
// log(NaN) is that NaN with no signal.
//
// Accuracy:
// according to an error analysis, the error is always less than
// 1 ulp (unit in the last place).
//
// Constants:
// The hexadecimal values are the intended ones for the following
// constants. The decimal values may be used, provided that the
// compiler will convert from decimal to binary accurately enough
// to produce the hexadecimal values shown.

// Log returns the natural logarithm of x.
//
// Special cases are:
//
// Log(+Inf) = +Inf
// Log(0) = -Inf
// Log(x < 0) = NaN
// Log(NaN) = NaN
func Log(x float64) float64 {
// if haveArchLog {
// return archLog(x)
// }
return log(x)
}

func log(x float64) float64 {
const (
Ln2Hi = 6.93147180369123816490e-01 /* 3fe62e42 fee00000 */
Ln2Lo = 1.90821492927058770002e-10 /* 3dea39ef 35793c76 */
L1 = 6.666666666666735130e-01 /* 3FE55555 55555593 */
L2 = 3.999999999940941908e-01 /* 3FD99999 9997FA04 */
L3 = 2.857142874366239149e-01 /* 3FD24924 94229359 */
L4 = 2.222219843214978396e-01 /* 3FCC71C5 1D8E78AF */
L5 = 1.818357216161805012e-01 /* 3FC74664 96CB03DE */
L6 = 1.531383769920937332e-01 /* 3FC39A09 D078C69F */
L7 = 1.479819860511658591e-01 /* 3FC2F112 DF3E5244 */
)

// special cases
switch {
case IsNaN(x) || IsInf(x, 1):
return x
case x < 0:
return NaN()
case x == 0:
return Inf(-1)
}

// reduce
f1, ki := Frexp(x)
if f1 < Sqrt2/2 {
f1 *= 2
ki--
}
f := f1 - 1
k := float64(ki)

// compute
s := f / (2 + f)
s2 := s * s
s4 := s2 * s2
t1 := s2 * (L1 + s4*(L3+s4*(L5+s4*L7)))
t2 := s4 * (L2 + s4*(L4+s4*L6))
R := t1 + t2
hfsq := 0.5 * f * f
return k*Ln2Hi - ((hfsq - (s*(hfsq+R) + k*Ln2Lo)) - f)
}
37 changes: 37 additions & 0 deletions stdlibs/math/log10.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package math

// Log10 returns the decimal logarithm of x.
// The special cases are the same as for Log.
func Log10(x float64) float64 {
// if haveArchLog10 {
// return archLog10(x)
// }
return log10(x)
}

func log10(x float64) float64 {
return Log(x) * (1 / Ln10)
}

// Log2 returns the binary logarithm of x.
// The special cases are the same as for Log.
func Log2(x float64) float64 {
// if haveArchLog2 {
// return archLog2(x)
// }
return log2(x)
}

func log2(x float64) float64 {
frac, exp := Frexp(x)
// Make sure exact powers of two give an exact answer.
// Don't depend on Log(0.5)*(1/Ln2)+exp being exactly exp-1.
if frac == 0.5 {
return float64(exp - 1)
}
return Log(frac)*(1/Ln2) + float64(exp)
}
161 changes: 161 additions & 0 deletions stdlibs/math/pow.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package math

import (
imath "internal/math"
)

func isOddInt(x float64) bool {
xi, xf := Modf(x)
return xf == 0 && int64(xi)&1 == 1
}

// 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".

// 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
func Pow(x, y float64) float64 {
// if haveArchPow {
// return archPow(x, y)
// }
return pow(x, y)
}

func pow(x, y float64) float64 {
switch {
case y == 0 || x == 1:
return 1
case y == 1:
return x
case IsNaN(x) || IsNaN(y):
return NaN()
case x == 0:
switch {
case y < 0:
if isOddInt(y) {
return Copysign(Inf(1), x)
}
return Inf(1)
case y > 0:
if isOddInt(y) {
return x
}
return 0
}
case IsInf(y, 0):
switch {
case x == -1:
return 1
case (Abs(x) < 1) == IsInf(y, 1):
return 0
default:
return Inf(1)
}
case IsInf(x, 0):
if IsInf(x, -1) {
return Pow(1/x, -y) // Pow(-0, -y)
}
switch {
case y < 0:
return 0
case y > 0:
return Inf(1)
}
case y == 0.5:
return Sqrt(x)
case y == -0.5:
return 1 / Sqrt(x)
}

yi, yf := Modf(Abs(y))
if yf != 0 && x < 0 {
return NaN()
}
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
case (Abs(x) < 1) == (y > 0):
return 0
default:
return Inf(1)
}
}

// ans = a1 * 2**ae (= 1 for now).
a1 := 1.0
ae := 0

// ans *= x**yf
if yf != 0 {
if yf > 0.5 {
yf--
yi++
}
a1 = Exp(yf * Log(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 := int64(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 float64 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--
}
}

// 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)
}
Loading

0 comments on commit 06ea99c

Please sign in to comment.