Skip to content

Commit 6c56c22

Browse files
committed
fix floordiv and divmod by zero for ints and floats
1 parent a6d6f0f commit 6c56c22

File tree

4 files changed

+103
-29
lines changed

4 files changed

+103
-29
lines changed

tests/snippets/builtin_divmod.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
11
assert divmod(11, 3) == (3, 2)
22
assert divmod(8,11) == (0, 8)
33
assert divmod(0.873, 0.252) == (3.0, 0.11699999999999999)
4+
5+
try:
6+
divmod(5, 0)
7+
except ZeroDivisionError:
8+
pass
9+
else:
10+
assert False, "Expected divmod by zero to throw ZeroDivisionError"
11+
12+
try:
13+
divmod(5.0, 0.0)
14+
except ZeroDivisionError:
15+
pass
16+
else:
17+
assert False, "Expected divmod by zero to throw ZeroDivisionError"

tests/snippets/division_by_zero.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,27 @@
2626
else:
2727
assert False, 'Expected ZeroDivisionError'
2828

29+
try:
30+
5 // 0
31+
except ZeroDivisionError:
32+
pass
33+
else:
34+
assert False, 'Expected ZeroDivisionError'
35+
36+
try:
37+
5.3 // (-0.0)
38+
except ZeroDivisionError:
39+
pass
40+
else:
41+
assert False, 'Expected ZeroDivisionError'
42+
43+
try:
44+
divmod(5, 0)
45+
except ZeroDivisionError:
46+
pass
47+
else:
48+
assert False, 'Expected ZeroDivisionError'
49+
2950
try:
3051
raise ZeroDivisionError('Is an ArithmeticError subclass?')
3152
except ArithmeticError:

vm/src/obj/objfloat.rs

Lines changed: 47 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -172,9 +172,9 @@ fn float_divmod(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
172172
let args = PyFuncArgs::new(vec![i.clone(), i2.clone()], vec![]);
173173
if objtype::isinstance(i2, &vm.ctx.float_type()) || objtype::isinstance(i2, &vm.ctx.int_type())
174174
{
175-
let r1 = float_floordiv(vm, args.clone());
176-
let r2 = float_mod(vm, args.clone());
177-
Ok(vm.ctx.new_tuple(vec![r1.unwrap(), r2.unwrap()]))
175+
let r1 = float_floordiv(vm, args.clone())?;
176+
let r2 = float_mod(vm, args.clone())?;
177+
Ok(vm.ctx.new_tuple(vec![r1, r2]))
178178
} else {
179179
Err(vm.new_type_error(format!(
180180
"Cannot divmod power {} and {}",
@@ -190,18 +190,26 @@ fn float_floordiv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
190190
args,
191191
required = [(i, Some(vm.ctx.float_type())), (i2, None)]
192192
);
193-
if objtype::isinstance(i2, &vm.ctx.float_type()) {
194-
Ok(vm.ctx.new_float((get_value(i) / get_value(i2)).floor()))
195-
} else if objtype::isinstance(i2, &vm.ctx.int_type()) {
196-
Ok(vm
197-
.ctx
198-
.new_float((get_value(i) / objint::get_value(i2).to_f64().unwrap()).floor()))
193+
194+
let v1 = get_value(i);
195+
let v2 = if objtype::isinstance(i2, &vm.ctx.float_type) {
196+
get_value(i2)
197+
} else if objtype::isinstance(i2, &vm.ctx.int_type) {
198+
objint::get_value(i2)
199+
.to_f64()
200+
.ok_or_else(|| vm.new_overflow_error("int too large to convert to float".to_string()))?
199201
} else {
200-
Err(vm.new_type_error(format!(
202+
return Err(vm.new_type_error(format!(
201203
"Cannot floordiv {} and {}",
202204
i.borrow(),
203205
i2.borrow()
204-
)))
206+
)));
207+
};
208+
209+
if v2 != 0.0 {
210+
Ok(vm.ctx.new_float((v1 / v2).floor()))
211+
} else {
212+
Err(vm.new_zero_division_error("float floordiv by zero".to_string()))
205213
}
206214
}
207215

@@ -229,14 +237,22 @@ fn float_mod(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
229237
args,
230238
required = [(i, Some(vm.ctx.float_type())), (i2, None)]
231239
);
232-
if objtype::isinstance(i2, &vm.ctx.float_type()) {
233-
Ok(vm.ctx.new_float(get_value(i) % get_value(i2)))
234-
} else if objtype::isinstance(i2, &vm.ctx.int_type()) {
235-
Ok(vm
236-
.ctx
237-
.new_float(get_value(i) % objint::get_value(i2).to_f64().unwrap()))
240+
241+
let v1 = get_value(i);
242+
let v2 = if objtype::isinstance(i2, &vm.ctx.float_type) {
243+
get_value(i2)
244+
} else if objtype::isinstance(i2, &vm.ctx.int_type) {
245+
objint::get_value(i2)
246+
.to_f64()
247+
.ok_or_else(|| vm.new_overflow_error("int too large to convert to float".to_string()))?
248+
} else {
249+
return Err(vm.new_type_error(format!("Cannot mod {} and {}", i.borrow(), i2.borrow())));
250+
};
251+
252+
if v2 != 0.0 {
253+
Ok(vm.ctx.new_float(v1 % v2))
238254
} else {
239-
Err(vm.new_type_error(format!("Cannot mod {} and {}", i.borrow(), i2.borrow())))
255+
Err(vm.new_zero_division_error("float mod by zero".to_string()))
240256
}
241257
}
242258

@@ -272,15 +288,22 @@ fn float_truediv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
272288
args,
273289
required = [(i, Some(vm.ctx.float_type())), (i2, None)]
274290
);
291+
275292
let v1 = get_value(i);
276-
if objtype::isinstance(i2, &vm.ctx.float_type) {
277-
Ok(vm.ctx.new_float(v1 / get_value(i2)))
293+
let v2 = if objtype::isinstance(i2, &vm.ctx.float_type) {
294+
get_value(i2)
278295
} else if objtype::isinstance(i2, &vm.ctx.int_type) {
279-
Ok(vm
280-
.ctx
281-
.new_float(v1 / objint::get_value(i2).to_f64().unwrap()))
296+
objint::get_value(i2)
297+
.to_f64()
298+
.ok_or_else(|| vm.new_overflow_error("int too large to convert to float".to_string()))?
299+
} else {
300+
return Err(vm.new_type_error(format!("Cannot divide {} and {}", i.borrow(), i2.borrow())));
301+
};
302+
303+
if v2 != 0.0 {
304+
Ok(vm.ctx.new_float(v1 / v2))
282305
} else {
283-
Err(vm.new_type_error(format!("Cannot divide {} and {}", i.borrow(), i2.borrow())))
306+
Err(vm.new_zero_division_error("float division by zero".to_string()))
284307
}
285308
}
286309

vm/src/obj/objint.rs

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use super::objfloat;
88
use super::objstr;
99
use super::objtype;
1010
use num_bigint::{BigInt, ToBigInt};
11+
use num_integer::Integer;
1112
use num_traits::{Pow, Signed, ToPrimitive, Zero};
1213
use std::hash::{Hash, Hasher};
1314

@@ -289,7 +290,13 @@ fn int_floordiv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
289290
required = [(i, Some(vm.ctx.int_type())), (i2, None)]
290291
);
291292
if objtype::isinstance(i2, &vm.ctx.int_type()) {
292-
Ok(vm.ctx.new_int(get_value(i) / get_value(i2)))
293+
let (v1, v2) = (get_value(i), get_value(i2));
294+
295+
if v2 != BigInt::zero() {
296+
Ok(vm.ctx.new_int(v1 / v2))
297+
} else {
298+
Err(vm.new_zero_division_error("integer floordiv by zero".to_string()))
299+
}
293300
} else {
294301
Err(vm.new_type_error(format!(
295302
"Cannot floordiv {} and {}",
@@ -462,11 +469,20 @@ fn int_divmod(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
462469
args,
463470
required = [(i, Some(vm.ctx.int_type())), (i2, None)]
464471
);
465-
let args = PyFuncArgs::new(vec![i.clone(), i2.clone()], vec![]);
472+
466473
if objtype::isinstance(i2, &vm.ctx.int_type()) {
467-
let r1 = int_floordiv(vm, args.clone());
468-
let r2 = int_mod(vm, args.clone());
469-
Ok(vm.ctx.new_tuple(vec![r1.unwrap(), r2.unwrap()]))
474+
let v1 = get_value(i);
475+
let v2 = get_value(i2);
476+
477+
if v2 != BigInt::zero() {
478+
let (r1, r2) = v1.div_rem(&v2);
479+
480+
Ok(vm
481+
.ctx
482+
.new_tuple(vec![vm.ctx.new_int(r1), vm.ctx.new_int(r2)]))
483+
} else {
484+
Err(vm.new_zero_division_error("integer divmod by zero".to_string()))
485+
}
470486
} else {
471487
Err(vm.new_type_error(format!(
472488
"Cannot divmod power {} and {}",

0 commit comments

Comments
 (0)