Skip to content

Commit 9888d27

Browse files
authored
Merge pull request RustPython#981 from youknowone/frexp
Add math.frexp
2 parents 23344dd + d223af6 commit 9888d27

File tree

4 files changed

+47
-9
lines changed

4 files changed

+47
-9
lines changed

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
@@ -300,6 +300,11 @@ impl PyFloat {
300300
)
301301
}
302302

303+
#[pymethod(name = "__pos__")]
304+
fn pos(&self, _vm: &VirtualMachine) -> f64 {
305+
self.value
306+
}
307+
303308
#[pymethod(name = "__neg__")]
304309
fn neg(&self, _vm: &VirtualMachine) -> f64 {
305310
-self.value
@@ -530,6 +535,17 @@ fn test_to_hex() {
530535
}
531536
}
532537

538+
pub fn ufrexp(value: f64) -> (f64, i32) {
539+
if 0.0 == value {
540+
(0.0, 0i32)
541+
} else {
542+
let bits = value.to_bits();
543+
let exponent: i32 = ((bits >> 52) & 0x7ff) as i32 - 1022;
544+
let mantissa_bits = bits & (0x000fffffffffffff) | (1022 << 52);
545+
(f64::from_bits(mantissa_bits), exponent)
546+
}
547+
}
548+
533549
pub type PyFloatRef = PyRef<PyFloat>;
534550

535551
// 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;
@@ -32,14 +33,7 @@ pub fn hash_float(value: f64) -> PyHash {
3233
};
3334
}
3435

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

4438
// process 28 bits at a time; this should work well both for binary
4539
// 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)