reimpl float

This commit is contained in:
BLUELOVETH 2023-09-22 16:32:57 +08:00
parent e86e7feafd
commit 7254c5a491
3 changed files with 91 additions and 39 deletions

View File

@ -76,10 +76,6 @@ struct NumberTraits<4> {
template<typename... Args> template<typename... Args>
static float_t stof(Args&&... args) { return std::stof(std::forward<Args>(args)...); } static float_t stof(Args&&... args) { return std::stof(std::forward<Args>(args)...); }
static constexpr int_t c0 = 0b00000000011111111111111111111100;
static constexpr int_t c1 = 0b11111111111111111111111111111100;
static constexpr int_t c2 = 0b00000000000000000000000000000011;
}; };
template <> template <>
@ -89,18 +85,59 @@ struct NumberTraits<8> {
template<typename... Args> template<typename... Args>
static float_t stof(Args&&... args) { return std::stod(std::forward<Args>(args)...); } static float_t stof(Args&&... args) { return std::stod(std::forward<Args>(args)...); }
static constexpr int_t c0 = 0b0000000000001111111111111111111111111111111111111111111111111100;
static constexpr int_t c1 = 0b1111111111111111111111111111111111111111111111111111111111111100;
static constexpr int_t c2 = 0b0000000000000000000000000000000000000000000000000000000000000011;
}; };
using Number = NumberTraits<sizeof(void*)>; using Number = NumberTraits<sizeof(void*)>;
using i64 = int64_t; using i64 = int64_t; // always 64-bit
using f64 = Number::float_t; 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;
struct{
unsigned int sign: 1;
unsigned int exp: 8;
uint64_t mantissa: 23;
} _float_bits;
static constexpr int C0 = 127; // 2^7 - 1
static constexpr int C1 = -62; // 2 - 2^6
static constexpr int C2 = 63; // 2^6 - 1
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;
struct{
unsigned int sign: 1;
unsigned int exp: 11;
uint64_t mantissa: 52;
} _float_bits;
static constexpr int C0 = 1023; // 2^10 - 1
static constexpr int C1 = -510; // 2 - 2^9
static constexpr int C2 = 511; // 2^9 - 1
BitsCvtImpl(NumberTraits<8>::float_t val): _float(val) {}
BitsCvtImpl(NumberTraits<8>::int_t val): _int(val) {}
};
using BitsCvt = BitsCvtImpl<sizeof(void*)>;
static_assert(sizeof(i64) == 8); static_assert(sizeof(i64) == 8);
static_assert(sizeof(f64) == sizeof(void*)); 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); static_assert(std::numeric_limits<f64>::is_iec559);
struct Dummy { }; struct Dummy { };
@ -133,10 +170,33 @@ struct Type {
struct PyObject; struct PyObject;
#define PK_BITS(p) (reinterpret_cast<Number::int_t>(p)) #define PK_BITS(p) (reinterpret_cast<Number::int_t>(p))
// special singals, is_tagged() for them is true inline PyObject* tag_float(f64 val){
inline PyObject* const PY_NULL = (PyObject*)0b000011; // tagged null BitsCvt decomposed(val);
inline PyObject* const PY_OP_CALL = (PyObject*)0b100011; unsigned int exp_7b = decomposed._float_bits.exp;
inline PyObject* const PY_OP_YIELD = (PyObject*)0b110011; if(exp_7b - BitsCvt::C0 < BitsCvt::C1){
exp_7b = 0;
decomposed._float_bits.mantissa = 0;
}else if(exp_7b - BitsCvt::C0 > BitsCvt::C2){
exp_7b = BitsCvt::C0;
if(!std::isnan(val)) decomposed._float_bits.mantissa = 0;
}
decomposed._float_bits.exp = exp_7b - BitsCvt::C0 + BitsCvt::C2;
decomposed._int = (decomposed._int << 1) | 0b01;
return reinterpret_cast<PyObject*>(decomposed._int);
}
inline f64 untag_float(PyObject* val){
BitsCvt decomposed(reinterpret_cast<Number::int_t>(val));
decomposed._int >>= 1;
unsigned int exp_7b = decomposed._float_bits.exp;
if(exp_7b == 0) return 0.0f;
if(exp_7b == BitsCvt::C0){
decomposed._float_bits.exp = -1;
return decomposed._float;
}
decomposed._float_bits.exp = exp_7b - BitsCvt::C2 + BitsCvt::C0;
return decomposed._float;
}
// is_pod<> for c++17 and c++20 // is_pod<> for c++17 and c++20
template<typename T> template<typename T>

View File

@ -123,20 +123,29 @@ struct PyObject{
} }
}; };
struct PySignalObject: PyObject {
PySignalObject() : PyObject(0) {
gc.enabled = false;
}
void _obj_gc_mark() override {}
};
inline PyObject* const PY_NULL = new PySignalObject();
inline PyObject* const PY_OP_CALL = new PySignalObject();
inline PyObject* const PY_OP_YIELD = new PySignalObject();
const int kTpIntIndex = 2; const int kTpIntIndex = 2;
const int kTpFloatIndex = 3; const int kTpFloatIndex = 3;
inline bool is_tagged(PyObject* p) noexcept { return (PK_BITS(p) & 0b11) != 0b00; } 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) == 0b01; } 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_heap_int(PyObject* p) noexcept { return !is_tagged(p) && p->type.index == kTpIntIndex; }
inline bool is_float(PyObject* p) noexcept { return (PK_BITS(p) & 0b11) == 0b10; } inline bool is_float(PyObject* p) noexcept { return (PK_BITS(p) & 1) == 1; } // 01 or 11
inline bool is_special(PyObject* p) noexcept { return (PK_BITS(p) & 0b11) == 0b11; }
inline bool is_int(PyObject* p) noexcept { return is_small_int(p) || is_heap_int(p); } inline bool is_int(PyObject* p) noexcept { return is_small_int(p) || is_heap_int(p); }
inline bool is_type(PyObject* obj, Type type) { inline bool is_type(PyObject* obj, Type type) {
#if PK_DEBUG_EXTRA_CHECK #if PK_DEBUG_EXTRA_CHECK
if(obj == nullptr) throw std::runtime_error("is_type() called with nullptr"); if(obj == nullptr) throw std::runtime_error("is_type() called with nullptr");
if(is_special(obj)) throw std::runtime_error("is_type() called with special object");
#endif #endif
switch(type.index){ switch(type.index){
case kTpIntIndex: return is_int(obj); case kTpIntIndex: return is_int(obj);
@ -148,7 +157,6 @@ inline bool is_type(PyObject* obj, Type type) {
inline bool is_non_tagged_type(PyObject* obj, Type type) { inline bool is_non_tagged_type(PyObject* obj, Type type) {
#if PK_DEBUG_EXTRA_CHECK #if PK_DEBUG_EXTRA_CHECK
if(obj == nullptr) throw std::runtime_error("is_non_tagged_type() called with nullptr"); if(obj == nullptr) throw std::runtime_error("is_non_tagged_type() called with nullptr");
if(is_special(obj)) throw std::runtime_error("is_non_tagged_type() called with special object");
#endif #endif
return !is_tagged(obj) && obj->type == type; return !is_tagged(obj) && obj->type == type;
} }
@ -200,13 +208,6 @@ Str obj_type_name(VM* vm, Type type);
#define OBJ_NAME(obj) PK_OBJ_GET(Str, vm->getattr(obj, __name__)) #define OBJ_NAME(obj) PK_OBJ_GET(Str, vm->getattr(obj, __name__))
#endif #endif
union BitsCvt {
Number::int_t _int;
f64 _float;
BitsCvt(Number::int_t val) : _int(val) {}
BitsCvt(f64 val) : _float(val) {}
};
template <typename, typename=void> struct is_py_class : std::false_type {}; template <typename, typename=void> struct is_py_class : std::false_type {};
template <typename T> struct is_py_class<T, std::void_t<decltype(T::_type)>> : std::true_type {}; template <typename T> struct is_py_class<T, std::void_t<decltype(T::_type)>> : std::true_type {};

View File

@ -505,7 +505,7 @@ PY_CAST_INT(unsigned long)
PY_CAST_INT(unsigned long long) PY_CAST_INT(unsigned long long)
template<> inline float py_cast<float>(VM* vm, PyObject* obj){ template<> inline float py_cast<float>(VM* vm, PyObject* obj){
if(is_float(obj)) return BitsCvt(PK_BITS(obj) & Number::c1)._float; if(is_float(obj)) return untag_float(obj);
i64 bits; i64 bits;
if(try_cast_int(obj, &bits)) return (float)bits; if(try_cast_int(obj, &bits)) return (float)bits;
vm->TypeError("expected 'int' or 'float', got " + OBJ_NAME(vm->_t(obj)).escape()); vm->TypeError("expected 'int' or 'float', got " + OBJ_NAME(vm->_t(obj)).escape());
@ -515,7 +515,7 @@ template<> inline float _py_cast<float>(VM* vm, PyObject* obj){
return py_cast<float>(vm, obj); return py_cast<float>(vm, obj);
} }
template<> inline double py_cast<double>(VM* vm, PyObject* obj){ template<> inline double py_cast<double>(VM* vm, PyObject* obj){
if(is_float(obj)) return BitsCvt(PK_BITS(obj) & Number::c1)._float; if(is_float(obj)) return untag_float(obj);
i64 bits; i64 bits;
if(try_cast_int(obj, &bits)) return (float)bits; if(try_cast_int(obj, &bits)) return (float)bits;
vm->TypeError("expected 'int' or 'float', got " + OBJ_NAME(vm->_t(obj)).escape()); vm->TypeError("expected 'int' or 'float', got " + OBJ_NAME(vm->_t(obj)).escape());
@ -532,7 +532,7 @@ const i64 kMinSmallInt = -(1ll << 28);
inline PyObject* py_var(VM* vm, T _val){ \ inline PyObject* py_var(VM* vm, T _val){ \
i64 val = static_cast<i64>(_val); \ i64 val = static_cast<i64>(_val); \
if(val >= kMinSmallInt && val <= kMaxSmallInt){ \ if(val >= kMinSmallInt && val <= kMaxSmallInt){ \
val = (val << 2) | 0b01; \ val = (val << 2) | 0b10; \
return reinterpret_cast<PyObject*>(val); \ return reinterpret_cast<PyObject*>(val); \
}else{ \ }else{ \
return vm->heap.gcnew<i64>(vm->tp_int, val); \ return vm->heap.gcnew<i64>(vm->tp_int, val); \
@ -554,16 +554,7 @@ PY_VAR_INT(unsigned long long)
#define PY_VAR_FLOAT(T) \ #define PY_VAR_FLOAT(T) \
inline PyObject* py_var(VM* vm, T _val){ \ inline PyObject* py_var(VM* vm, T _val){ \
PK_UNUSED(vm); \ PK_UNUSED(vm); \
BitsCvt val(static_cast<f64>(_val)); \ return tag_float(static_cast<f64>(_val)); \
i64 bits = val._int & Number::c1; \
i64 tail = val._int & Number::c2; \
if(tail == 0b10){ \
if(bits&0b100) bits += 0b100; \
}else if(tail == 0b11){ \
bits += 0b100; \
} \
bits |= 0b10; \
return reinterpret_cast<PyObject*>(bits); \
} }
PY_VAR_FLOAT(float) PY_VAR_FLOAT(float)