diff --git a/Cargo.lock b/Cargo.lock index 2fa56aaa77..9833a0a918 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -514,6 +514,16 @@ dependencies = [ "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "num-rational" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-bigint 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "num-traits" version = "0.2.6" @@ -756,6 +766,7 @@ dependencies = [ "num-bigint 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "num-complex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", + "num-rational 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1285,6 +1296,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum num-bigint 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "10b8423ea72ec64751198856a853e07b37087cfc9b53a87ecb19bff67b6d1320" "checksum num-complex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "107b9be86cd2481930688277b675b0114578227f034674726605b8a482d8baf8" "checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea" +"checksum num-rational 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4e96f040177bb3da242b5b1ecf3f54b5d5af3efbbfb18608977a5d2767b22f10" "checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1" "checksum ordermap 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a86ed3f5f244b372d6b1a00b72ef7f8876d0bc6a78a4c9985c53614041512063" "checksum petgraph 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)" = "9c3659d1ee90221741f65dd128d9998311b0e40c5d3c23a62445938214abce4f" diff --git a/tests/snippets/floats.py b/tests/snippets/floats.py index ac24a4dbfa..6675615c56 100644 --- a/tests/snippets/floats.py +++ b/tests/snippets/floats.py @@ -87,3 +87,22 @@ assert (1.7).real == 1.7 assert (1.3).is_integer() == False assert (1.0).is_integer() == True + +assert (0.875).as_integer_ratio() == (7, 8) +assert (-0.875).as_integer_ratio() == (-7, 8) +assert (0.0).as_integer_ratio() == (0, 1) +assert (11.5).as_integer_ratio() == (23, 2) +assert (0.0).as_integer_ratio() == (0, 1) +assert (2.5).as_integer_ratio() == (5, 2) +assert (0.5).as_integer_ratio() == (1, 2) +assert (2.1).as_integer_ratio() == (4728779608739021, 2251799813685248) +assert (-2.1).as_integer_ratio() == (-4728779608739021, 2251799813685248) +assert (-2100.0).as_integer_ratio() == (-2100, 1) +assert (2.220446049250313e-16).as_integer_ratio() == (1, 4503599627370496) +assert (1.7976931348623157e+308).as_integer_ratio() == (179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368, 1) +assert (2.2250738585072014e-308).as_integer_ratio() == (1, 44942328371557897693232629769725618340449424473557664318357520289433168951375240783177119330601884005280028469967848339414697442203604155623211857659868531094441973356216371319075554900311523529863270738021251442209537670585615720368478277635206809290837627671146574559986811484619929076208839082406056034304) + +assert_raises(OverflowError, float('inf').as_integer_ratio) +assert_raises(OverflowError, float('-inf').as_integer_ratio) +assert_raises(ValueError, float('nan').as_integer_ratio) + diff --git a/vm/Cargo.toml b/vm/Cargo.toml index 85e0bf936e..e245aadbcc 100644 --- a/vm/Cargo.toml +++ b/vm/Cargo.toml @@ -10,6 +10,7 @@ num-complex = "0.2" num-bigint = "0.2.1" num-traits = "0.2" num-integer = "0.1.39" +num-rational = "0.2.1" rand = "0.5" log = "0.3" rustpython_parser = {path = "../parser"} diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index a2038bffbc..d2bf03ef5b 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -2,12 +2,14 @@ use super::objbytes; use super::objint; use super::objstr; use super::objtype; +use crate::function::PyRef; use crate::pyobject::{ PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, PyResult, TypeProtocol, }; use crate::vm::VirtualMachine; use num_bigint::ToBigInt; +use num_rational::Ratio; use num_traits::ToPrimitive; #[derive(Debug, Copy, Clone, PartialEq)] @@ -27,6 +29,25 @@ impl From for PyFloat { } } +impl PyFloat { + fn as_integer_ratio(zelf: PyRef, vm: &mut VirtualMachine) -> PyResult { + let value = zelf.value; + if value.is_infinite() { + return Err( + vm.new_overflow_error("cannot convert Infinity to integer ratio".to_string()) + ); + } + if value.is_nan() { + return Err(vm.new_value_error("cannot convert NaN to integer ratio".to_string())); + } + + let ratio = Ratio::from_float(value).unwrap(); + let numer = vm.ctx.new_int(ratio.numer().clone()); + let denom = vm.ctx.new_int(ratio.denom().clone()); + Ok(vm.ctx.new_tuple(vec![numer, denom])) + } +} + fn float_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(float, Some(vm.ctx.float_type()))]); let v = get_value(float); @@ -494,4 +515,9 @@ pub fn init(context: &PyContext) { "is_integer", context.new_rustfunc(float_is_integer), ); + context.set_attr( + &float_type, + "as_integer_ratio", + context.new_rustfunc(PyFloat::as_integer_ratio), + ); }