mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
boxing_word.h: lossless float encoding using rotation
Replace lossy 2-bit truncation with rotation-based encoding for 64-bit word boxing with float64. The new scheme uses rotl64(float_bits - ADDEND, 3) to embed floats inline with full 52-bit mantissa precision. Floats with exponents outside [-255,+256] (0.0, NaN, Inf, very small/large values) fall back to heap-allocated RFloat. Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
+15
-18
@@ -11,7 +11,7 @@
|
||||
# define MRB_WORDBOX_NO_FLOAT_TRUNCATE
|
||||
#endif
|
||||
|
||||
#if !defined(MRB_NO_FLOAT) && defined(MRB_WORDBOX_NO_FLOAT_TRUNCATE)
|
||||
#ifndef MRB_NO_FLOAT
|
||||
struct RFloat {
|
||||
MRB_OBJECT_HEADER;
|
||||
mrb_float f;
|
||||
@@ -83,27 +83,22 @@ enum mrb_special_consts {
|
||||
/*
|
||||
* mrb_value representation:
|
||||
*
|
||||
* 64-bit word with inline float:
|
||||
* 64-bit word with inline float (rotation encoding, lossless):
|
||||
* nil : ...0000 0000 (all bits are 0)
|
||||
* false : ...0000 0100 (mrb_fixnum(v) != 0)
|
||||
* true : ...0000 1100
|
||||
* undef : ...0001 0100
|
||||
* symbol: ...0001 1100 (use only upper 32-bit as symbol value with MRB_64BIT)
|
||||
* fixnum: ...IIII III1
|
||||
* float : ...FFFF FF10 (51 bit significands; require MRB_64BIT)
|
||||
* float : ...FFFF FF10 (rotl64(float64-ADDEND, 3); exponent [-255,+256])
|
||||
* object: ...PPPP P000
|
||||
* (floats outside inline range are heap-allocated as RFloat)
|
||||
*
|
||||
* 32-bit word with inline float:
|
||||
* nil : ...0000 0000 (all bits are 0)
|
||||
* false : ...0000 0100 (mrb_fixnum(v) != 0)
|
||||
* true : ...0000 1100
|
||||
* undef : ...0001 0100
|
||||
* symbol: ...SSS1 0100 (symbol occupies 20bits)
|
||||
* fixnum: ...IIII III1
|
||||
* float : ...FFFF FF10 (22 bit significands; require MRB_64BIT)
|
||||
* object: ...PPPP P000
|
||||
* 64-bit word with inline float32 (MRB_USE_FLOAT32):
|
||||
* float : ...FFFF FF10 (float32 shifted left by 2)
|
||||
* (other values same as above)
|
||||
*
|
||||
* and word boxing without inline float (MRB_WORDBOX_NO_FLOAT_TRUNCATE):
|
||||
* word boxing without inline float (MRB_WORDBOX_NO_FLOAT_TRUNCATE):
|
||||
* nil : ...0000 0000 (all bits are 0)
|
||||
* false : ...0000 0100 (mrb_fixnum(v) != 0)
|
||||
* true : ...0000 1100
|
||||
@@ -120,10 +115,9 @@ union mrb_value_ {
|
||||
void *p;
|
||||
struct RBasic *bp;
|
||||
#ifndef MRB_NO_FLOAT
|
||||
#ifndef MRB_WORDBOX_NO_FLOAT_TRUNCATE
|
||||
mrb_float f;
|
||||
#else
|
||||
struct RFloat *fp;
|
||||
#if !defined(MRB_WORDBOX_NO_FLOAT_TRUNCATE) && defined(MRB_USE_FLOAT32)
|
||||
mrb_float f;
|
||||
#endif
|
||||
#endif
|
||||
struct RInteger *ip;
|
||||
@@ -182,10 +176,13 @@ mrb_integer_func(mrb_value o) {
|
||||
#define mrb_false_p(o) ((o).w == MRB_Qfalse)
|
||||
#define mrb_true_p(o) ((o).w == MRB_Qtrue)
|
||||
#ifndef MRB_NO_FLOAT
|
||||
#ifndef MRB_WORDBOX_NO_FLOAT_TRUNCATE
|
||||
#ifdef MRB_WORDBOX_NO_FLOAT_TRUNCATE
|
||||
#define mrb_float_p(o) WORDBOX_OBJ_TYPE_P(o, FLOAT)
|
||||
#elif defined(MRB_USE_FLOAT32)
|
||||
#define mrb_float_p(o) WORDBOX_SHIFT_VALUE_P(o, FLOAT)
|
||||
#else
|
||||
#define mrb_float_p(o) WORDBOX_OBJ_TYPE_P(o, FLOAT)
|
||||
/* rotation encoding: most floats inline, edge cases on heap */
|
||||
#define mrb_float_p(o) (WORDBOX_SHIFT_VALUE_P(o, FLOAT) || WORDBOX_OBJ_TYPE_P(o, FLOAT))
|
||||
#endif
|
||||
#else
|
||||
#define mrb_float_p(o) FALSE
|
||||
|
||||
+1
-1
@@ -3959,7 +3959,7 @@ init_copy(mrb_state *mrb, mrb_value dest, mrb_value obj)
|
||||
case MRB_TT_ISTRUCT:
|
||||
mrb_istruct_copy(dest, obj);
|
||||
break;
|
||||
#if !defined(MRB_NO_FLOAT) && defined(MRB_WORDBOX_NO_FLOAT_TRUNCATE)
|
||||
#if !defined(MRB_NO_FLOAT) && defined(MRB_WORD_BOXING)
|
||||
case MRB_TT_FLOAT:
|
||||
{
|
||||
struct RFloat *f = (struct RFloat*)mrb_obj_ptr(dest);
|
||||
|
||||
@@ -182,8 +182,51 @@ mrb_obj_id(mrb_value obj)
|
||||
* RFloat object on the heap.
|
||||
* - If `MRB_64BIT` and `MRB_USE_FLOAT32` are defined, it stores the float
|
||||
* in the lower bits of the word, shifted and tagged.
|
||||
* - Otherwise, it stores the float directly in the word, tagged.
|
||||
* - Otherwise (64-bit float64), it uses rotation encoding to store the
|
||||
* float losslessly inline. Floats outside the inline exponent range
|
||||
* [-255, +256] are heap-allocated as RFloat.
|
||||
*/
|
||||
|
||||
#if !defined(MRB_WORDBOX_NO_FLOAT_TRUNCATE) && !defined(MRB_USE_FLOAT32)
|
||||
/*
|
||||
* Rotation-based float encoding for 64-bit + float64.
|
||||
*
|
||||
* Encode: rotl64(float64_bits - ADDEND, 3) produces a tagged value
|
||||
* with bottom 2 bits == 10 (WORDBOX_FLOAT_FLAG).
|
||||
* Decode: rotl64(tagged_value, 61) + ADDEND recovers the original bits.
|
||||
*
|
||||
* The addend shifts the exponent so that biased exponents [768, 1279]
|
||||
* (actual [-255, +256]) produce the correct tag pattern after rotation.
|
||||
* This covers all practical float values with full precision.
|
||||
*/
|
||||
#define WORDBOX_FLOAT_ROTATE 3
|
||||
#define WORDBOX_FLOAT_EXP_MIN (1023 - 255) /* 768 */
|
||||
#define WORDBOX_FLOAT_EXP_MAX (1023 + 256) /* 1279 */
|
||||
#define WORDBOX_FLOAT_ADDEND ((uint64_t)((int64_t)(WORDBOX_FLOAT_EXP_MIN - (WORDBOX_FLOAT_FLAG << 9)) << 52))
|
||||
|
||||
static uint64_t
|
||||
wordbox_rotl64(uint64_t a, int n)
|
||||
{
|
||||
return (a << n) | (a >> (64 - n));
|
||||
}
|
||||
|
||||
static uint64_t
|
||||
wordbox_float64_to_u64(double d)
|
||||
{
|
||||
union { double d; uint64_t u; } u;
|
||||
u.d = d;
|
||||
return u.u;
|
||||
}
|
||||
|
||||
static double
|
||||
wordbox_u64_to_float64(uint64_t v)
|
||||
{
|
||||
union { double d; uint64_t u; } u;
|
||||
u.u = v;
|
||||
return u.d;
|
||||
}
|
||||
#endif
|
||||
|
||||
MRB_API mrb_value
|
||||
mrb_word_boxing_float_value(mrb_state *mrb, mrb_float f)
|
||||
{
|
||||
@@ -197,7 +240,21 @@ mrb_word_boxing_float_value(mrb_state *mrb, mrb_float f)
|
||||
v.w = 0;
|
||||
v.f = f;
|
||||
v.w = (v.w<<2) | 2;
|
||||
#elif defined(MRB_64BIT)
|
||||
{
|
||||
uint64_t bits = wordbox_float64_to_u64((double)f);
|
||||
uint64_t exp = (bits >> 52) & 0x7FF;
|
||||
if (exp >= WORDBOX_FLOAT_EXP_MIN && exp <= WORDBOX_FLOAT_EXP_MAX) {
|
||||
v.w = (uintptr_t)wordbox_rotl64(bits - WORDBOX_FLOAT_ADDEND, WORDBOX_FLOAT_ROTATE);
|
||||
}
|
||||
else {
|
||||
v.p = mrb_obj_alloc(mrb, MRB_TT_FLOAT, mrb->float_class);
|
||||
v.fp->f = f;
|
||||
v.bp->frozen = 1;
|
||||
}
|
||||
}
|
||||
#else
|
||||
/* 32-bit + float32: truncate bottom 2 bits */
|
||||
v.f = f;
|
||||
v.w = (v.w & ~3) | 2;
|
||||
#endif
|
||||
@@ -207,26 +264,36 @@ mrb_word_boxing_float_value(mrb_state *mrb, mrb_float f)
|
||||
|
||||
#ifndef MRB_WORDBOX_NO_FLOAT_TRUNCATE
|
||||
/*
|
||||
* Unboxes an `mrb_value` to an `mrb_float` when word boxing is used and
|
||||
* float truncation (`MRB_WORDBOX_NO_FLOAT_TRUNCATE`) is not disabled.
|
||||
* The function extracts the float value from the `mrb_value`'s union
|
||||
* representation (`u.value`).
|
||||
* - If `MRB_64BIT` and `MRB_USE_FLOAT32` are defined, the word (`u.w`)
|
||||
* is right-shifted by 2 bits to retrieve the float.
|
||||
* - Otherwise, the lower 2 bits of the word (`u.w`) are cleared to
|
||||
* retrieve the float.
|
||||
* Unboxes an `mrb_value` to an `mrb_float`.
|
||||
* - If `MRB_USE_FLOAT32`: right-shift by 2 to retrieve the float.
|
||||
* - Otherwise (rotation encoding): decode inline floats via rotation,
|
||||
* or read from heap RFloat for edge cases.
|
||||
*/
|
||||
MRB_API mrb_float
|
||||
mrb_word_boxing_value_float(mrb_value v)
|
||||
{
|
||||
#if defined(MRB_64BIT) && defined(MRB_USE_FLOAT32)
|
||||
union mrb_value_ u;
|
||||
u.value = v;
|
||||
#if defined(MRB_64BIT) && defined(MRB_USE_FLOAT32)
|
||||
u.w >>= 2;
|
||||
#else
|
||||
u.w &= ~3;
|
||||
#endif
|
||||
return u.f;
|
||||
#elif defined(MRB_64BIT)
|
||||
if ((v.w & WORDBOX_FLOAT_MASK) == WORDBOX_FLOAT_FLAG) {
|
||||
return (mrb_float)wordbox_u64_to_float64(
|
||||
wordbox_rotl64((uint64_t)v.w, 64 - WORDBOX_FLOAT_ROTATE) + WORDBOX_FLOAT_ADDEND);
|
||||
}
|
||||
else {
|
||||
union mrb_value_ u;
|
||||
u.value = v;
|
||||
return u.fp->f;
|
||||
}
|
||||
#else
|
||||
/* 32-bit + float32: clear tag bits */
|
||||
union mrb_value_ u;
|
||||
u.value = v;
|
||||
u.w &= ~3;
|
||||
return u.f;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
#endif /* MRB_NO_FLOAT */
|
||||
|
||||
Reference in New Issue
Block a user