mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-20 03:20:18 +00:00
make float
always 64-bit
This commit is contained in:
parent
367d576a66
commit
dcb5f38ddf
@ -85,7 +85,7 @@ They are prefixed by `tp_`. For example, `tp_object`(object),
|
||||
`tp_int`(int), `tp_str`(str), `tp_list`(list), etc.
|
||||
|
||||
Types are divided into **tagged type** and **non-tagged type**.
|
||||
+ `int` (small) and `float` are tagged type.
|
||||
+ small `int` objects are tagged.
|
||||
+ Other types are non-tagged type.
|
||||
|
||||
To determine whether a `PyObject*` is of a specific type,
|
||||
|
@ -82,8 +82,6 @@ inline constexpr bool is_negative_shift_well_defined(){
|
||||
template <>
|
||||
struct NumberTraits<4> {
|
||||
using int_t = int32_t;
|
||||
using float_t = float;
|
||||
|
||||
static constexpr int_t kMaxSmallInt = (1 << 28) - 1;
|
||||
static constexpr int_t kMinSmallInt = is_negative_shift_well_defined() ? -(1 << 28) : 0;
|
||||
static constexpr float_t kEpsilon = (float_t)1e-4;
|
||||
@ -92,8 +90,6 @@ struct NumberTraits<4> {
|
||||
template <>
|
||||
struct NumberTraits<8> {
|
||||
using int_t = int64_t;
|
||||
using float_t = double;
|
||||
|
||||
static constexpr int_t kMaxSmallInt = (1ll << 60) - 1;
|
||||
static constexpr int_t kMinSmallInt = is_negative_shift_well_defined() ? -(1ll << 60) : 0;
|
||||
static constexpr float_t kEpsilon = (float_t)1e-8;
|
||||
@ -101,66 +97,9 @@ struct NumberTraits<8> {
|
||||
|
||||
using Number = NumberTraits<sizeof(void*)>;
|
||||
using i64 = int64_t; // always 64-bit
|
||||
using f64 = Number::float_t;
|
||||
|
||||
template<size_t T>
|
||||
union BitsCvtImpl;
|
||||
|
||||
template<>
|
||||
union BitsCvtImpl<4>{
|
||||
NumberTraits<4>::int_t _int;
|
||||
NumberTraits<4>::float_t _float;
|
||||
|
||||
// 1 + 8 + 23
|
||||
int sign() const { return _int >> 31; }
|
||||
unsigned int exp() const { return (_int >> 23) & 0b1111'1111; }
|
||||
uint64_t mantissa() const { return _int & 0x7fffff; }
|
||||
|
||||
void set_exp(int exp) { _int = (_int & 0x807f'ffff) | (exp << 23); }
|
||||
void set_sign(int sign) { _int = (_int & 0x7fff'ffff) | (sign << 31); }
|
||||
void zero_mantissa() { _int &= 0xff80'0000; }
|
||||
|
||||
static constexpr int C0 = 127; // 2^7 - 1
|
||||
static constexpr int C1 = -62; // 2 - 2^6
|
||||
static constexpr int C2 = 63; // 2^6 - 1
|
||||
static constexpr NumberTraits<4>::int_t C3 = 0b1011'1111'1111'1111'1111'1111'1111'1111;
|
||||
static constexpr int C4 = 0b11111111;
|
||||
|
||||
BitsCvtImpl(NumberTraits<4>::float_t val): _float(val) {}
|
||||
BitsCvtImpl(NumberTraits<4>::int_t val): _int(val) {}
|
||||
};
|
||||
|
||||
template<>
|
||||
union BitsCvtImpl<8>{
|
||||
NumberTraits<8>::int_t _int;
|
||||
NumberTraits<8>::float_t _float;
|
||||
|
||||
// 1 + 11 + 52
|
||||
int sign() const { return _int >> 63; }
|
||||
unsigned int exp() const { return (_int >> 52) & 0b0111'1111'1111; }
|
||||
uint64_t mantissa() const { return _int & 0xfffffffffffff; }
|
||||
|
||||
void set_exp(uint64_t exp) { _int = (_int & 0x800f'ffff'ffff'ffff) | (exp << 52); }
|
||||
void set_sign(uint64_t sign) { _int = (_int & 0x7fff'ffff'ffff'ffff) | (sign << 63); }
|
||||
void zero_mantissa() { _int &= 0xfff0'0000'0000'0000; }
|
||||
|
||||
static constexpr int C0 = 1023; // 2^10 - 1
|
||||
static constexpr int C1 = -510; // 2 - 2^9
|
||||
static constexpr int C2 = 511; // 2^9 - 1
|
||||
static constexpr NumberTraits<8>::int_t C3 = 0b1011'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111;
|
||||
static constexpr int C4 = 0b11111111111;
|
||||
|
||||
BitsCvtImpl(NumberTraits<8>::float_t val): _float(val) {}
|
||||
BitsCvtImpl(NumberTraits<8>::int_t val): _int(val) {}
|
||||
};
|
||||
|
||||
using BitsCvt = BitsCvtImpl<sizeof(void*)>;
|
||||
using f64 = double; // always 64-bit
|
||||
|
||||
static_assert(sizeof(i64) == 8);
|
||||
static_assert(sizeof(Number::float_t) == sizeof(void*));
|
||||
static_assert(sizeof(Number::int_t) == sizeof(void*));
|
||||
static_assert(sizeof(BitsCvt) == sizeof(void*));
|
||||
static_assert(std::numeric_limits<f64>::is_iec559);
|
||||
|
||||
struct Dummy { }; // for special objects: True, False, None, Ellipsis, etc.
|
||||
struct DummyInstance { };
|
||||
@ -193,38 +132,6 @@ struct PyObject;
|
||||
#define PK_BITS(p) (reinterpret_cast<i64>(p))
|
||||
#define PK_SMALL_INT(val) (reinterpret_cast<PyObject*>(val << 2 | 0b10))
|
||||
|
||||
inline PyObject* tag_float(f64 val){
|
||||
BitsCvt decomposed(val);
|
||||
// std::cout << "tagging: " << val << std::endl;
|
||||
int sign = decomposed.sign();
|
||||
int exp_7b = decomposed.exp() - BitsCvt::C0;
|
||||
if(exp_7b < BitsCvt::C1){
|
||||
exp_7b = BitsCvt::C1 - 1; // -63 + 63 = 0
|
||||
decomposed.zero_mantissa();
|
||||
}else if(exp_7b > BitsCvt::C2){
|
||||
exp_7b = BitsCvt::C2 + 1; // 64 + 63 = 127
|
||||
if(!std::isnan(val)) decomposed.zero_mantissa();
|
||||
}
|
||||
decomposed.set_exp(exp_7b + BitsCvt::C2);
|
||||
decomposed._int = (decomposed._int << 1) | 0b01;
|
||||
decomposed.set_sign(sign);
|
||||
return reinterpret_cast<PyObject*>(decomposed._int);
|
||||
}
|
||||
|
||||
inline f64 untag_float(PyObject* val){
|
||||
BitsCvt decomposed(reinterpret_cast<Number::int_t>(val));
|
||||
// std::cout << "untagging: " << val << std::endl;
|
||||
decomposed._int = (decomposed._int >> 1) & BitsCvt::C3;
|
||||
unsigned int exp_7b = decomposed.exp();
|
||||
if(exp_7b == 0) return 0.0f;
|
||||
if(exp_7b == BitsCvt::C0){
|
||||
decomposed.set_exp(BitsCvt::C4);
|
||||
return decomposed._float;
|
||||
}
|
||||
decomposed.set_exp(exp_7b - BitsCvt::C2 + BitsCvt::C0);
|
||||
return decomposed._float;
|
||||
}
|
||||
|
||||
// is_pod<> for c++17 and c++20
|
||||
template<typename T>
|
||||
struct is_pod {
|
||||
|
@ -142,7 +142,7 @@ const int kTpFloatIndex = 3;
|
||||
inline bool is_tagged(PyObject* p) noexcept { return (PK_BITS(p) & 0b11) != 0b00; }
|
||||
inline bool is_small_int(PyObject* p) noexcept { return (PK_BITS(p) & 0b11) == 0b10; }
|
||||
inline bool is_heap_int(PyObject* p) noexcept { return !is_tagged(p) && p->type.index == kTpIntIndex; }
|
||||
inline bool is_float(PyObject* p) noexcept { return (PK_BITS(p) & 1) == 1; } // 01 or 11
|
||||
inline bool is_float(PyObject* p) noexcept { return !is_tagged(p) && p->type.index == kTpFloatIndex; }
|
||||
inline bool is_int(PyObject* p) noexcept { return is_small_int(p) || is_heap_int(p); }
|
||||
|
||||
inline bool is_type(PyObject* obj, Type type) {
|
||||
|
@ -504,7 +504,8 @@ PyObject* py_var(VM* vm, __T&& value){
|
||||
}
|
||||
}else if constexpr(is_floating_point_v<T>){
|
||||
// float
|
||||
return tag_float(static_cast<f64>(std::forward<__T>(value)));
|
||||
f64 val = static_cast<f64>(std::forward<__T>(value));
|
||||
return vm->heap.gcnew<f64>(vm->tp_float, val);
|
||||
}else if constexpr(std::is_pointer_v<T>){
|
||||
return from_void_p(vm, (void*)value);
|
||||
}else{
|
||||
@ -553,7 +554,7 @@ __T _py_cast__internal(VM* vm, PyObject* obj) {
|
||||
}else if constexpr(is_floating_point_v<T>){
|
||||
static_assert(!std::is_reference_v<__T>);
|
||||
// float
|
||||
if(is_float(obj)) return untag_float(obj);
|
||||
if(is_float(obj)) return PK_OBJ_GET(f64, obj);
|
||||
i64 bits;
|
||||
if(try_cast_int(obj, &bits)) return (float)bits;
|
||||
vm->TypeError("expected 'int' or 'float', got " + _type_name(vm, vm->_tp(obj)).escape());
|
||||
|
@ -30,13 +30,15 @@ PyObject* PyArrayGetItem(VM* vm, PyObject* _0, PyObject* _1){
|
||||
void init_builtins(VM* _vm) {
|
||||
#define BIND_NUM_ARITH_OPT(name, op) \
|
||||
_vm->bind##name(VM::tp_int, [](VM* vm, PyObject* lhs, PyObject* rhs) { \
|
||||
if(is_int(rhs)) return VAR(_CAST(i64, lhs) op _CAST(i64, rhs)); \
|
||||
i64 val; \
|
||||
if(try_cast_int(rhs, &val)) return VAR(_CAST(i64, lhs) op val); \
|
||||
if(is_float(rhs)) return VAR(_CAST(i64, lhs) op _CAST(f64, rhs)); \
|
||||
return vm->NotImplemented; \
|
||||
}); \
|
||||
_vm->bind##name(VM::tp_float, [](VM* vm, PyObject* lhs, PyObject* rhs) { \
|
||||
_vm->bind##name(VM::tp_float, [](VM* vm, PyObject* lhs, PyObject* rhs) { \
|
||||
i64 val; \
|
||||
if(try_cast_int(rhs, &val)) return VAR(_CAST(f64, lhs) op val); \
|
||||
if(is_float(rhs)) return VAR(_CAST(f64, lhs) op _CAST(f64, rhs)); \
|
||||
if(is_int(rhs)) return VAR(_CAST(f64, lhs) op _CAST(i64, rhs)); \
|
||||
return vm->NotImplemented; \
|
||||
});
|
||||
|
||||
|
@ -62,9 +62,9 @@ assert 1/inf == 0
|
||||
assert -1/inf == 0
|
||||
assert math.isnan(0/0)
|
||||
|
||||
assert 2**-600 == 0.0
|
||||
assert 2.0 ** 600 == inf
|
||||
assert (-2.0) ** 601 == -inf
|
||||
assert 2**-6000 == 0.0
|
||||
assert 2.0 ** 6000 == inf
|
||||
assert (-2.0) ** 6001 == -inf
|
||||
|
||||
# test .123 forms
|
||||
assert float(".123") == 0.123
|
||||
|
@ -44,9 +44,9 @@ assert repr(-math.nan) == repr(-nan) == 'nan'
|
||||
assert repr(math.inf) == repr(inf) == 'inf'
|
||||
assert repr(-math.inf) == repr(-inf) == '-inf'
|
||||
assert repr(nanj) == '(0.0+nanj)', nanj
|
||||
assert repr(-nanj) == '(0.0+nanj)', -nanj
|
||||
assert repr(-nanj) == '(-0.0+nanj)', -nanj
|
||||
assert repr(infj) == '(0.0+infj)', infj
|
||||
assert repr(-infj) == '(0.0-infj)', -infj
|
||||
assert repr(-infj) == '(-0.0-infj)', -infj
|
||||
|
||||
assert math.log(1) == 0.0
|
||||
assert isclose(log(10+5j), 2.4141568686511508+0.4636476090008061j)
|
||||
|
Loading…
x
Reference in New Issue
Block a user