Skip to content

Commit 2153ebb

Browse files
committed
Fix pow(0, exp) with negative exp to raise ZeroDivisionError
1 parent df6acaf commit 2153ebb

3 files changed

Lines changed: 19 additions & 8 deletions

File tree

tests/snippets/ints.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@
4949
assert (-3).__rdivmod__(2) == (-1, -1)
5050
assert (2).__pow__(3) == 8
5151
assert (10).__pow__(-1) == 0.1
52+
with assert_raises(ZeroDivisionError):
53+
(0).__pow__(-1)
5254
assert (2).__rpow__(3) == 9
5355
assert (10).__mod__(5) == 0
5456
assert (10).__mod__(6) == 4

vm/src/obj/objfloat.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,15 @@ fn inner_gt_int(value: f64, other_int: &BigInt) -> bool {
154154
}
155155
}
156156

157+
pub fn float_pow(v1: f64, v2: f64, vm: &VirtualMachine) -> PyResult {
158+
if v1.is_zero() {
159+
let msg = format!("{} cannot be raised to a negative power", v1);
160+
Err(vm.new_zero_division_error(msg))
161+
} else {
162+
v1.powf(v2).into_pyobject(vm)
163+
}
164+
}
165+
157166
#[pyimpl]
158167
#[allow(clippy::trivially_copy_pass_by_ref)]
159168
impl PyFloat {
@@ -359,15 +368,15 @@ impl PyFloat {
359368
fn pow(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
360369
try_float(&other, vm)?.map_or_else(
361370
|| Ok(vm.ctx.not_implemented()),
362-
|other| self.value.powf(other).into_pyobject(vm),
371+
|other| float_pow(self.value, other, vm),
363372
)
364373
}
365374

366375
#[pymethod(name = "__rpow__")]
367376
fn rpow(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
368377
try_float(&other, vm)?.map_or_else(
369378
|| Ok(vm.ctx.not_implemented()),
370-
|other| other.powf(self.value).into_pyobject(vm),
379+
|other| float_pow(other, self.value, vm),
371380
)
372381
}
373382

vm/src/obj/objint.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use crate::vm::VirtualMachine;
1818
use super::objbool::IntoPyBool;
1919
use super::objbyteinner::PyByteInner;
2020
use super::objbytes::PyBytes;
21+
use super::objfloat;
2122
use super::objint;
2223
use super::objstr::{PyString, PyStringRef};
2324
use super::objtype;
@@ -118,12 +119,12 @@ impl_try_from_object_int!(
118119

119120
#[allow(clippy::collapsible_if)]
120121
fn inner_pow(int1: &PyInt, int2: &PyInt, vm: &VirtualMachine) -> PyResult {
121-
let result = if int2.value.is_negative() {
122+
if int2.value.is_negative() {
122123
let v1 = int1.float(vm)?;
123124
let v2 = int2.float(vm)?;
124-
vm.ctx.new_float(v1.pow(v2))
125+
objfloat::float_pow(v1, v2, vm)
125126
} else {
126-
if let Some(v2) = int2.value.to_u64() {
127+
Ok(if let Some(v2) = int2.value.to_u64() {
127128
vm.ctx.new_int(int1.value.pow(v2))
128129
} else if int1.value.is_one() || int1.value.is_zero() {
129130
vm.ctx.new_int(int1.value.clone())
@@ -137,9 +138,8 @@ fn inner_pow(int1: &PyInt, int2: &PyInt, vm: &VirtualMachine) -> PyResult {
137138
// missing feature: BigInt exp
138139
// practically, exp over u64 is not possible to calculate anyway
139140
vm.ctx.not_implemented()
140-
}
141-
};
142-
Ok(result)
141+
})
142+
}
143143
}
144144

145145
fn inner_mod(int1: &PyInt, int2: &PyInt, vm: &VirtualMachine) -> PyResult {

0 commit comments

Comments
 (0)