From dcb5f38ddff78234996ecb703c90d6c36d429b44 Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Sat, 6 Apr 2024 17:54:53 +0800 Subject: [PATCH] make `float` always 64-bit --- docs/quick-start/interop.md | 2 +- include/pocketpy/common.h | 95 +------------------------------------ include/pocketpy/obj.h | 2 +- include/pocketpy/vm.h | 5 +- src/pocketpy.cpp | 8 ++-- tests/02_float.py | 6 +-- tests/10_cmath.py | 4 +- 7 files changed, 16 insertions(+), 106 deletions(-) diff --git a/docs/quick-start/interop.md b/docs/quick-start/interop.md index 3335225d..d406e18c 100644 --- a/docs/quick-start/interop.md +++ b/docs/quick-start/interop.md @@ -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, diff --git a/include/pocketpy/common.h b/include/pocketpy/common.h index 89aec77d..2eefa336 100644 --- a/include/pocketpy/common.h +++ b/include/pocketpy/common.h @@ -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; using i64 = int64_t; // always 64-bit -using f64 = Number::float_t; - -template -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; +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::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(p)) #define PK_SMALL_INT(val) (reinterpret_cast(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(decomposed._int); -} - -inline f64 untag_float(PyObject* val){ - BitsCvt decomposed(reinterpret_cast(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 struct is_pod { diff --git a/include/pocketpy/obj.h b/include/pocketpy/obj.h index f306706f..4f271941 100644 --- a/include/pocketpy/obj.h +++ b/include/pocketpy/obj.h @@ -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) { diff --git a/include/pocketpy/vm.h b/include/pocketpy/vm.h index e882e226..f6d9808b 100644 --- a/include/pocketpy/vm.h +++ b/include/pocketpy/vm.h @@ -504,7 +504,8 @@ PyObject* py_var(VM* vm, __T&& value){ } }else if constexpr(is_floating_point_v){ // float - return tag_float(static_cast(std::forward<__T>(value))); + f64 val = static_cast(std::forward<__T>(value)); + return vm->heap.gcnew(vm->tp_float, val); }else if constexpr(std::is_pointer_v){ 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){ 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()); diff --git a/src/pocketpy.cpp b/src/pocketpy.cpp index afc77631..2750e7d5 100644 --- a/src/pocketpy.cpp +++ b/src/pocketpy.cpp @@ -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; \ }); diff --git a/tests/02_float.py b/tests/02_float.py index 07d972b8..8f079574 100644 --- a/tests/02_float.py +++ b/tests/02_float.py @@ -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 diff --git a/tests/10_cmath.py b/tests/10_cmath.py index 515c0b50..a758c8d0 100644 --- a/tests/10_cmath.py +++ b/tests/10_cmath.py @@ -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)