Numworks Epsilon  1.4.1
Graphing Calculator Operating System
objfloat.c
Go to the documentation of this file.
1 /*
2  * This file is part of the MicroPython project, http://micropython.org/
3  *
4  * The MIT License (MIT)
5  *
6  * Copyright (c) 2013, 2014 Damien P. George
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining a copy
9  * of this software and associated documentation files (the "Software"), to deal
10  * in the Software without restriction, including without limitation the rights
11  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12  * copies of the Software, and to permit persons to whom the Software is
13  * furnished to do so, subject to the following conditions:
14  *
15  * The above copyright notice and this permission notice shall be included in
16  * all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24  * THE SOFTWARE.
25  */
26 
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <assert.h>
31 
32 #include "py/parsenum.h"
33 #include "py/runtime.h"
34 
35 #if MICROPY_PY_BUILTINS_FLOAT
36 
37 #include <math.h>
38 #include "py/formatfloat.h"
39 
40 #if MICROPY_OBJ_REPR != MICROPY_OBJ_REPR_C && MICROPY_OBJ_REPR != MICROPY_OBJ_REPR_D
41 
42 // M_E and M_PI are not part of the math.h standard and may not be defined
43 #ifndef M_E
44 #define M_E (2.7182818284590452354)
45 #endif
46 #ifndef M_PI
47 #define M_PI (3.14159265358979323846)
48 #endif
49 
50 typedef struct _mp_obj_float_t {
51  mp_obj_base_t base;
52  mp_float_t value;
53 } mp_obj_float_t;
54 
55 const mp_obj_float_t mp_const_float_e_obj = {{&mp_type_float}, M_E};
56 const mp_obj_float_t mp_const_float_pi_obj = {{&mp_type_float}, M_PI};
57 
58 #endif
59 
60 #if MICROPY_FLOAT_HIGH_QUALITY_HASH
61 // must return actual integer value if it fits in mp_int_t
62 mp_int_t mp_float_hash(mp_float_t src) {
63 #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE
64 typedef uint64_t mp_float_uint_t;
65 #elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
66 typedef uint32_t mp_float_uint_t;
67 #endif
68  union {
69  mp_float_t f;
70  #if MP_ENDIANNESS_LITTLE
71  struct { mp_float_uint_t frc:MP_FLOAT_FRAC_BITS, exp:MP_FLOAT_EXP_BITS, sgn:1; } p;
72  #else
73  struct { mp_float_uint_t sgn:1, exp:MP_FLOAT_EXP_BITS, frc:MP_FLOAT_FRAC_BITS; } p;
74  #endif
75  mp_float_uint_t i;
76  } u = {.f = src};
77 
78  mp_int_t val;
79  const int adj_exp = (int)u.p.exp - MP_FLOAT_EXP_BIAS;
80  if (adj_exp < 0) {
81  // value < 1; must be sure to handle 0.0 correctly (ie return 0)
82  val = u.i;
83  } else {
84  // if adj_exp is max then: u.p.frc==0 indicates inf, else NaN
85  // else: 1 <= value
86  mp_float_uint_t frc = u.p.frc | ((mp_float_uint_t)1 << MP_FLOAT_FRAC_BITS);
87 
88  if (adj_exp <= MP_FLOAT_FRAC_BITS) {
89  // number may have a fraction; xor the integer part with the fractional part
90  val = (frc >> (MP_FLOAT_FRAC_BITS - adj_exp))
91  ^ (frc & ((1 << (MP_FLOAT_FRAC_BITS - adj_exp)) - 1));
92  } else if ((unsigned int)adj_exp < BITS_PER_BYTE * sizeof(mp_int_t) - 1) {
93  // the number is a (big) whole integer and will fit in val's signed-width
94  val = (mp_int_t)frc << (adj_exp - MP_FLOAT_FRAC_BITS);
95  } else {
96  // integer part will overflow val's width so just use what bits we can
97  val = frc;
98  }
99  }
100 
101  if (u.p.sgn) {
102  val = -val;
103  }
104 
105  return val;
106 }
107 #endif
108 
109 STATIC void float_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) {
110  (void)kind;
111  mp_float_t o_val = mp_obj_float_get(o_in);
112 #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
113  char buf[16];
114  #if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C
115  const int precision = 6;
116  #else
117  const int precision = 7;
118  #endif
119 #else
120  char buf[32];
121  const int precision = 16;
122 #endif
123  mp_format_float(o_val, buf, sizeof(buf), 'g', precision, '\0');
124  mp_print_str(print, buf);
125  if (strchr(buf, '.') == NULL && strchr(buf, 'e') == NULL && strchr(buf, 'n') == NULL) {
126  // Python floats always have decimal point (unless inf or nan)
127  mp_print_str(print, ".0");
128  }
129 }
130 
131 STATIC mp_obj_t float_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) {
132  (void)type_in;
133  mp_arg_check_num(n_args, n_kw, 0, 1, false);
134 
135  switch (n_args) {
136  case 0:
137  return mp_obj_new_float(0);
138 
139  case 1:
140  default:
141  if (MP_OBJ_IS_STR(args[0])) {
142  // a string, parse it
143  size_t l;
144  const char *s = mp_obj_str_get_data(args[0], &l);
145  return mp_parse_num_decimal(s, l, false, false, NULL);
146  } else if (mp_obj_is_float(args[0])) {
147  // a float, just return it
148  return args[0];
149  } else {
150  // something else, try to cast it to a float
151  return mp_obj_new_float(mp_obj_get_float(args[0]));
152  }
153  }
154 }
155 
156 STATIC mp_obj_t float_unary_op(mp_unary_op_t op, mp_obj_t o_in) {
157  mp_float_t val = mp_obj_float_get(o_in);
158  switch (op) {
159  case MP_UNARY_OP_BOOL: return mp_obj_new_bool(val != 0);
160  case MP_UNARY_OP_HASH: return MP_OBJ_NEW_SMALL_INT(mp_float_hash(val));
161  case MP_UNARY_OP_POSITIVE: return o_in;
162  case MP_UNARY_OP_NEGATIVE: return mp_obj_new_float(-val);
163  case MP_UNARY_OP_ABS: {
164  // TODO check for NaN etc
165  if (val < 0) {
166  return mp_obj_new_float(-val);
167  } else {
168  return o_in;
169  }
170  }
171  default: return MP_OBJ_NULL; // op not supported
172  }
173 }
174 
175 STATIC mp_obj_t float_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
176  mp_float_t lhs_val = mp_obj_float_get(lhs_in);
177 #if MICROPY_PY_BUILTINS_COMPLEX
178  if (MP_OBJ_IS_TYPE(rhs_in, &mp_type_complex)) {
179  return mp_obj_complex_binary_op(op, lhs_val, 0, rhs_in);
180  } else
181 #endif
182  {
183  return mp_obj_float_binary_op(op, lhs_val, rhs_in);
184  }
185 }
186 
188  { &mp_type_type },
189  .name = MP_QSTR_float,
190  .print = float_print,
191  .make_new = float_make_new,
192  .unary_op = float_unary_op,
193  .binary_op = float_binary_op,
194 };
195 
196 #if MICROPY_OBJ_REPR != MICROPY_OBJ_REPR_C && MICROPY_OBJ_REPR != MICROPY_OBJ_REPR_D
197 
198 mp_obj_t mp_obj_new_float(mp_float_t value) {
199  mp_obj_float_t *o = m_new(mp_obj_float_t, 1);
200  o->base.type = &mp_type_float;
201  o->value = value;
202  return MP_OBJ_FROM_PTR(o);
203 }
204 
205 mp_float_t mp_obj_float_get(mp_obj_t self_in) {
206  assert(mp_obj_is_float(self_in));
207  mp_obj_float_t *self = MP_OBJ_TO_PTR(self_in);
208  return self->value;
209 }
210 
211 #endif
212 
213 STATIC void mp_obj_float_divmod(mp_float_t *x, mp_float_t *y) {
214  // logic here follows that of CPython
215  // https://docs.python.org/3/reference/expressions.html#binary-arithmetic-operations
216  // x == (x//y)*y + (x%y)
217  // divmod(x, y) == (x//y, x%y)
218  mp_float_t mod = MICROPY_FLOAT_C_FUN(fmod)(*x, *y);
219  mp_float_t div = (*x - mod) / *y;
220 
221  // Python specs require that mod has same sign as second operand
222  if (mod == 0.0) {
223  mod = MICROPY_FLOAT_C_FUN(copysign)(0.0, *y);
224  } else {
225  if ((mod < 0.0) != (*y < 0.0)) {
226  mod += *y;
227  div -= 1.0;
228  }
229  }
230 
231  mp_float_t floordiv;
232  if (div == 0.0) {
233  // if division is zero, take the correct sign of zero
234  floordiv = MICROPY_FLOAT_C_FUN(copysign)(0.0, *x / *y);
235  } else {
236  // Python specs require that x == (x//y)*y + (x%y)
237  floordiv = MICROPY_FLOAT_C_FUN(floor)(div);
238  if (div - floordiv > 0.5) {
239  floordiv += 1.0;
240  }
241  }
242 
243  // return results
244  *x = floordiv;
245  *y = mod;
246 }
247 
248 mp_obj_t mp_obj_float_binary_op(mp_binary_op_t op, mp_float_t lhs_val, mp_obj_t rhs_in) {
249  mp_float_t rhs_val;
250  if (!mp_obj_get_float_maybe(rhs_in, &rhs_val)) {
251  return MP_OBJ_NULL; // op not supported
252  }
253 
254  switch (op) {
255  case MP_BINARY_OP_ADD:
256  case MP_BINARY_OP_INPLACE_ADD: lhs_val += rhs_val; break;
258  case MP_BINARY_OP_INPLACE_SUBTRACT: lhs_val -= rhs_val; break;
260  case MP_BINARY_OP_INPLACE_MULTIPLY: lhs_val *= rhs_val; break;
263  if (rhs_val == 0) {
264  zero_division_error:
265  mp_raise_msg(&mp_type_ZeroDivisionError, "division by zero");
266  }
267  // Python specs require that x == (x//y)*y + (x%y) so we must
268  // call divmod to compute the correct floor division, which
269  // returns the floor divide in lhs_val.
270  mp_obj_float_divmod(&lhs_val, &rhs_val);
271  break;
274  if (rhs_val == 0) {
275  goto zero_division_error;
276  }
277  lhs_val /= rhs_val;
278  break;
279  case MP_BINARY_OP_MODULO:
281  if (rhs_val == 0) {
282  goto zero_division_error;
283  }
284  lhs_val = MICROPY_FLOAT_C_FUN(fmod)(lhs_val, rhs_val);
285  // Python specs require that mod has same sign as second operand
286  if (lhs_val == 0.0) {
287  lhs_val = MICROPY_FLOAT_C_FUN(copysign)(0.0, rhs_val);
288  } else {
289  if ((lhs_val < 0.0) != (rhs_val < 0.0)) {
290  lhs_val += rhs_val;
291  }
292  }
293  break;
294  case MP_BINARY_OP_POWER:
296  if (lhs_val == 0 && rhs_val < 0) {
297  goto zero_division_error;
298  }
299  if (lhs_val < 0 && rhs_val != MICROPY_FLOAT_C_FUN(floor)(rhs_val)) {
300  #if MICROPY_PY_BUILTINS_COMPLEX
301  return mp_obj_complex_binary_op(MP_BINARY_OP_POWER, lhs_val, 0, rhs_in);
302  #else
303  mp_raise_ValueError("complex values not supported");
304  #endif
305  }
306  lhs_val = MICROPY_FLOAT_C_FUN(pow)(lhs_val, rhs_val);
307  break;
308  case MP_BINARY_OP_DIVMOD: {
309  if (rhs_val == 0) {
310  goto zero_division_error;
311  }
312  mp_obj_float_divmod(&lhs_val, &rhs_val);
313  mp_obj_t tuple[2] = {
314  mp_obj_new_float(lhs_val),
315  mp_obj_new_float(rhs_val),
316  };
317  return mp_obj_new_tuple(2, tuple);
318  }
319  case MP_BINARY_OP_LESS: return mp_obj_new_bool(lhs_val < rhs_val);
320  case MP_BINARY_OP_MORE: return mp_obj_new_bool(lhs_val > rhs_val);
321  case MP_BINARY_OP_EQUAL: return mp_obj_new_bool(lhs_val == rhs_val);
322  case MP_BINARY_OP_LESS_EQUAL: return mp_obj_new_bool(lhs_val <= rhs_val);
323  case MP_BINARY_OP_MORE_EQUAL: return mp_obj_new_bool(lhs_val >= rhs_val);
324 
325  default:
326  return MP_OBJ_NULL; // op not supported
327  }
328  return mp_obj_new_float(lhs_val);
329 }
330 
331 #endif // MICROPY_PY_BUILTINS_FLOAT
#define exp(x)
Definition: math.h:176
intptr_t mp_int_t
Definition: mpconfigport.h:73
NORETURN void mp_raise_msg(const mp_obj_type_t *exc_type, const char *msg)
Definition: runtime.c:1448
#define copysign(x, y)
Definition: math.h:171
mp_obj_t mp_obj_new_tuple(size_t n, const mp_obj_t *items)
Definition: objtuple.c:235
const mp_obj_type_t mp_type_float
#define assert(e)
Definition: assert.h:9
const mp_obj_type_t mp_type_ZeroDivisionError
#define MP_OBJ_IS_TYPE(o, t)
Definition: obj.h:254
#define M_PI
Definition: math.h:17
int mp_print_str(const mp_print_t *print, const char *str)
Definition: mpprint.c:53
#define MP_OBJ_FROM_PTR(p)
Definition: obj.h:233
void mp_arg_check_num(size_t n_args, size_t n_kw, size_t n_args_min, size_t n_args_max, bool takes_kw)
Definition: argcheck.c:32
mp_unary_op_t
Definition: runtime0.h:45
#define STATIC
Definition: mpconfig.h:1178
#define mp_obj_is_float(o)
Definition: obj.h:745
const mp_obj_type_t mp_type_complex
mp_print_kind_t
Definition: obj.h:412
#define MP_OBJ_NEW_SMALL_INT(small_int)
Definition: obj.h:87
#define M_E
Definition: math.h:12
const char * mp_obj_str_get_data(mp_obj_t self_in, size_t *len)
Definition: objstr.c:2105
unsigned int uint32_t
Definition: stdint.h:6
#define NULL
Definition: stddef.h:4
#define fmod(x, y)
Definition: math.h:180
#define MP_OBJ_NULL
Definition: obj.h:73
char * strchr(const char *s, int c)
Definition: strchr.c:3
unsigned long long uint64_t
Definition: stdint.h:7
#define pow(x, y)
Definition: math.h:190
mp_binary_op_t
Definition: runtime0.h:67
args
Definition: i18n.py:175
const mp_obj_type_t mp_type_type
Definition: objtype.c:969
mp_obj_t mp_parse_num_decimal(const char *str, size_t len, bool allow_imag, bool force_complex, mp_lexer_t *lex)
Definition: parsenum.c:171
NORETURN void mp_raise_ValueError(const char *msg)
Definition: runtime.c:1456
#define MP_OBJ_TO_PTR(o)
Definition: obj.h:228
qstr name
Definition: obj.h:478
#define BITS_PER_BYTE
Definition: mpconfig.h:1186
uint64_t mp_obj_t
Definition: obj.h:39
#define floor(x)
Definition: math.h:179
#define MP_OBJ_IS_STR(o)
Definition: obj.h:256
#define m_new(type, num)
Definition: misc.h:57