Skip to content

Commit 6e0b6d0

Browse files
doogledpgeorge
authored andcommitted
py: Fix float to int conversion for large exponents.
1 parent ffc96a9 commit 6e0b6d0

4 files changed

Lines changed: 109 additions & 7 deletions

File tree

py/mpz.c

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -590,6 +590,14 @@ mpz_t *mpz_from_ll(long long val, bool is_signed) {
590590
return z;
591591
}
592592

593+
#if MICROPY_PY_BUILTINS_FLOAT
594+
mpz_t *mpz_from_float(mp_float_t val) {
595+
mpz_t *z = mpz_zero();
596+
mpz_set_from_float(z, val);
597+
return z;
598+
}
599+
#endif
600+
593601
mpz_t *mpz_from_str(const char *str, mp_uint_t len, bool neg, mp_uint_t base) {
594602
mpz_t *z = mpz_zero();
595603
mpz_set_from_str(z, str, len, neg, base);
@@ -682,6 +690,80 @@ void mpz_set_from_ll(mpz_t *z, long long val, bool is_signed) {
682690
}
683691
}
684692

693+
#if MICROPY_PY_BUILTINS_FLOAT
694+
void mpz_set_from_float(mpz_t *z, mp_float_t src) {
695+
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE
696+
#define EXP_SZ 11
697+
#define FRC_SZ 52
698+
typedef uint64_t mp_float_int_t;
699+
#else
700+
#define EXP_SZ 8
701+
#define FRC_SZ 23
702+
typedef uint32_t mp_float_int_t;
703+
#endif
704+
union {
705+
mp_float_t f;
706+
struct { mp_float_int_t frc:FRC_SZ, exp:EXP_SZ, sgn:1; } p;
707+
} u = {src};
708+
709+
z->neg = u.p.sgn;
710+
if (u.p.exp == 0) {
711+
// value == 0 || value < 1
712+
mpz_init_zero(z);
713+
} else if (u.p.exp == ((1 << EXP_SZ) - 1)) {
714+
// inf or NaN
715+
#if 0
716+
// TODO: this probably isn't the right place to throw an exception
717+
if(u.p.frc == 0)
718+
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OverflowError, "cannot convert float infinity to integer"));
719+
else
720+
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "cannot convert float NaN to integer"));
721+
#else
722+
mpz_init_zero(z);
723+
#endif
724+
} else {
725+
const int adj_exp = (int)u.p.exp - ((1 << (EXP_SZ - 1)) - 1);
726+
if (adj_exp < 0) {
727+
// value < 1 , truncates to 0
728+
mpz_init_zero(z);
729+
} else if (adj_exp == 0) {
730+
// 1 <= value < 2 , so truncates to 1
731+
mpz_init_from_int(z, 1);
732+
} else {
733+
// 2 <= value
734+
const int dig_cnt = (adj_exp + 1 + (DIG_SIZE - 1)) / DIG_SIZE;
735+
const unsigned int rem = adj_exp % DIG_SIZE;
736+
int dig_ind, shft;
737+
mp_float_int_t frc = u.p.frc | ((mp_float_int_t)1 << FRC_SZ);
738+
739+
if (adj_exp < FRC_SZ) {
740+
shft = 0;
741+
dig_ind = 0;
742+
frc >>= FRC_SZ - adj_exp;
743+
} else {
744+
shft = (rem - FRC_SZ) % DIG_SIZE;
745+
dig_ind = (adj_exp - FRC_SZ) / DIG_SIZE;
746+
}
747+
mpz_need_dig(z, dig_cnt);
748+
z->len = dig_cnt;
749+
if (dig_ind != 0) {
750+
memset(z->dig, 0, dig_ind * sizeof(mpz_dig_t));
751+
}
752+
if (shft != 0) {
753+
z->dig[dig_ind++] = (frc << shft) & DIG_MASK;
754+
frc >>= DIG_SIZE - shft;
755+
}
756+
while (dig_ind != dig_cnt) {
757+
z->dig[dig_ind++] = frc & DIG_MASK;
758+
frc >>= DIG_SIZE;
759+
}
760+
}
761+
}
762+
}
763+
#undef EXP_SZ
764+
#undef FRC_SZ
765+
#endif
766+
685767
// returns number of bytes from str that were processed
686768
mp_uint_t mpz_set_from_str(mpz_t *z, const char *str, mp_uint_t len, bool neg, mp_uint_t base) {
687769
assert(base < 36);

py/mpz.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ void mpz_deinit(mpz_t *z);
8585
mpz_t *mpz_zero(void);
8686
mpz_t *mpz_from_int(mp_int_t i);
8787
mpz_t *mpz_from_ll(long long i, bool is_signed);
88+
#if MICROPY_PY_BUILTINS_FLOAT
89+
mpz_t *mpz_from_float(mp_float_t i);
90+
#endif
8891
mpz_t *mpz_from_str(const char *str, mp_uint_t len, bool neg, mp_uint_t base);
8992
void mpz_free(mpz_t *z);
9093

@@ -93,6 +96,9 @@ mpz_t *mpz_clone(const mpz_t *src);
9396
void mpz_set(mpz_t *dest, const mpz_t *src);
9497
void mpz_set_from_int(mpz_t *z, mp_int_t src);
9598
void mpz_set_from_ll(mpz_t *z, long long i, bool is_signed);
99+
#if MICROPY_PY_BUILTINS_FLOAT
100+
void mpz_set_from_float(mpz_t *z, mp_float_t src);
101+
#endif
96102
mp_uint_t mpz_set_from_str(mpz_t *z, const char *str, mp_uint_t len, bool neg, mp_uint_t base);
97103

98104
bool mpz_is_zero(const mpz_t *z);

py/objint_mpz.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -298,9 +298,9 @@ mp_obj_t mp_obj_new_int_from_uint(mp_uint_t value) {
298298

299299
#if MICROPY_PY_BUILTINS_FLOAT
300300
mp_obj_t mp_obj_new_int_from_float(mp_float_t val) {
301-
// TODO: This doesn't handle numbers with large exponent
302-
long long i = MICROPY_FLOAT_C_FUN(trunc)(val);
303-
return mp_obj_new_int_from_ll(i);
301+
mp_obj_int_t *o = mp_obj_int_new_mpz();
302+
mpz_set_from_float(&o->mpz, val);
303+
return o;
304304
}
305305
#endif
306306

tests/float/float2int.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,24 @@
11
# This case occurs with time.time() values
22
print(int(1418774543.))
33

4-
# TODO: General case with large exponent
5-
#print(int(2.**100))
4+
print(int(2.**100))
65

76
print("%d" % 1418774543.)
87

9-
# TODO: General case with large exponent
10-
#print("%d" % 2.**100)
8+
print("%d" % 2.**100)
9+
10+
testpass = True
11+
for i in range(0,1024):
12+
bitcnt = len(bin(int(2.**i))) - 3;
13+
if i != bitcnt:
14+
print('fail: 2**%u was %u bits long' % (i, bitcnt));
15+
testpass = False
16+
print("power of 2 test: %s" % (testpass and 'passed' or 'failed'))
17+
18+
testpass = True
19+
for i in range(0,23):
20+
digcnt = len(str(int(10.**i))) - 1;
21+
if i != digcnt:
22+
print('fail: 10**%u was %u digits long' % (i, digcnt));
23+
testpass = False
24+
print("power of 10 test: %s" % (testpass and 'passed' or 'failed'))

0 commit comments

Comments
 (0)