@@ -182,8 +182,51 @@ mrb_obj_id(mrb_value obj)
182182 * RFloat object on the heap.
183183 * - If `MRB_64BIT` and `MRB_USE_FLOAT32` are defined, it stores the float
184184 * in the lower bits of the word, shifted and tagged.
185- * - Otherwise, it stores the float directly in the word, tagged.
185+ * - Otherwise (64-bit float64), it uses rotation encoding to store the
186+ * float losslessly inline. Floats outside the inline exponent range
187+ * [-255, +256] are heap-allocated as RFloat.
186188 */
189+190+#if !defined(MRB_WORDBOX_NO_FLOAT_TRUNCATE) && !defined(MRB_USE_FLOAT32)
191+/*
192+ * Rotation-based float encoding for 64-bit + float64.
193+ *
194+ * Encode: rotl64(float64_bits - ADDEND, 3) produces a tagged value
195+ * with bottom 2 bits == 10 (WORDBOX_FLOAT_FLAG).
196+ * Decode: rotl64(tagged_value, 61) + ADDEND recovers the original bits.
197+ *
198+ * The addend shifts the exponent so that biased exponents [768, 1279]
199+ * (actual [-255, +256]) produce the correct tag pattern after rotation.
200+ * This covers all practical float values with full precision.
201+ */
202+#define WORDBOX_FLOAT_ROTATE 3
203+#define WORDBOX_FLOAT_EXP_MIN (1023 - 255) /* 768 */
204+#define WORDBOX_FLOAT_EXP_MAX (1023 + 256) /* 1279 */
205+#define WORDBOX_FLOAT_ADDEND ((uint64_t)((int64_t)(WORDBOX_FLOAT_EXP_MIN - (WORDBOX_FLOAT_FLAG << 9)) << 52))
206+207+static uint64_t
208+wordbox_rotl64(uint64_t a, int n)
209+{
210+return (a << n) | (a >> (64 - n));
211+}
212+213+static uint64_t
214+wordbox_float64_to_u64(double d)
215+{
216+union { double d; uint64_t u; } u;
217+u.d = d;
218+return u.u;
219+}
220+221+static double
222+wordbox_u64_to_float64(uint64_t v)
223+{
224+union { double d; uint64_t u; } u;
225+u.u = v;
226+return u.d;
227+}
228+#endif
229+187230MRB_API mrb_value
188231mrb_word_boxing_float_value(mrb_state *mrb, mrb_float f)
189232{
@@ -197,7 +240,21 @@ mrb_word_boxing_float_value(mrb_state *mrb, mrb_float f)
197240v.w = 0;
198241v.f = f;
199242v.w = (v.w<<2) | 2;
243+#elif defined(MRB_64BIT)
244+ {
245+uint64_t bits = wordbox_float64_to_u64((double)f);
246+uint64_t exp = (bits >> 52) & 0x7FF;
247+if (exp >= WORDBOX_FLOAT_EXP_MIN && exp <= WORDBOX_FLOAT_EXP_MAX) {
248+v.w = (uintptr_t)wordbox_rotl64(bits - WORDBOX_FLOAT_ADDEND, WORDBOX_FLOAT_ROTATE);
249+ }
250+else {
251+v.p = mrb_obj_alloc(mrb, MRB_TT_FLOAT, mrb->float_class);
252+v.fp->f = f;
253+v.bp->frozen = 1;
254+ }
255+ }
200256#else
257+/* 32-bit + float32: truncate bottom 2 bits */
201258v.f = f;
202259v.w = (v.w & ~3) | 2;
203260#endif
@@ -207,26 +264,36 @@ mrb_word_boxing_float_value(mrb_state *mrb, mrb_float f)
207264208265#ifndef MRB_WORDBOX_NO_FLOAT_TRUNCATE
209266/*
210- * Unboxes an `mrb_value` to an `mrb_float` when word boxing is used and
211- * float truncation (`MRB_WORDBOX_NO_FLOAT_TRUNCATE`) is not disabled.
212- * The function extracts the float value from the `mrb_value`'s union
213- * representation (`u.value`).
214- * - If `MRB_64BIT` and `MRB_USE_FLOAT32` are defined, the word (`u.w`)
215- * is right-shifted by 2 bits to retrieve the float.
216- * - Otherwise, the lower 2 bits of the word (`u.w`) are cleared to
217- * retrieve the float.
267+ * Unboxes an `mrb_value` to an `mrb_float`.
268+ * - If `MRB_USE_FLOAT32`: right-shift by 2 to retrieve the float.
269+ * - Otherwise (rotation encoding): decode inline floats via rotation,
270+ * or read from heap RFloat for edge cases.
218271 */
219272MRB_API mrb_float
220273mrb_word_boxing_value_float(mrb_value v)
221274{
275+#if defined(MRB_64BIT) && defined(MRB_USE_FLOAT32)
222276union mrb_value_ u;
223277u.value = v;
224-#if defined(MRB_64BIT) && defined(MRB_USE_FLOAT32)
225278u.w >>= 2;
279+return u.f;
280+#elif defined(MRB_64BIT)
281+if ((v.w & WORDBOX_FLOAT_MASK) == WORDBOX_FLOAT_FLAG) {
282+return (mrb_float)wordbox_u64_to_float64(
283+wordbox_rotl64((uint64_t)v.w, 64 - WORDBOX_FLOAT_ROTATE) + WORDBOX_FLOAT_ADDEND);
284+ }
285+else {
286+union mrb_value_ u;
287+u.value = v;
288+return u.fp->f;
289+ }
226290#else
291+/* 32-bit + float32: clear tag bits */
292+union mrb_value_ u;
293+u.value = v;
227294u.w &= ~3;
228-#endif
229295return u.f;
296+#endif
230297}
231298#endif
232299#endif /* MRB_NO_FLOAT */