From 9950865c39044e9fef295d0676af7c5fd55289ea Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sat, 2 Sep 2017 21:19:01 +0300 Subject: py/objfloat: Fix binary ops with incompatible objects. These are now returned as "operation not supported" instead of raising TypeError. In particular, this fixes equality for float vs incompatible types, which now properly results in False instead of exception. This also paves the road to support reverse operation (e.g. __radd__) with float objects. This is achieved by introducing mp_obj_get_float_maybe(), similar to existing mp_obj_get_int_maybe(). --- tests/float/float_compare.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 tests/float/float_compare.py (limited to 'tests/float') diff --git a/tests/float/float_compare.py b/tests/float/float_compare.py new file mode 100644 index 000000000..105923ac7 --- /dev/null +++ b/tests/float/float_compare.py @@ -0,0 +1,22 @@ +# Extended float comparisons + +class Foo: + pass + +foo = Foo() + +print(foo == 1.0) +print(1.0 == foo) +print(1.0 == Foo) +print(1.0 == []) +print(1.0 == {}) + +try: + print(foo < 1.0) +except TypeError: + print("TypeError") + +try: + print(1.0 < foo) +except TypeError: + print("TypeError") -- cgit v1.2.3 From d4b75f6b6822885e331c69a74e56e23af40a6264 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 4 Sep 2017 14:16:27 +1000 Subject: py/obj: Fix comparison of float/complex NaN with itself. IEEE floating point is specified such that a comparison of NaN with itself returns false, and Python respects these semantics. This patch makes uPy also have these semantics. The fix has a minor impact on the speed of the object-equality fast-path, but that seems to be unavoidable and it's much more important to have correct behaviour (especially in this case where the wrong answer for nan==nan is silently returned). --- py/obj.c | 11 ++++++++++- tests/float/complex1.py | 5 +++++ tests/float/float1.py | 5 +++++ 3 files changed, 20 insertions(+), 1 deletion(-) (limited to 'tests/float') diff --git a/py/obj.c b/py/obj.c index 90ce47e8f..857fe373f 100644 --- a/py/obj.c +++ b/py/obj.c @@ -162,7 +162,16 @@ bool mp_obj_is_callable(mp_obj_t o_in) { // comparison returns NotImplemented, == and != are decided by comparing the object // pointer." bool mp_obj_equal(mp_obj_t o1, mp_obj_t o2) { - if (o1 == o2) { + // Float (and complex) NaN is never equal to anything, not even itself, + // so we must have a special check here to cover those cases. + if (o1 == o2 + #if MICROPY_PY_BUILTINS_FLOAT + && !mp_obj_is_float(o1) + #endif + #if MICROPY_PY_BUILTINS_COMPLEX + && !MP_OBJ_IS_TYPE(o1, &mp_type_complex) + #endif + ) { return true; } if (o1 == mp_const_none || o2 == mp_const_none) { diff --git a/tests/float/complex1.py b/tests/float/complex1.py index a6038de04..7f0b317b3 100644 --- a/tests/float/complex1.py +++ b/tests/float/complex1.py @@ -37,6 +37,11 @@ ans = 1j ** 2.5j; print("%.5g %.5g" % (ans.real, ans.imag)) print(1j == 1) print(1j == 1j) +# comparison of nan is special +nan = float('nan') * 1j +print(nan == 1j) +print(nan == nan) + # builtin abs print(abs(1j)) print("%.5g" % abs(1j + 2)) diff --git a/tests/float/float1.py b/tests/float/float1.py index 93f6f014c..137dacc23 100644 --- a/tests/float/float1.py +++ b/tests/float/float1.py @@ -60,6 +60,11 @@ print(1.2 <= -3.4) print(1.2 >= 3.4) print(1.2 >= -3.4) +# comparison of nan is special +nan = float('nan') +print(nan == 1.2) +print(nan == nan) + try: 1.0 / 0 except ZeroDivisionError: -- cgit v1.2.3 From bdc6e86e079dd8a82e9ead1d4041c2e17c882437 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 26 Sep 2017 12:57:51 +1000 Subject: py/objfloat: Support raising a negative number to a fractional power. This returns a complex number, following CPython behaviour. For ports that don't have complex numbers enabled this will raise a ValueError which gives a fail-safe for scripts that were written assuming complex numbers exist. --- py/objfloat.c | 7 +++++++ tests/float/complex1.py | 4 ++++ 2 files changed, 11 insertions(+) (limited to 'tests/float') diff --git a/py/objfloat.c b/py/objfloat.c index 55a9379ff..0831be3fd 100644 --- a/py/objfloat.c +++ b/py/objfloat.c @@ -298,6 +298,13 @@ mp_obj_t mp_obj_float_binary_op(mp_binary_op_t op, mp_float_t lhs_val, mp_obj_t if (lhs_val == 0 && rhs_val < 0) { goto zero_division_error; } + if (lhs_val < 0 && rhs_val != MICROPY_FLOAT_C_FUN(floor)(rhs_val)) { + #if MICROPY_PY_BUILTINS_COMPLEX + return mp_obj_complex_binary_op(MP_BINARY_OP_POWER, lhs_val, 0, rhs_in); + #else + mp_raise_ValueError("complex values not supported"); + #endif + } lhs_val = MICROPY_FLOAT_C_FUN(pow)(lhs_val, rhs_val); break; case MP_BINARY_OP_DIVMOD: { diff --git a/tests/float/complex1.py b/tests/float/complex1.py index 7f0b317b3..854410545 100644 --- a/tests/float/complex1.py +++ b/tests/float/complex1.py @@ -53,6 +53,10 @@ print(type(hash(1j))) # float on lhs should delegate to complex print(1.2 + 3j) +# negative base and fractional power should create a complex +ans = (-1) ** 2.3; print("%.5g %.5g" % (ans.real, ans.imag)) +ans = (-1.2) ** -3.4; print("%.5g %.5g" % (ans.real, ans.imag)) + # check printing of inf/nan print(float('nan') * 1j) print(float('inf') * (1 + 1j)) -- cgit v1.2.3