Implement float.as_integer_ratio
This commit is contained in:
parent
b9332b2578
commit
1fadea3027
@ -2998,6 +2998,90 @@ KrkValue krk_parse_float(const char * s, size_t l) {
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert a double to a tuple of two longs.
|
||||
*
|
||||
* Turns a floating-point value into a tuple representing a
|
||||
* ratio of two integers that is equivalent.
|
||||
*
|
||||
* @param d Value to convert.
|
||||
* @returns tuple(numerator,denominator)
|
||||
*/
|
||||
KrkValue krk_float_to_fraction(double d) {
|
||||
uint64_t x = ((union Float { double d; uint64_t i; }){.d=d}).i;
|
||||
|
||||
uint64_t m = x & 0x000fffffffffffffULL;
|
||||
uint64_t e = ((x >> 52) & 0x7FF);
|
||||
|
||||
if (e) {
|
||||
/* If not subnormal, include hidden bit 53 */
|
||||
m |= (1ULL << 52);
|
||||
} else if (m) {
|
||||
/* If subnormal and not zero, increase e to correct value */
|
||||
e++;
|
||||
}
|
||||
|
||||
krk_long a, b;
|
||||
|
||||
/* NaN or Inf */
|
||||
if (e == 0x7FF) return krk_runtimeError(vm.exceptions->valueError, "unrepresentable");
|
||||
if (e == 0) {
|
||||
/* Python doesn't set a sign to represent this, so we won't either. */
|
||||
krk_long_init_ui(a, 0);
|
||||
krk_long_init_ui(b, 1);
|
||||
goto _finish;
|
||||
}
|
||||
|
||||
krk_long_init_ui(a, m);
|
||||
krk_long_init_ui(b, (1ULL << 52));
|
||||
|
||||
/* Generate the numerator and denominator of the complete fraction */
|
||||
if (e > 0x3FF) {
|
||||
krk_long tmp;
|
||||
krk_long_init_ui(tmp, 0);
|
||||
_krk_long_lshift_z(tmp,a,e-0x3FF);
|
||||
krk_long_clear(a);
|
||||
memcpy(&a,&tmp,sizeof(krk_long));
|
||||
} else if (e < 0x3FF) {
|
||||
krk_long tmp;
|
||||
krk_long_init_ui(tmp, 0);
|
||||
_krk_long_lshift_z(tmp,b,0x3FF-e);
|
||||
krk_long_clear(b);
|
||||
memcpy(&b,&tmp,sizeof(krk_long));
|
||||
}
|
||||
|
||||
/* Slowly reduce the fraction to something reasonable;
|
||||
* given that one or the other of the top or bottom is
|
||||
* unshifted, we should be doing this at most ~50 times
|
||||
* so it doesn't really matter much; we _could_ count
|
||||
* the common trailing zero bits first and do one shift... */
|
||||
while (!_bit_is_set(a,0) && !_bit_is_set(b,0)) {
|
||||
krk_long tmpa, tmpb;
|
||||
krk_long_init_ui(tmpa, 0);
|
||||
krk_long_init_ui(tmpb, 0);
|
||||
|
||||
_krk_long_rshift_z(tmpa, a, 1);
|
||||
_krk_long_rshift_z(tmpb, b, 1);
|
||||
|
||||
krk_long_clear(a);
|
||||
krk_long_clear(b);
|
||||
|
||||
memcpy(&a,&tmpa,sizeof(krk_long));
|
||||
memcpy(&b,&tmpb,sizeof(krk_long));
|
||||
}
|
||||
|
||||
/* Set sign of a to match sign of float */
|
||||
krk_long_set_sign(a, d < 0 ? -1 : 1);
|
||||
|
||||
/* Stuff it in a tuple */
|
||||
_finish: (void)0;
|
||||
KrkTuple * mtuple = krk_newTuple(2);
|
||||
krk_push(OBJECT_VAL(mtuple));
|
||||
mtuple->values.values[mtuple->values.count++] = make_long_obj(a);
|
||||
mtuple->values.values[mtuple->values.count++] = make_long_obj(b);
|
||||
return krk_pop();
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
|
@ -813,6 +813,11 @@ KRK_Method(float,__rfloordiv__) {
|
||||
KRK_Method(float,__pos__) {
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
extern KrkValue krk_float_to_fraction(double d);
|
||||
KRK_Method(float,as_integer_ratio) {
|
||||
return krk_float_to_fraction(self);
|
||||
}
|
||||
#endif
|
||||
|
||||
#undef CURRENT_CTYPE
|
||||
@ -958,6 +963,7 @@ void _createAndBind_numericClasses(void) {
|
||||
BIND_METHOD(float,__abs__);
|
||||
BIND_METHOD(float,__pos__);
|
||||
BIND_METHOD(float,__format__);
|
||||
BIND_METHOD(float,as_integer_ratio);
|
||||
#endif
|
||||
krk_finalizeClass(_float);
|
||||
KRK_DOC(_float, "Convert a number or string type to a float representation.");
|
||||
|
Loading…
Reference in New Issue
Block a user