From ae9678e42fa56deb8da5bba8b6e9c02fcb834f16 Mon Sep 17 00:00:00 2001 From: YuNing Chen Date: Mon, 30 Sep 2024 15:12:18 +0800 Subject: [PATCH] refactor: log --- double/log.mbt | 48 ++--------------- math/log.mbt | 141 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 144 insertions(+), 45 deletions(-) create mode 100644 math/log.mbt diff --git a/double/log.mbt b/double/log.mbt index 5d85b97c..dac119fa 100644 --- a/double/log.mbt +++ b/double/log.mbt @@ -59,6 +59,7 @@ fn frexp(f : Double) -> (Double, Int) { return (frac, exp) } +/// @alert deprecated "Use `@math::ln` instead" pub fn ln(self : Double) -> Double { if self.is_nan() || self.is_inf() { return self @@ -83,6 +84,7 @@ pub fn ln(self : Double) -> Double { k * ln2_hi - (hfsq - (s * (hfsq + r) + k * ln2_lo) - f) } +/// @alert deprecated "Use `@math::log2` instead" pub fn log2(self : Double) -> Double { let (f, e) = frexp(self) if f == 0.5 { @@ -91,51 +93,7 @@ pub fn log2(self : Double) -> Double { ln(f) / ln2 + e.to_double() } +/// @alert deprecated "Use `@math.log10` instead" pub fn log10(self : Double) -> Double { ln(self) / ln10 } - -test "log2 log10" { - // log2 - assert_eq!(3.0.log2(), 1.584962500721156) - assert_eq!(2.0.log2(), 1.0) - assert_eq!(1.0.log2(), 0.0) - assert_eq!(0.5.log2(), -1.0) - assert_eq!(0.25.log2(), -2.0) - assert_eq!(0.1.log2(), -3.321928094887362) - - // log10 - assert_eq!(0.2.log10(), -0.6989700043360187) - assert_eq!(15.0.log10(), 1.1760912590556811) -} - -test "ln" { - assert_true!(not_a_number.ln().is_nan()) - assert_true!(infinity.ln().is_pos_inf()) - assert_true!(neg_infinity.ln().is_neg_inf()) - assert_true!((-1.0).ln().is_nan()) - assert_true!(0.0.ln().is_neg_inf()) - assert_true!((-0.0).ln().is_neg_inf()) - assert_eq!(50.0.ln(), 3.912023005428146) - assert_eq!(2.0.ln(), 0.6931471805599453) - assert_eq!(1.1125369292536007e-308.ln(), -709.0895657128241) - assert_eq!(5.562684646268003e-309.ln(), -709.782712893384) - assert_true!( - match frexp(0.0) { - (0.0, 0) => true - _ => false - }, - ) - assert_true!( - match frexp(infinity) { - (f, 0) => if f.is_pos_inf() { true } else { false } - _ => false - }, - ) - assert_true!( - match frexp(not_a_number) { - (f, 0) => if f.is_nan() { true } else { false } - _ => false - }, - ) -} diff --git a/math/log.mbt b/math/log.mbt new file mode 100644 index 00000000..65e13305 --- /dev/null +++ b/math/log.mbt @@ -0,0 +1,141 @@ +// Copyright 2024 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// ported from https://github.com/golang/go/blob/master/src/math/log.go + +let sqrt2 = 1.41421356237309504880168872420969807856967187537694807317667974 + +let ln2 = 0.693147180559945309417232121458176568075500134360255254120680009 + +let ln10 = 2.30258509299404568401799145468436420760110148862877297603332790 + +let ln2_hi = 6.93147180369123816490e-01 // 3fe62e42 fee00000 + +let ln2_lo = 1.90821492927058770002e-10 // 3dea39ef 35793c76 + +let l1 = 6.666666666666735130e-01 // 3FE55555 55555593 + +let l2 = 3.999999999940941908e-01 // 3FD99999 9997FA04 + +let l3 = 2.857142874366239149e-01 // 3FD24924 94229359 + +let l4 = 2.222219843214978396e-01 // 3FCC71C5 1D8E78AF + +let l5 = 1.818357216161805012e-01 // 3FC74664 96CB03DE + +let l6 = 1.531383769920937332e-01 // 3FC39A09 D078C69F + +let l7 = 1.479819860511658591e-01 // 3FC2F112 DF3E5244 + +fn normalize(f : Double) -> (Double, Int) { + if f.abs() < @double.min_positive { + return (f * 1L.lsl(52).to_double(), -52) + } + (f, 0) +} + +fn frexp(f : Double) -> (Double, Int) { + if f == 0.0 || f.is_inf() || f.is_nan() { + return (f, 0) + } + let (norm_f, exp) = normalize(f) + let u = norm_f.reinterpret_as_i64() + let exp = exp + u.lsr(52).land(0x7FFL).to_int() - 1022 + let frac = u + .land(0x7FFL.lsl(52).lnot()) + .lor(1022L.lsl(52)) + .reinterpret_as_double() + return (frac, exp) +} + +pub fn ln(input : Double) -> Double { + if input.is_nan() || input.is_inf() { + return input + } else if input < 0.0 { + return @double.not_a_number + } else if input == 0.0 { + return @double.neg_infinity + } + let (f1, ki) = frexp(input) + let (f, k) = if f1 < sqrt2 / 2.0 { + (f1 * 2.0 - 1.0, (ki - 1).to_double()) + } else { + (f1 - 1.0, ki.to_double()) + } + let s = f / (2.0 + f) + let s2 = s * s + let s4 = s2 * s2 + let t1 = s2 * (l1 + s4 * (l3 + s4 * (l5 + s4 * l7))) + let t2 = s4 * (l2 + s4 * (l4 + s4 * l6)) + let r = t1 + t2 + let hfsq = 0.5 * f * f + k * ln2_hi - (hfsq - (s * (hfsq + r) + k * ln2_lo) - f) +} + +pub fn log2(input : Double) -> Double { + let (f, e) = frexp(input) + if f == 0.5 { + return e.to_double() - 1.0 + } + ln(f) / ln2 + e.to_double() +} + +pub fn log10(input : Double) -> Double { + ln(input) / ln10 +} + +test "log2 log10" { + // log2 + assert_eq!(log2(3.0), 1.584962500721156) + assert_eq!(log2(2.0), 1.0) + assert_eq!(log2(1.0), 0.0) + assert_eq!(log2(0.5), -1.0) + assert_eq!(log2(0.25), -2.0) + assert_eq!(log2(0.1), -3.321928094887362) + + // log10 + assert_eq!(log10(0.2), -0.6989700043360187) + assert_eq!(log10(15.0), 1.1760912590556811) +} + +test "ln" { + assert_true!(ln(@double.not_a_number).is_nan()) + assert_true!(ln(@double.infinity).is_pos_inf()) + assert_true!(ln(@double.neg_infinity).is_neg_inf()) + assert_true!(ln(-1.0).is_nan()) + assert_true!(ln(0.0).is_neg_inf()) + assert_true!(ln(-0.0).is_neg_inf()) + assert_eq!(ln(50.0), 3.912023005428146) + assert_eq!(ln(2.0), 0.6931471805599453) + assert_eq!(ln(1.1125369292536007e-308), -709.0895657128241) + assert_eq!(ln(5.562684646268003e-309), -709.782712893384) + assert_true!( + match frexp(0.0) { + (0.0, 0) => true + _ => false + }, + ) + assert_true!( + match frexp(@double.infinity) { + (f, 0) => if f.is_pos_inf() { true } else { false } + _ => false + }, + ) + assert_true!( + match frexp(@double.not_a_number) { + (f, 0) => if f.is_nan() { true } else { false } + _ => false + }, + ) +}