diff --git a/mpy-cross/mpconfigport.h b/mpy-cross/mpconfigport.h index 94a598c9954b8..81cbfc2eeb937 100644 --- a/mpy-cross/mpconfigport.h +++ b/mpy-cross/mpconfigport.h @@ -55,6 +55,7 @@ #define MICROPY_COMP_CONST_FOLDING (1) #define MICROPY_COMP_MODULE_CONST (1) #define MICROPY_COMP_CONST (1) +#define MICROPY_COMP_CONST_FLOAT (1) #define MICROPY_COMP_DOUBLE_TUPLE_ASSIGN (1) #define MICROPY_COMP_TRIPLE_TUPLE_ASSIGN (1) #define MICROPY_COMP_RETURN_IF_EXPR (1) @@ -88,7 +89,8 @@ #define MICROPY_PY_ARRAY (0) #define MICROPY_PY_ATTRTUPLE (0) #define MICROPY_PY_COLLECTIONS (0) -#define MICROPY_PY_MATH (0) +#define MICROPY_PY_MATH (MICROPY_COMP_CONST_FLOAT) +#define MICROPY_PY_MATH_CONSTANTS (MICROPY_COMP_CONST_FLOAT) #define MICROPY_PY_CMATH (0) #define MICROPY_PY_GC (0) #define MICROPY_PY_IO (0) diff --git a/py/builtin.h b/py/builtin.h index 6efe3e8facabd..388bc8470053d 100644 --- a/py/builtin.h +++ b/py/builtin.h @@ -138,6 +138,7 @@ extern const mp_obj_module_t mp_module_sys; extern const mp_obj_module_t mp_module_errno; extern const mp_obj_module_t mp_module_uctypes; extern const mp_obj_module_t mp_module_machine; +extern const mp_obj_module_t mp_module_math; extern const char MICROPY_PY_BUILTINS_HELP_TEXT[]; diff --git a/py/emitcommon.c b/py/emitcommon.c index a9eb6e2021fc8..1f701db80a0a9 100644 --- a/py/emitcommon.c +++ b/py/emitcommon.c @@ -25,6 +25,7 @@ */ #include +#include #include "py/emit.h" #include "py/nativeglue.h" @@ -72,7 +73,21 @@ static bool strictly_equal(mp_obj_t a, mp_obj_t b) { } return true; } else { - return mp_obj_equal(a, b); + if (!mp_obj_equal(a, b)) { + return false; + } + #if MICROPY_PY_BUILTINS_FLOAT && MICROPY_COMP_CONST_FLOAT + if (a_type == &mp_type_float) { + mp_float_t a_val = mp_obj_float_get(a); + if (a_val == (mp_float_t)0.0) { + // Although 0.0 == -0.0, they are not strictly_equal and + // must be stored as two different constants in .mpy files + mp_float_t b_val = mp_obj_float_get(b); + return signbit(a_val) == signbit(b_val); + } + } + #endif + return true; } } diff --git a/py/mpconfig.h b/py/mpconfig.h index 01712bd5b4d90..f42776ca664e1 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -485,6 +485,13 @@ #define MICROPY_COMP_CONST (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) #endif +// Whether to enable float constant folding like 1.2+3.4 (when MICROPY_COMP_CONST_FOLDING is also enabled) +// and constant optimisation like id = const(1.2) (when MICROPY_COMP_CONST is also enabled) +// and constant lookup like math.inf (when MICROPY_COMP_MODULE_CONST is also enabled) +#ifndef MICROPY_COMP_CONST_FLOAT +#define MICROPY_COMP_CONST_FLOAT (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) +#endif + // Whether to enable optimisation of: a, b = c, d // Costs 124 bytes (Thumb2) #ifndef MICROPY_COMP_DOUBLE_TUPLE_ASSIGN diff --git a/py/objfloat.c b/py/objfloat.c index 0728fce3151fe..7241566a4c30c 100644 --- a/py/objfloat.c +++ b/py/objfloat.c @@ -124,6 +124,20 @@ static void float_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t const int precision = 16; #endif mp_format_float(o_val, buf, sizeof(buf), 'g', precision, '\0'); + #if MICROPY_COMP_CONST_FLOAT + if (kind == PRINT_REPR) { + // See if we can add an extra (partial) digit for precision. + // This is useful in particular for saving floats to .mpy files, + // but also for general `repr` support. + // If the length increases by more than one digit, it means we are + // digging too far (eg. 1.234499999999), so we keep the short form + char ebuf[32]; + mp_format_float(o_val, ebuf, sizeof(ebuf), 'g', precision + 1, '\0'); + if (strlen(ebuf) == strlen(buf) + 1) { + memcpy(buf, ebuf, sizeof(buf)); + } + } + #endif mp_print_str(print, buf); if (strchr(buf, '.') == NULL && strchr(buf, 'e') == NULL && strchr(buf, 'n') == NULL) { // Python floats always have decimal point (unless inf or nan) diff --git a/py/parse.c b/py/parse.c index db89fb58450e3..91eea3e368907 100644 --- a/py/parse.c +++ b/py/parse.c @@ -336,18 +336,34 @@ static uint8_t peek_rule(parser_t *parser, size_t n) { } #endif -bool mp_parse_node_get_int_maybe(mp_parse_node_t pn, mp_obj_t *o) { +#if MICROPY_COMP_CONST_FOLDING || MICROPY_EMIT_INLINE_ASM +static bool mp_parse_node_get_number_maybe(mp_parse_node_t pn, mp_obj_t *o) { if (MP_PARSE_NODE_IS_SMALL_INT(pn)) { *o = MP_OBJ_NEW_SMALL_INT(MP_PARSE_NODE_LEAF_SMALL_INT(pn)); return true; } else if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, RULE_const_object)) { mp_parse_node_struct_t *pns = (mp_parse_node_struct_t *)pn; *o = mp_parse_node_extract_const_object(pns); - return mp_obj_is_int(*o); + return mp_obj_is_int(*o) + #if MICROPY_COMP_CONST_FLOAT + || mp_obj_is_float(*o) + #endif + ; } else { return false; } } +#endif + +#if MICROPY_EMIT_INLINE_ASM +bool mp_parse_node_get_int_maybe(mp_parse_node_t pn, mp_obj_t *o) { + return mp_parse_node_get_number_maybe(pn, o) + #if MICROPY_COMP_CONST_FLOAT + && mp_obj_is_int(*o) + #endif + ; +} +#endif #if MICROPY_COMP_CONST_TUPLE || MICROPY_COMP_CONST static bool mp_parse_node_is_const(mp_parse_node_t pn) { @@ -642,12 +658,32 @@ static const mp_rom_map_elem_t mp_constants_table[] = { #if MICROPY_PY_UCTYPES { MP_ROM_QSTR(MP_QSTR_uctypes), MP_ROM_PTR(&mp_module_uctypes) }, #endif + #if MICROPY_PY_BUILTINS_FLOAT && MICROPY_PY_MATH && MICROPY_COMP_CONST_FLOAT + { MP_ROM_QSTR(MP_QSTR_math), MP_ROM_PTR(&mp_module_math) }, + #endif // Extra constants as defined by a port MICROPY_PORT_CONSTANTS }; static MP_DEFINE_CONST_MAP(mp_constants_map, mp_constants_table); #endif +static bool binary_op_maybe(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs, mp_obj_t *res) { + nlr_buf_t nlr; + if (nlr_push(&nlr) == 0) { + mp_obj_t tmp = mp_binary_op(op, lhs, rhs); + #if MICROPY_PY_BUILTINS_COMPLEX + if (mp_obj_is_type(tmp, &mp_type_complex)) { + return false; + } + #endif + *res = tmp; + nlr_pop(); + return true; + } else { + return false; + } +} + static bool fold_logical_constants(parser_t *parser, uint8_t rule_id, size_t *num_args) { if (rule_id == RULE_or_test || rule_id == RULE_and_test) { @@ -706,7 +742,7 @@ static bool fold_logical_constants(parser_t *parser, uint8_t rule_id, size_t *nu } static bool fold_constants(parser_t *parser, uint8_t rule_id, size_t num_args) { - // this code does folding of arbitrary integer expressions, eg 1 + 2 * 3 + 4 + // this code does folding of arbitrary numeric expressions, eg 1 + 2 * 3 + 4 // it does not do partial folding, eg 1 + 2 + x -> 3 + x mp_obj_t arg0; @@ -716,7 +752,7 @@ static bool fold_constants(parser_t *parser, uint8_t rule_id, size_t num_args) { || rule_id == RULE_power) { // folding for binary ops: | ^ & ** mp_parse_node_t pn = peek_result(parser, num_args - 1); - if (!mp_parse_node_get_int_maybe(pn, &arg0)) { + if (!mp_parse_node_get_number_maybe(pn, &arg0)) { return false; } mp_binary_op_t op; @@ -732,58 +768,61 @@ static bool fold_constants(parser_t *parser, uint8_t rule_id, size_t num_args) { for (ssize_t i = num_args - 2; i >= 0; --i) { pn = peek_result(parser, i); mp_obj_t arg1; - if (!mp_parse_node_get_int_maybe(pn, &arg1)) { + if (!mp_parse_node_get_number_maybe(pn, &arg1)) { return false; } + #if !MICROPY_COMP_CONST_FLOAT if (op == MP_BINARY_OP_POWER && mp_obj_int_sign(arg1) < 0) { // ** can't have negative rhs return false; } - arg0 = mp_binary_op(op, arg0, arg1); + #endif + if (!binary_op_maybe(op, arg0, arg1, &arg0)) { + return false; + } } } else if (rule_id == RULE_shift_expr || rule_id == RULE_arith_expr || rule_id == RULE_term) { // folding for binary ops: << >> + - * @ / % // mp_parse_node_t pn = peek_result(parser, num_args - 1); - if (!mp_parse_node_get_int_maybe(pn, &arg0)) { + if (!mp_parse_node_get_number_maybe(pn, &arg0)) { return false; } for (ssize_t i = num_args - 2; i >= 1; i -= 2) { pn = peek_result(parser, i - 1); mp_obj_t arg1; - if (!mp_parse_node_get_int_maybe(pn, &arg1)) { + if (!mp_parse_node_get_number_maybe(pn, &arg1)) { return false; } mp_token_kind_t tok = MP_PARSE_NODE_LEAF_ARG(peek_result(parser, i)); - if (tok == MP_TOKEN_OP_AT || tok == MP_TOKEN_OP_SLASH) { - // Can't fold @ or / + if (tok == MP_TOKEN_OP_AT) { + // Can't fold @ + return false; + } + #if !MICROPY_COMP_CONST_FLOAT + if (tok == MP_TOKEN_OP_SLASH) { + // Can't fold / return false; } + #endif mp_binary_op_t op = MP_BINARY_OP_LSHIFT + (tok - MP_TOKEN_OP_DBL_LESS); - int rhs_sign = mp_obj_int_sign(arg1); - if (op <= MP_BINARY_OP_RSHIFT) { - // << and >> can't have negative rhs - if (rhs_sign < 0) { - return false; - } - } else if (op >= MP_BINARY_OP_FLOOR_DIVIDE) { - // % and // can't have zero rhs - if (rhs_sign == 0) { - return false; - } + if (!binary_op_maybe(op, arg0, arg1, &arg0)) { + return false; } - arg0 = mp_binary_op(op, arg0, arg1); } } else if (rule_id == RULE_factor_2) { // folding for unary ops: + - ~ mp_parse_node_t pn = peek_result(parser, 0); - if (!mp_parse_node_get_int_maybe(pn, &arg0)) { + if (!mp_parse_node_get_number_maybe(pn, &arg0)) { return false; } mp_token_kind_t tok = MP_PARSE_NODE_LEAF_ARG(peek_result(parser, 1)); mp_unary_op_t op; if (tok == MP_TOKEN_OP_TILDE) { + if (!mp_obj_is_int(arg0)) { + return false; + } op = MP_UNARY_OP_INVERT; } else { assert(tok == MP_TOKEN_OP_PLUS || tok == MP_TOKEN_OP_MINUS); // should be @@ -855,7 +894,7 @@ static bool fold_constants(parser_t *parser, uint8_t rule_id, size_t num_args) { return false; } // id1.id2 - // look it up in constant table, see if it can be replaced with an integer + // look it up in constant table, see if it can be replaced with an integer or a float mp_parse_node_struct_t *pns1 = (mp_parse_node_struct_t *)pn1; assert(MP_PARSE_NODE_IS_ID(pns1->nodes[0])); qstr q_base = MP_PARSE_NODE_LEAF_ARG(pn0); @@ -866,7 +905,7 @@ static bool fold_constants(parser_t *parser, uint8_t rule_id, size_t num_args) { } mp_obj_t dest[2]; mp_load_method_maybe(elem->value, q_attr, dest); - if (!(dest[0] != MP_OBJ_NULL && mp_obj_is_int(dest[0]) && dest[1] == MP_OBJ_NULL)) { + if (!(dest[0] != MP_OBJ_NULL && (mp_obj_is_int(dest[0]) || mp_obj_is_float(dest[0])) && dest[1] == MP_OBJ_NULL)) { return false; } arg0 = dest[0]; diff --git a/tests/float/float_parse_doubleprec.py b/tests/float/float_parse_doubleprec.py index 81fcadcee88b4..35b9cbc516eae 100644 --- a/tests/float/float_parse_doubleprec.py +++ b/tests/float/float_parse_doubleprec.py @@ -19,3 +19,9 @@ print(float("1.00000000000000000000e-307")) print(float("10.0000000000000000000e-308")) print(float("100.000000000000000000e-309")) + +# ensure repr() adds an extra digit when needed for accurate parsing +print(float(repr(float('2.0') ** 100)) == float('2.0') ** 100) + +# ensure repr does not add meaningless extra digits (1.234999999999) +print(repr(1.2345)) diff --git a/tests/micropython/const_error.py b/tests/micropython/const_error.py index d35be530a7c8d..950360e4dc782 100644 --- a/tests/micropython/const_error.py +++ b/tests/micropython/const_error.py @@ -18,8 +18,6 @@ def test_syntax(code): # these operations are not supported within const test_syntax("A = const(1 @ 2)") -test_syntax("A = const(1 / 2)") -test_syntax("A = const(1 ** -2)") test_syntax("A = const(1 << -2)") test_syntax("A = const(1 >> -2)") test_syntax("A = const(1 % 0)") diff --git a/tests/micropython/const_error.py.exp b/tests/micropython/const_error.py.exp index 3edc3efe9c3e9..bef69eb32ea7d 100644 --- a/tests/micropython/const_error.py.exp +++ b/tests/micropython/const_error.py.exp @@ -5,5 +5,3 @@ SyntaxError SyntaxError SyntaxError SyntaxError -SyntaxError -SyntaxError diff --git a/tests/micropython/const_float.py b/tests/micropython/const_float.py new file mode 100644 index 0000000000000..dd3cdf94444d7 --- /dev/null +++ b/tests/micropython/const_float.py @@ -0,0 +1,28 @@ +# test constant optimisation, with consts that are floats +try: + float("3.14") +except NameError: + print("SKIP") + raise SystemExit + +from micropython import const + +# check we can make consts from floats +F1 = const(2.5) +F2 = const(-0.3) +print(type(F1), F1) +print(type(F2), F2) + +# check arithmetic with floats +F3 = const(F1 + F2) +F4 = const(F1**2) +print(F3, F4) + +# check int operations with float results +F5 = const(1 / 2) +F6 = const(2**-2) +print(F5, F6) + +# note: we also test float expression folding when +# we're compiling test cases in tests/float, as +# many expressions are resolved at compile time. diff --git a/tests/micropython/const_float.py.exp b/tests/micropython/const_float.py.exp new file mode 100644 index 0000000000000..17a86a6d936c2 --- /dev/null +++ b/tests/micropython/const_float.py.exp @@ -0,0 +1,4 @@ + 2.5 + -0.3 +2.2 6.25 +0.5 0.25 diff --git a/tests/micropython/const_math.py b/tests/micropython/const_math.py new file mode 100644 index 0000000000000..62732ffc95ef2 --- /dev/null +++ b/tests/micropython/const_math.py @@ -0,0 +1,12 @@ +# Test expressions based on math module constants +try: + import math +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit + +from micropython import const + +# check that we can make consts from math constants +two_pi = const(2.0 * math.pi) +print(math.cos(two_pi)) diff --git a/tests/micropython/const_math.py.exp b/tests/micropython/const_math.py.exp new file mode 100644 index 0000000000000..d3827e75a5cad --- /dev/null +++ b/tests/micropython/const_math.py.exp @@ -0,0 +1 @@ +1.0 pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy