Skip to content

Commit 03d2659

Browse files
committed
Runtime: Implemented builtin round and [float,int].__round__
1 parent fcd097b commit 03d2659

File tree

5 files changed

+81
-3
lines changed

5 files changed

+81
-3
lines changed

src/runtime/PyFloat.cpp

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
#include "PyFloat.hpp"
22
#include "MemoryError.hpp"
3+
#include "runtime/PyNumber.hpp"
4+
#include "runtime/Value.hpp"
5+
#include "runtime/forward.hpp"
36
#include "types/api.hpp"
47
#include "types/builtin.hpp"
58
#include "vm/VM.hpp"
69

10+
#include <cmath>
11+
712
namespace py {
813

914
template<> PyFloat *as(PyObject *obj)
@@ -56,6 +61,25 @@ PyResult<PyFloat *> PyFloat::create(double value)
5661
return Ok(obj);
5762
}
5863

64+
PyResult<PyObject *> PyFloat::__round__(PyObject *ndigits_obj) const
65+
{
66+
if (!ndigits_obj || ndigits_obj == py_none()) {
67+
return PyInteger::create(BigIntType{ as_f64() });
68+
}
69+
70+
if (!ndigits_obj->type()->issubclass(types::integer())) {
71+
return Err(type_error(
72+
"'{}' object cannot be interpreted as an integer", ndigits_obj->type()->name()));
73+
}
74+
75+
auto ndigits = static_cast<const PyInteger &>(*ndigits_obj).as_big_int();
76+
77+
const auto multiplier = std::pow(10., ndigits.get_d());
78+
const auto value = std::floor(as_f64() * multiplier) / multiplier;
79+
return PyFloat::create(value);
80+
}
81+
82+
5983
PyType *PyFloat::static_type() const { return types::float_(); }
6084

6185
double PyFloat::as_f64() const
@@ -70,7 +94,7 @@ namespace {
7094

7195
std::unique_ptr<TypePrototype> register_float()
7296
{
73-
return std::move(klass<PyFloat>("float").type);
97+
return std::move(klass<PyFloat>("float").def("__round__", &PyFloat::__round__).type);
7498
}
7599
}// namespace
76100

src/runtime/PyFloat.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ class PyFloat final : public PyNumber
1818

1919
double as_f64() const;
2020

21+
PyResult<PyObject *> __round__(PyObject *ndigits) const;
22+
2123
static std::function<std::unique_ptr<TypePrototype>()> type_factory();
2224
};
2325

src/runtime/PyInteger.cpp

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@
44
#include "PyFloat.hpp"
55
#include "TypeError.hpp"
66
#include "ValueError.hpp"
7-
#include "interpreter/Interpreter.hpp"
7+
#include "runtime/PyByteArray.hpp"
88
#include "runtime/PyObject.hpp"
99
#include "runtime/Value.hpp"
1010
#include "types/api.hpp"
1111
#include "types/builtin.hpp"
12-
#include "utilities.hpp"
1312
#include "vm/VM.hpp"
1413

14+
#include <gmpxx.h>
15+
1516
namespace py {
1617

1718
template<> PyInteger *as(PyObject *obj)
@@ -266,6 +267,24 @@ PyResult<PyObject *> PyInteger::from_bytes(PyType *type, PyTuple *args, PyDict *
266267
return result;
267268
}
268269

270+
PyResult<PyObject *> PyInteger::__round__(PyObject *ndigits_obj) const
271+
{
272+
if (!ndigits_obj || ndigits_obj == py_none()) { return PyInteger::create(as_big_int()); }
273+
274+
if (!ndigits_obj->type()->issubclass(types::integer())) {
275+
return Err(type_error(
276+
"'{}' object cannot be interpreted as an integer", ndigits_obj->type()->name()));
277+
}
278+
279+
auto ndigits = static_cast<const PyInteger &>(*ndigits_obj).as_big_int();
280+
281+
if (ndigits >= 0) { return PyInteger::create(as_big_int()); }
282+
283+
auto result = m_value - (m_value % Number{ BigIntType{ 10 } }.exp(Number{ -ndigits }));
284+
285+
return PyInteger::create(std::visit([](auto el) { return BigIntType{ el }; }, result.value));
286+
}
287+
269288
int64_t PyInteger::as_i64() const
270289
{
271290
ASSERT(std::holds_alternative<BigIntType>(m_value.value));
@@ -298,6 +317,7 @@ namespace {
298317
.def("bit_length", &PyInteger::bit_length)
299318
.def("bit_count", &PyInteger::bit_count)
300319
.def("to_bytes", &PyInteger::to_bytes)
320+
.def("__round__", &PyInteger::__round__)
301321
.classmethod("from_bytes", &PyInteger::from_bytes)
302322
.type);
303323
}

src/runtime/PyInteger.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#pragma once
22

33
#include "PyNumber.hpp"
4+
#include "forward.hpp"
5+
#include "runtime/PyType.hpp"
46

57
namespace py {
68

@@ -30,6 +32,8 @@ class PyInteger : public Interface<PyNumber, PyInteger>
3032
PyResult<PyObject *> __lshift__(const PyObject *other) const;
3133
PyResult<PyObject *> __rshift__(const PyObject *other) const;
3234

35+
PyResult<PyObject *> __round__(PyObject *ndigits) const;
36+
3337
PyResult<PyObject *> bit_length() const;
3438
PyResult<PyObject *> bit_count() const;
3539

src/runtime/modules/BuiltinsModule.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1313,6 +1313,29 @@ PyResult<PyObject *> divmod(PyTuple *args, PyDict *kwargs, Interpreter &)
13131313
return lhs->divmod(rhs);
13141314
}
13151315

1316+
PyResult<PyObject *> round(PyTuple *args, PyDict *kwargs, Interpreter &)
1317+
{
1318+
auto result = PyArgsParser<PyObject *, PyObject *>::unpack_tuple(args,
1319+
kwargs,
1320+
"round",
1321+
std::integral_constant<size_t, 1>{},
1322+
std::integral_constant<size_t, 2>{},
1323+
nullptr);
1324+
1325+
if (result.is_err()) { return Err(result.unwrap_err()); }
1326+
auto [value, ndigits] = result.unwrap();
1327+
1328+
auto round = value->lookup_attribute(PyString::create("__round__").unwrap());
1329+
1330+
if (std::get<1>(round) == LookupAttrResult::NOT_FOUND) {
1331+
return Err(type_error("type {} doesn't define __round__ method", value->type()->name()));
1332+
}
1333+
if (std::get<0>(round).is_err()) { return std::get<0>(round); }
1334+
1335+
if (!ndigits) { ndigits = py_none(); }
1336+
return std::get<0>(round).unwrap()->call(PyTuple::create(ndigits).unwrap(), nullptr);
1337+
}
1338+
13161339
auto builtin_types()
13171340
{
13181341
return std::array{
@@ -1580,6 +1603,11 @@ PyModule *builtins_module(Interpreter &interpreter)
15801603
return divmod(args, kwargs, interpreter);
15811604
}));
15821605

1606+
s_builtin_module->add_symbol(PyString::create("round").unwrap(),
1607+
heap.allocate<PyNativeFunction>("round", [&interpreter](PyTuple *args, PyDict *kwargs) {
1608+
return round(args, kwargs, interpreter);
1609+
}));
1610+
15831611
return s_builtin_module;
15841612
}
15851613

0 commit comments

Comments
 (0)