Allow log2() to work on large integers

This commit is contained in:
Devon Hollowood committed 2022-10-22 14:55:14 -07:00
1 parent 4331d65285
commit beeb0441ef
4 files changed
+34 -11

No files matched your search

Generated
+3 -2
View File
@@ -1250,9 +1250,9 @@ dependencies = [
[[package]]
name = "num-rational"
version = "0.4.0"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d41702bd167c2df5520b384281bc111a4b5efcf7fbc4c9c222c815b07e0a6a6a"
checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
dependencies = [
"autocfg",
"num-bigint",
@@ -1936,6 +1936,7 @@ dependencies = [
"num-bigint",
"num-complex",
"num-integer",
"num-rational",
"num-traits",
"num_enum",
"once_cell",
-2
View File
@@ -1162,8 +1162,6 @@ class MathTests(unittest.TestCase):
self.assertRaises(ValueError, math.log1p, -1)
self.assertEqual(math.log1p(INF), INF)
# TODO: RUSTPYTHON
@unittest.expectedFailure
@requires_IEEE_754
def testLog2(self):
self.assertRaises(TypeError, math.log2)
+1
View File
@@ -53,6 +53,7 @@ bzip2 = { version = "0.4", optional = true }
num-complex = "0.4.0"
num-bigint = "0.4.3"
num-integer = "0.1.44"
num-rational = "0.4.1"
crossbeam-utils = "=0.8.9"
itertools = "0.10.3"
+30 -7
View File
@@ -8,7 +8,8 @@ mod math {
identifier, PyObject, PyObjectRef, PyRef, PyResult, VirtualMachine,
};
use num_bigint::BigInt;
use num_traits::{One, Signed, Zero};
use num_rational::Ratio;
use num_traits::{One, Signed, ToPrimitive, Zero};
use rustpython_common::float_ops;
use std::cmp::Ordering;
@@ -155,13 +156,35 @@ mod math {
}
}
/// Generates the base-2 logarithm of a BigInt `x`
fn ilog2(x: &BigInt) -> f64 {
// log2(x) = log2(2^n * 2^-n * x) = n + log2(x/2^n)
// If we set 2^n to be the greatest power of 2 below x, then x/2^n is in [1, 2), and can
// thus be converted into a float.
let n = x.bits() as u32 - 1;
let frac = Ratio::new(x.clone(), BigInt::from(2).pow(n));
f64::from(n) + frac.to_f64().unwrap().log2()
}
#[pyfunction]
fn log2(x: ArgIntoFloat, vm: &VirtualMachine) -> PyResult<f64> {
let x = *x;
if x.is_nan() || x > 0.0_f64 {
Ok(x.log2())
} else {
Err(vm.new_value_error("math domain error".to_owned()))
fn log2(x: PyObjectRef, vm: &VirtualMachine) -> PyResult<f64> {
match x.try_float(vm) {
Ok(x) => {
let x = x.to_f64();
if x.is_nan() || x > 0.0_f64 {
Ok(x.log2())
} else {
Err(vm.new_value_error("math domain error".to_owned()))
}
}
Err(float_err) => {
if let Ok(x) = x.try_int(vm) {
Ok(ilog2(x.as_bigint()))
} else {
// Return the float error, as it will be more intuitive to users
Err(float_err)
}
}
}
}