Skip to content

Commit d223af6

Browse files
committed
Add math.frexp
1 parent 54152b8 commit d223af6

4 files changed

Lines changed: 47 additions & 9 deletions

File tree

tests/snippets/math_module.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import math
2-
from testutils import assertRaises
2+
from testutils import assertRaises, assert_raises
33

44
# assert(math.exp(2) == math.exp(2.0))
55
# assert(math.exp(True) == math.exp(1.0))
@@ -78,3 +78,12 @@ def __floor__(self):
7878
math.ceil(object())
7979
with assertRaises(TypeError):
8080
math.floor(object())
81+
82+
assert str(math.frexp(0.0)) == str((+0.0, 0))
83+
assert str(math.frexp(-0.0)) == str((-0.0, 0))
84+
assert math.frexp(1) == (0.5, 1)
85+
assert math.frexp(1.5) == (0.75, 1)
86+
87+
assert math.frexp(float('inf')) == (float('inf'), 0)
88+
assert str(math.frexp(float('nan'))) == str((float('nan'), 0))
89+
assert_raises(TypeError, lambda: math.frexp(None))

vm/src/obj/objfloat.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,11 @@ impl PyFloat {
298298
)
299299
}
300300

301+
#[pymethod(name = "__pos__")]
302+
fn pos(&self, _vm: &VirtualMachine) -> f64 {
303+
self.value
304+
}
305+
301306
#[pymethod(name = "__neg__")]
302307
fn neg(&self, _vm: &VirtualMachine) -> f64 {
303308
-self.value
@@ -475,6 +480,17 @@ impl PyFloat {
475480
}
476481
}
477482

483+
pub fn ufrexp(value: f64) -> (f64, i32) {
484+
if 0.0 == value {
485+
(0.0, 0i32)
486+
} else {
487+
let bits = value.to_bits();
488+
let exponent: i32 = ((bits >> 52) & 0x7ff) as i32 - 1022;
489+
let mantissa_bits = bits & (0x000fffffffffffff) | (1022 << 52);
490+
(f64::from_bits(mantissa_bits), exponent)
491+
}
492+
}
493+
478494
pub type PyFloatRef = PyRef<PyFloat>;
479495

480496
// Retrieve inner float value:

vm/src/pyhash.rs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::hash::{Hash, Hasher};
22

3+
use crate::obj::objfloat;
34
use crate::pyobject::PyObjectRef;
45
use crate::pyobject::PyResult;
56
use crate::vm::VirtualMachine;
@@ -28,14 +29,7 @@ pub fn hash_float(value: f64) -> PyHash {
2829
};
2930
}
3031

31-
let frexp = if 0.0 == value {
32-
(value, 0i32)
33-
} else {
34-
let bits = value.to_bits();
35-
let exponent: i32 = ((bits >> 52) & 0x7ff) as i32 - 1022;
36-
let mantissa_bits = bits & (0x000fffffffffffff) | (1022 << 52);
37-
(f64::from_bits(mantissa_bits), exponent)
38-
};
32+
let frexp = objfloat::ufrexp(value);
3933

4034
// process 28 bits at a time; this should work well both for binary
4135
// and hexadecimal floating point.

vm/src/stdlib/math.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,23 @@ fn math_floor(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
209209
}
210210
}
211211

212+
fn math_frexp(value: PyObjectRef, vm: &VirtualMachine) -> PyResult {
213+
objfloat::try_float(&value, vm)?.map_or_else(
214+
|| Err(vm.new_type_error(format!("must be real number, not {}", value.class()))),
215+
|value| {
216+
let (m, e) = if value.is_finite() {
217+
let (m, e) = objfloat::ufrexp(value);
218+
(m * value.signum(), e)
219+
} else {
220+
(value, 0)
221+
};
222+
Ok(vm
223+
.ctx
224+
.new_tuple(vec![vm.ctx.new_float(m), vm.ctx.new_int(e)]))
225+
},
226+
)
227+
}
228+
212229
pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
213230
let ctx = &vm.ctx;
214231

@@ -256,6 +273,8 @@ pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
256273
"gamma" => ctx.new_rustfunc(math_gamma),
257274
"lgamma" => ctx.new_rustfunc(math_lgamma),
258275

276+
"frexp" => ctx.new_rustfunc(math_frexp),
277+
259278
// Rounding functions:
260279
"trunc" => ctx.new_rustfunc(math_trunc),
261280
"ceil" => ctx.new_rustfunc(math_ceil),

0 commit comments

Comments
 (0)