Skip to content

Commit b542b39

Browse files
authored
Merge pull request RustPython#893 from youknowone/float-overflow
PyFloat overflow handling + missing methods
2 parents 177704f + 1c814ff commit b542b39

File tree

2 files changed

+143
-127
lines changed

2 files changed

+143
-127
lines changed

tests/snippets/floats.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
b = 1.3
99
c = 1.2
1010
z = 2
11+
ov = 10 ** 1000
1112

1213
assert -a == -1.2
1314

@@ -37,6 +38,20 @@
3738
assert 6 / a == 5.0
3839
assert 2.0 % z == 0.0
3940
assert z % 2.0 == 0.0
41+
assert_raises(OverflowError, lambda: a + ov)
42+
assert_raises(OverflowError, lambda: a - ov)
43+
assert_raises(OverflowError, lambda: a * ov)
44+
assert_raises(OverflowError, lambda: a / ov)
45+
assert_raises(OverflowError, lambda: a // ov)
46+
assert_raises(OverflowError, lambda: a % ov)
47+
assert_raises(OverflowError, lambda: a ** ov)
48+
assert_raises(OverflowError, lambda: ov + a)
49+
assert_raises(OverflowError, lambda: ov - a)
50+
assert_raises(OverflowError, lambda: ov * a)
51+
assert_raises(OverflowError, lambda: ov / a)
52+
assert_raises(OverflowError, lambda: ov // a)
53+
assert_raises(OverflowError, lambda: ov % a)
54+
# assert_raises(OverflowError, lambda: ov ** a)
4055

4156
assert a < 5
4257
assert a <= 5
@@ -91,6 +106,8 @@
91106
assert 2.0.__rmul__(1.0) == 2.0
92107
assert 1.0.__truediv__(2.0) == 0.5
93108
assert 1.0.__rtruediv__(2.0) == 2.0
109+
assert 2.5.__divmod__(2.0) == (1.0, 0.5)
110+
assert 2.0.__rdivmod__(2.5) == (1.0, 0.5)
94111

95112
assert 1.0.__add__(1) == 2.0
96113
assert 1.0.__radd__(1) == 2.0
@@ -105,6 +122,11 @@
105122
assert_raises(ZeroDivisionError, lambda: 2.0 / 0)
106123
assert_raises(ZeroDivisionError, lambda: 2.0 // 0)
107124
assert_raises(ZeroDivisionError, lambda: 2.0 % 0)
125+
assert_raises(ZeroDivisionError, lambda: divmod(2.0, 0))
126+
assert_raises(ZeroDivisionError, lambda: 2 / 0.0)
127+
assert_raises(ZeroDivisionError, lambda: 2 // 0.0)
128+
assert_raises(ZeroDivisionError, lambda: 2 % 0.0)
129+
# assert_raises(ZeroDivisionError, lambda: divmod(2, 0.0))
108130

109131
assert 1.2.__int__() == 1
110132
assert 1.2.__float__() == 1.2
@@ -113,6 +135,10 @@
113135
assert float(1.2) == 1.2
114136
# assert math.trunc(1.2) == 1
115137

138+
assert 1.2 ** 2 == 1.44
139+
assert_raises(OverflowError, lambda: 1.2 ** (10 ** 1000))
140+
assert 3 ** 2.0 == 9.0
141+
116142
assert (1.7).real == 1.7
117143
assert (1.3).is_integer() == False
118144
assert (1.0).is_integer() == True

vm/src/obj/objfloat.rs

Lines changed: 117 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,48 @@ impl From<f64> for PyFloat {
4141
}
4242
}
4343

44-
fn mod_(v1: f64, v2: f64, vm: &VirtualMachine) -> PyResult {
44+
fn try_float(value: &PyObjectRef, vm: &VirtualMachine) -> PyResult<Option<f64>> {
45+
Ok(if objtype::isinstance(&value, &vm.ctx.float_type()) {
46+
Some(get_value(&value))
47+
} else if objtype::isinstance(&value, &vm.ctx.int_type()) {
48+
Some(objint::get_float_value(&value, vm)?)
49+
} else {
50+
None
51+
})
52+
}
53+
54+
fn inner_div(v1: f64, v2: f64, vm: &VirtualMachine) -> PyResult<f64> {
4555
if v2 != 0.0 {
46-
Ok(vm.ctx.new_float(v1 % v2))
56+
Ok(v1 / v2)
57+
} else {
58+
Err(vm.new_zero_division_error("float division by zero".to_string()))
59+
}
60+
}
61+
62+
fn inner_mod(v1: f64, v2: f64, vm: &VirtualMachine) -> PyResult<f64> {
63+
if v2 != 0.0 {
64+
Ok(v1 % v2)
4765
} else {
4866
Err(vm.new_zero_division_error("float mod by zero".to_string()))
4967
}
5068
}
5169

70+
fn inner_floordiv(v1: f64, v2: f64, vm: &VirtualMachine) -> PyResult<f64> {
71+
if v2 != 0.0 {
72+
Ok((v1 / v2).floor())
73+
} else {
74+
Err(vm.new_zero_division_error("float floordiv by zero".to_string()))
75+
}
76+
}
77+
78+
fn inner_divmod(v1: f64, v2: f64, vm: &VirtualMachine) -> PyResult<(f64, f64)> {
79+
if v2 != 0.0 {
80+
Ok(((v1 / v2).floor(), v1 % v2))
81+
} else {
82+
Err(vm.new_zero_division_error("float divmod()".to_string()))
83+
}
84+
}
85+
5286
#[pyimpl]
5387
impl PyFloat {
5488
#[pymethod(name = "__eq__")]
@@ -129,20 +163,15 @@ impl PyFloat {
129163
}
130164

131165
#[pymethod(name = "__add__")]
132-
fn add(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
133-
let v1 = self.value;
134-
if objtype::isinstance(&other, &vm.ctx.float_type()) {
135-
vm.ctx.new_float(v1 + get_value(&other))
136-
} else if objtype::isinstance(&other, &vm.ctx.int_type()) {
137-
vm.ctx
138-
.new_float(v1 + objint::get_value(&other).to_f64().unwrap())
139-
} else {
140-
vm.ctx.not_implemented()
141-
}
166+
fn add(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
167+
try_float(&other, vm)?.map_or_else(
168+
|| Ok(vm.ctx.not_implemented()),
169+
|other| (self.value + other).into_pyobject(vm),
170+
)
142171
}
143172

144173
#[pymethod(name = "__radd__")]
145-
fn radd(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
174+
fn radd(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
146175
self.add(other, vm)
147176
}
148177

@@ -153,45 +182,51 @@ impl PyFloat {
153182

154183
#[pymethod(name = "__divmod__")]
155184
fn divmod(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
156-
if objtype::isinstance(&other, &vm.ctx.float_type())
157-
|| objtype::isinstance(&other, &vm.ctx.int_type())
158-
{
159-
let r1 = self.floordiv(other.clone(), vm)?;
160-
let r2 = self.mod_(other, vm)?;
161-
Ok(vm.ctx.new_tuple(vec![r1, r2]))
162-
} else {
163-
Ok(vm.ctx.not_implemented())
164-
}
185+
try_float(&other, vm)?.map_or_else(
186+
|| Ok(vm.ctx.not_implemented()),
187+
|other| {
188+
let (r1, r2) = inner_divmod(self.value, other, vm)?;
189+
Ok(vm
190+
.ctx
191+
.new_tuple(vec![vm.ctx.new_float(r1), vm.ctx.new_float(r2)]))
192+
},
193+
)
194+
}
195+
196+
#[pymethod(name = "__rdivmod__")]
197+
fn rdivmod(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
198+
try_float(&other, vm)?.map_or_else(
199+
|| Ok(vm.ctx.not_implemented()),
200+
|other| {
201+
let (r1, r2) = inner_divmod(other, self.value, vm)?;
202+
Ok(vm
203+
.ctx
204+
.new_tuple(vec![vm.ctx.new_float(r1), vm.ctx.new_float(r2)]))
205+
},
206+
)
165207
}
166208

167209
#[pymethod(name = "__floordiv__")]
168210
fn floordiv(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
169-
let v1 = self.value;
170-
let v2 = if objtype::isinstance(&other, &vm.ctx.float_type) {
171-
get_value(&other)
172-
} else if objtype::isinstance(&other, &vm.ctx.int_type) {
173-
objint::get_float_value(&other, vm)?
174-
} else {
175-
return Ok(vm.ctx.not_implemented());
176-
};
211+
try_float(&other, vm)?.map_or_else(
212+
|| Ok(vm.ctx.not_implemented()),
213+
|other| inner_floordiv(self.value, other, vm)?.into_pyobject(vm),
214+
)
215+
}
177216

178-
if v2 != 0.0 {
179-
Ok(vm.ctx.new_float((v1 / v2).floor()))
180-
} else {
181-
Err(vm.new_zero_division_error("float floordiv by zero".to_string()))
182-
}
217+
#[pymethod(name = "__rfloordiv__")]
218+
fn rfloordiv(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
219+
try_float(&other, vm)?.map_or_else(
220+
|| Ok(vm.ctx.not_implemented()),
221+
|other| inner_floordiv(other, self.value, vm)?.into_pyobject(vm),
222+
)
183223
}
184224

185225
fn new_float(cls: PyClassRef, arg: PyObjectRef, vm: &VirtualMachine) -> PyResult<PyFloatRef> {
186226
let value = if objtype::isinstance(&arg, &vm.ctx.float_type()) {
187227
get_value(&arg)
188228
} else if objtype::isinstance(&arg, &vm.ctx.int_type()) {
189-
match objint::get_float_value(&arg, vm) {
190-
Ok(f) => f,
191-
Err(e) => {
192-
return Err(e);
193-
}
194-
}
229+
objint::get_float_value(&arg, vm)?
195230
} else if objtype::isinstance(&arg, &vm.ctx.str_type()) {
196231
match lexical::try_parse(objstr::get_value(&arg)) {
197232
Ok(f) => f,
@@ -222,28 +257,18 @@ impl PyFloat {
222257

223258
#[pymethod(name = "__mod__")]
224259
fn mod_(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
225-
let v1 = self.value;
226-
let v2 = if objtype::isinstance(&other, &vm.ctx.float_type) {
227-
get_value(&other)
228-
} else if objtype::isinstance(&other, &vm.ctx.int_type) {
229-
objint::get_float_value(&other, vm)?
230-
} else {
231-
return Ok(vm.ctx.not_implemented());
232-
};
233-
234-
mod_(v1, v2, vm)
260+
try_float(&other, vm)?.map_or_else(
261+
|| Ok(vm.ctx.not_implemented()),
262+
|other| inner_mod(self.value, other, vm)?.into_pyobject(vm),
263+
)
235264
}
236265

237266
#[pymethod(name = "__rmod__")]
238267
fn rmod(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
239-
let v2 = self.value;
240-
let v1 = if objtype::isinstance(&other, &vm.ctx.int_type) {
241-
objint::get_float_value(&other, vm)?
242-
} else {
243-
return Ok(vm.ctx.not_implemented());
244-
};
245-
246-
mod_(v1, v2, vm)
268+
try_float(&other, vm)?.map_or_else(
269+
|| Ok(vm.ctx.not_implemented()),
270+
|other| inner_mod(other, self.value, vm)?.into_pyobject(vm),
271+
)
247272
}
248273

249274
#[pymethod(name = "__neg__")]
@@ -252,44 +277,35 @@ impl PyFloat {
252277
}
253278

254279
#[pymethod(name = "__pow__")]
255-
fn pow(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
256-
let v1 = self.value;
257-
if objtype::isinstance(&other, &vm.ctx.float_type()) {
258-
vm.ctx.new_float(v1.powf(get_value(&other)))
259-
} else if objtype::isinstance(&other, &vm.ctx.int_type()) {
260-
let result = v1.powf(objint::get_value(&other).to_f64().unwrap());
261-
vm.ctx.new_float(result)
262-
} else {
263-
vm.ctx.not_implemented()
264-
}
280+
fn pow(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
281+
try_float(&other, vm)?.map_or_else(
282+
|| Ok(vm.ctx.not_implemented()),
283+
|other| self.value.powf(other).into_pyobject(vm),
284+
)
285+
}
286+
287+
#[pymethod(name = "__rpow__")]
288+
fn rpow(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
289+
try_float(&other, vm)?.map_or_else(
290+
|| Ok(vm.ctx.not_implemented()),
291+
|other| other.powf(self.value).into_pyobject(vm),
292+
)
265293
}
266294

267295
#[pymethod(name = "__sub__")]
268296
fn sub(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
269-
let v1 = self.value;
270-
if objtype::isinstance(&other, &vm.ctx.float_type()) {
271-
Ok(vm.ctx.new_float(v1 - get_value(&other)))
272-
} else if objtype::isinstance(&other, &vm.ctx.int_type()) {
273-
Ok(vm
274-
.ctx
275-
.new_float(v1 - objint::get_value(&other).to_f64().unwrap()))
276-
} else {
277-
Ok(vm.ctx.not_implemented())
278-
}
297+
try_float(&other, vm)?.map_or_else(
298+
|| Ok(vm.ctx.not_implemented()),
299+
|other| (self.value - other).into_pyobject(vm),
300+
)
279301
}
280302

281303
#[pymethod(name = "__rsub__")]
282304
fn rsub(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
283-
let v1 = self.value;
284-
if objtype::isinstance(&other, &vm.ctx.float_type()) {
285-
Ok(vm.ctx.new_float(get_value(&other) - v1))
286-
} else if objtype::isinstance(&other, &vm.ctx.int_type()) {
287-
Ok(vm
288-
.ctx
289-
.new_float(objint::get_value(&other).to_f64().unwrap() - v1))
290-
} else {
291-
Ok(vm.ctx.not_implemented())
292-
}
305+
try_float(&other, vm)?.map_or_else(
306+
|| Ok(vm.ctx.not_implemented()),
307+
|other| (other - self.value).into_pyobject(vm),
308+
)
293309
}
294310

295311
#[pymethod(name = "__repr__")]
@@ -299,52 +315,26 @@ impl PyFloat {
299315

300316
#[pymethod(name = "__truediv__")]
301317
fn truediv(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
302-
let v1 = self.value;
303-
let v2 = if objtype::isinstance(&other, &vm.ctx.float_type) {
304-
get_value(&other)
305-
} else if objtype::isinstance(&other, &vm.ctx.int_type) {
306-
objint::get_float_value(&other, vm)?
307-
} else {
308-
return Ok(vm.ctx.not_implemented());
309-
};
310-
311-
if v2 != 0.0 {
312-
Ok(vm.ctx.new_float(v1 / v2))
313-
} else {
314-
Err(vm.new_zero_division_error("float division by zero".to_string()))
315-
}
318+
try_float(&other, vm)?.map_or_else(
319+
|| Ok(vm.ctx.not_implemented()),
320+
|other| inner_div(self.value, other, vm)?.into_pyobject(vm),
321+
)
316322
}
317323

318324
#[pymethod(name = "__rtruediv__")]
319325
fn rtruediv(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
320-
let v1 = self.value;
321-
let v2 = if objtype::isinstance(&other, &vm.ctx.float_type) {
322-
get_value(&other)
323-
} else if objtype::isinstance(&other, &vm.ctx.int_type) {
324-
objint::get_float_value(&other, vm)?
325-
} else {
326-
return Ok(vm.ctx.not_implemented());
327-
};
328-
329-
if v1 != 0.0 {
330-
Ok(vm.ctx.new_float(v2 / v1))
331-
} else {
332-
Err(vm.new_zero_division_error("float division by zero".to_string()))
333-
}
326+
try_float(&other, vm)?.map_or_else(
327+
|| Ok(vm.ctx.not_implemented()),
328+
|other| inner_div(other, self.value, vm)?.into_pyobject(vm),
329+
)
334330
}
335331

336332
#[pymethod(name = "__mul__")]
337333
fn mul(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
338-
let v1 = self.value;
339-
if objtype::isinstance(&other, &vm.ctx.float_type) {
340-
Ok(vm.ctx.new_float(v1 * get_value(&other)))
341-
} else if objtype::isinstance(&other, &vm.ctx.int_type) {
342-
Ok(vm
343-
.ctx
344-
.new_float(v1 * objint::get_value(&other).to_f64().unwrap()))
345-
} else {
346-
Ok(vm.ctx.not_implemented())
347-
}
334+
try_float(&other, vm)?.map_or_else(
335+
|| Ok(vm.ctx.not_implemented()),
336+
|other| (self.value * other).into_pyobject(vm),
337+
)
348338
}
349339

350340
#[pymethod(name = "__rmul__")]

0 commit comments

Comments
 (0)