Skip to content

Commit 6032b54

Browse files
authored
Fix negative ** fraction to returns complex value
Fix RustPython#1986 which (-2) ** 0.5 yields NaN. Result: RustPython gives `float.__rpow__(0.5, -2) (0.00000000000000008659560562354934+1.4142135623730951j)` CPython 3.85 gives `>>> float.__rpow__(0.5, -2) (8.659560562354934e-17+1.4142135623730951j)`
1 parent d193587 commit 6032b54

File tree

2 files changed

+20
-6
lines changed

2 files changed

+20
-6
lines changed

Lib/test/test_fractions.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -392,8 +392,6 @@ def testRound(self):
392392
self.assertTypedEquals(F(-2, 10), round(F(-15, 100), 1))
393393
self.assertTypedEquals(F(-2, 10), round(F(-25, 100), 1))
394394

395-
# TODO: RUSTPYTHON
396-
@unittest.expectedFailure
397395
def testArithmetic(self):
398396
self.assertEqual(F(1, 2), F(1, 10) + F(2, 5))
399397
self.assertEqual(F(-3, 10), F(1, 10) - F(2, 5))

vm/src/obj/objfloat.rs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use num_bigint::{BigInt, ToBigInt};
2+
use num_complex::Complex64;
23
use num_rational::Ratio;
34
use num_traits::{pow, ToPrimitive, Zero};
45

@@ -134,12 +135,16 @@ fn inner_divmod(v1: f64, v2: f64, vm: &VirtualMachine) -> PyResult<(f64, f64)> {
134135
}
135136
}
136137

137-
pub fn float_pow(v1: f64, v2: f64, vm: &VirtualMachine) -> PyResult<f64> {
138+
pub fn float_pow(v1: f64, v2: f64, vm: &VirtualMachine) -> PyResult {
138139
if v1.is_zero() {
139140
let msg = format!("{} cannot be raised to a negative power", v1);
140141
Err(vm.new_zero_division_error(msg))
142+
} else if v1.is_sign_negative() && (v2.floor() - v2).abs() > f64::EPSILON {
143+
let v1 = Complex64::new(v1, 0.);
144+
let v2 = Complex64::new(v2, 0.);
145+
v1.powc(v2).into_pyobject(vm)
141146
} else {
142-
Ok(v1.powf(v2))
147+
v1.powf(v2).into_pyobject(vm)
143148
}
144149
}
145150

@@ -258,6 +263,17 @@ impl PyFloat {
258263
)
259264
}
260265

266+
#[inline]
267+
fn complex_op<F>(&self, other: PyObjectRef, op: F, vm: &VirtualMachine) -> PyResult
268+
where
269+
F: Fn(f64, f64) -> PyResult,
270+
{
271+
try_float(&other, vm)?.map_or_else(
272+
|| Ok(vm.ctx.not_implemented()),
273+
|other| op(self.value, other).into_pyobject(vm),
274+
)
275+
}
276+
261277
#[inline]
262278
fn tuple_op<F>(&self, other: PyObjectRef, op: F, vm: &VirtualMachine) -> PyResult
263279
where
@@ -331,12 +347,12 @@ impl PyFloat {
331347

332348
#[pymethod(name = "__pow__")]
333349
fn pow(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
334-
self.simple_op(other, |a, b| float_pow(a, b, vm), vm)
350+
self.complex_op(other, |a, b| float_pow(a, b, vm), vm)
335351
}
336352

337353
#[pymethod(name = "__rpow__")]
338354
fn rpow(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
339-
self.simple_op(other, |a, b| float_pow(b, a, vm), vm)
355+
self.complex_op(other, |a, b| float_pow(b, a, vm), vm)
340356
}
341357

342358
#[pymethod(name = "__sub__")]

0 commit comments

Comments
 (0)