@@ -143,14 +143,16 @@ mp_obj_t mp_obj_float_binary_op(mp_uint_t op, mp_float_t lhs_val, mp_obj_t rhs_i
143143 case MP_BINARY_OP_INPLACE_SUBTRACT : lhs_val -= rhs_val ; break ;
144144 case MP_BINARY_OP_MULTIPLY :
145145 case MP_BINARY_OP_INPLACE_MULTIPLY : lhs_val *= rhs_val ; break ;
146- // TODO: verify that C floor matches Python semantics
147146 case MP_BINARY_OP_FLOOR_DIVIDE :
148147 case MP_BINARY_OP_INPLACE_FLOOR_DIVIDE :
149148 if (rhs_val == 0 ) {
150149 zero_division_error :
151- nlr_raise (mp_obj_new_exception_msg (& mp_type_ZeroDivisionError , "float division by zero" ));
150+ nlr_raise (mp_obj_new_exception_msg (& mp_type_ZeroDivisionError , "division by zero" ));
152151 }
153- lhs_val = MICROPY_FLOAT_C_FUN (floor )(lhs_val / rhs_val );
152+ // Python specs require that x == (x//y)*y + (x%y) so we must
153+ // call divmod to compute the correct floor division, which
154+ // returns the floor divide in lhs_val.
155+ mp_obj_float_divmod (& lhs_val , & rhs_val );
154156 break ;
155157 case MP_BINARY_OP_TRUE_DIVIDE :
156158 case MP_BINARY_OP_INPLACE_TRUE_DIVIDE :
@@ -159,6 +161,21 @@ mp_obj_t mp_obj_float_binary_op(mp_uint_t op, mp_float_t lhs_val, mp_obj_t rhs_i
159161 }
160162 lhs_val /= rhs_val ;
161163 break ;
164+ case MP_BINARY_OP_MODULO :
165+ case MP_BINARY_OP_INPLACE_MODULO :
166+ if (rhs_val == 0 ) {
167+ goto zero_division_error ;
168+ }
169+ lhs_val = MICROPY_FLOAT_C_FUN (fmod )(lhs_val , rhs_val );
170+ // Python specs require that mod has same sign as second operand
171+ if (lhs_val == 0.0 ) {
172+ lhs_val = MICROPY_FLOAT_C_FUN (copysign )(0.0 , rhs_val );
173+ } else {
174+ if ((lhs_val < 0.0 ) != (rhs_val < 0.0 )) {
175+ lhs_val += rhs_val ;
176+ }
177+ }
178+ break ;
162179 case MP_BINARY_OP_POWER :
163180 case MP_BINARY_OP_INPLACE_POWER : lhs_val = MICROPY_FLOAT_C_FUN (pow )(lhs_val , rhs_val ); break ;
164181 case MP_BINARY_OP_LESS : return MP_BOOL (lhs_val < rhs_val );
@@ -173,4 +190,39 @@ mp_obj_t mp_obj_float_binary_op(mp_uint_t op, mp_float_t lhs_val, mp_obj_t rhs_i
173190 return mp_obj_new_float (lhs_val );
174191}
175192
193+ void mp_obj_float_divmod (mp_float_t * x , mp_float_t * y ) {
194+ // logic here follows that of CPython
195+ // https://docs.python.org/3/reference/expressions.html#binary-arithmetic-operations
196+ // x == (x//y)*y + (x%y)
197+ // divmod(x, y) == (x//y, x%y)
198+ mp_float_t mod = MICROPY_FLOAT_C_FUN (fmod )(* x , * y );
199+ mp_float_t div = (* x - mod ) / * y ;
200+
201+ // Python specs require that mod has same sign as second operand
202+ if (mod == 0.0 ) {
203+ mod = MICROPY_FLOAT_C_FUN (copysign )(0.0 , * y );
204+ } else {
205+ if ((mod < 0.0 ) != (* y < 0.0 )) {
206+ mod += * y ;
207+ div -= 1.0 ;
208+ }
209+ }
210+
211+ mp_float_t floordiv ;
212+ if (div == 0.0 ) {
213+ // if division is zero, take the correct sign of zero
214+ floordiv = MICROPY_FLOAT_C_FUN (copysign )(0.0 , * x / * y );
215+ } else {
216+ // Python specs require that x == (x//y)*y + (x%y)
217+ floordiv = MICROPY_FLOAT_C_FUN (floor )(div );
218+ if (div - floordiv > 0.5 ) {
219+ floordiv += 1.0 ;
220+ }
221+ }
222+
223+ // return results
224+ * x = floordiv ;
225+ * y = mod ;
226+ }
227+
176228#endif // MICROPY_PY_BUILTINS_FLOAT
0 commit comments