GitHub

@@ -182,32 +182,26 @@ 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 (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.

185+

* - 64-bit float64: rotation encoding, lossless for exponents [-255, +256].

186+

* - 32-bit float32: rotation encoding, lossless for exponents [-32, +31].

187+

* Floats outside the inline range are heap-allocated as RFloat.

188188

*/

189189190-

#if !defined(MRB_WORDBOX_NO_FLOAT_TRUNCATE) && !defined(MRB_USE_FLOAT32)

190+

#if !defined(MRB_WORDBOX_NO_FLOAT_TRUNCATE) && \

191+

(!defined(MRB_USE_FLOAT32) || !defined(MRB_64BIT))

191192

/*

192-

* Rotation-based float encoding for 64-bit + float64.

193+

* Rotation-based float encoding (shared between 64-bit float64 and

194+

* 32-bit float32 paths).

193195

*

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.

196+

* Encode: rotl(bits - ADDEND, 3) produces a tagged value with bottom

197+

* 2 bits == 10 (WORDBOX_FLOAT_FLAG).

198+

* Decode: rotl(tagged_value, N-3) + ADDEND recovers the original bits.

197199

*

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-

* Special values (0.0, -0.0, +Inf, -Inf) are encoded as small sentinel

203-

* constants that also have bottom 2 bits == 10. This avoids heap

204-

* allocation for these common values. The 4 obscure floats whose

205-

* rotation would collide with a sentinel are heap-allocated instead.

200+

* Special values (0.0, -0.0, +Inf, -Inf, NaN) are encoded as small

201+

* sentinel constants that also have bottom 2 bits == 10. This avoids

202+

* heap allocation for these common values.

206203

*/

207204

#define WORDBOX_FLOAT_ROTATE 3

208-

#define WORDBOX_FLOAT_EXP_MIN (1023 - 255) /* 768 */

209-

#define WORDBOX_FLOAT_EXP_MAX (1023 + 256) /* 1279 */

210-

#define WORDBOX_FLOAT_ADDEND ((uint64_t)(WORDBOX_FLOAT_EXP_MIN - (WORDBOX_FLOAT_FLAG << 9)) << 52)

211205212206

/* sentinel values for special floats (all have & 3 == 2) */

213207

#define WORDBOX_FLOAT_PZERO 0x02 /* +0.0 */

@@ -217,6 +211,52 @@ mrb_obj_id(mrb_value obj)

217211

#define WORDBOX_FLOAT_NAN 0x12 /* NaN (all NaN bit patterns normalize to this) */

218212

#define WORDBOX_FLOAT_SENTINEL_MAX WORDBOX_FLOAT_NAN

219213214+

#if defined(MRB_USE_FLOAT32) && !defined(MRB_64BIT)

215+

/*

216+

* 32-bit + float32 rotation.

217+

*

218+

* Biased exponents [95, 158] (actual [-32, +31]) produce the correct

219+

* tag pattern after rotation, covering values ~2.3e-10 to ~4.3e9.

220+

* Out-of-range floats are heap-allocated as RFloat.

221+

*/

222+

#define WORDBOX_FLOAT32_EXP_MIN 95 /* biased, actual -32 */

223+

#define WORDBOX_FLOAT32_EXP_MAX 158 /* biased, actual +31 */

224+

#define WORDBOX_FLOAT32_ADDEND ((uint32_t)(WORDBOX_FLOAT32_EXP_MIN - (WORDBOX_FLOAT_FLAG << 6)) << 23)

225+226+

static uint32_t

227+

wordbox_rotl32(uint32_t a, int n)

228+

{

229+

return (a << n) | (a >> (32 - n));

230+

}

231+232+

static uint32_t

233+

wordbox_float32_to_u32(float f)

234+

{

235+

union { float f; uint32_t u; } u;

236+

u.f = f;

237+

return u.u;

238+

}

239+240+

static float

241+

wordbox_u32_to_float32(uint32_t v)

242+

{

243+

union { float f; uint32_t u; } u;

244+

u.u = v;

245+

return u.f;

246+

}

247+248+

#else /* 64-bit + float64 */

249+

/*

250+

* 64-bit + float64 rotation.

251+

*

252+

* Biased exponents [768, 1279] (actual [-255, +256]) produce the

253+

* correct tag pattern. Obscure floats whose rotation would collide

254+

* with a sentinel are heap-allocated instead.

255+

*/

256+

#define WORDBOX_FLOAT_EXP_MIN (1023 - 255) /* 768 */

257+

#define WORDBOX_FLOAT_EXP_MAX (1023 + 256) /* 1279 */

258+

#define WORDBOX_FLOAT_ADDEND ((uint64_t)(WORDBOX_FLOAT_EXP_MIN - (WORDBOX_FLOAT_FLAG << 9)) << 52)

259+220260

static uint64_t

221261

wordbox_rotl64(uint64_t a, int n)

222262

{

@@ -238,6 +278,7 @@ wordbox_u64_to_float64(uint64_t v)

238278

u.u = v;

239279

return u.d;

240280

}

281+

#endif /* MRB_USE_FLOAT32 && !MRB_64BIT */

241282

#endif

242283243284

MRB_API mrb_value

@@ -287,9 +328,39 @@ mrb_word_boxing_float_value(mrb_state *mrb, mrb_float f)

287328

}

288329

}

289330

#else

290-

/* 32-bit + float32: truncate bottom 2 bits */

291-

v.f = f;

292-

v.w = (v.w & ~3) | 2;

331+

/* 32-bit + float32: rotation encoding */

332+

{

333+

uint32_t bits = wordbox_float32_to_u32(f);

334+

uint32_t exp = (bits >> 23) & 0xFF;

335+

if (exp == 0) {

336+

/* +0.0 or -0.0 (subnormals also fall here, go to heap) */

337+

if (bits == 0u)

338+

v.w = WORDBOX_FLOAT_PZERO;

339+

else if (bits == 0x80000000u)

340+

v.w = WORDBOX_FLOAT_NZERO;

341+

else goto float_heap;

342+

}

343+

else if (exp == 0xFF) {

344+

/* +Inf, -Inf, or NaN */

345+

if (bits == 0x7F800000u)

346+

v.w = WORDBOX_FLOAT_PINF;

347+

else if (bits == 0xFF800000u)

348+

v.w = WORDBOX_FLOAT_NINF;

349+

else

350+

v.w = WORDBOX_FLOAT_NAN;

351+

}

352+

else if (exp >= WORDBOX_FLOAT32_EXP_MIN && exp <= WORDBOX_FLOAT32_EXP_MAX) {

353+

uintptr_t w = (uintptr_t)wordbox_rotl32(bits - WORDBOX_FLOAT32_ADDEND, WORDBOX_FLOAT_ROTATE);

354+

if (w <= WORDBOX_FLOAT_SENTINEL_MAX) goto float_heap;

355+

v.w = w;

356+

}

357+

else {

358+

float_heap:

359+

v.p = mrb_obj_alloc(mrb, MRB_TT_FLOAT, mrb->float_class);

360+

v.fp->f = f;

361+

v.bp->frozen = 1;

362+

}

363+

}

293364

#endif

294365

return v.value;

295366

}

@@ -298,9 +369,9 @@ mrb_word_boxing_float_value(mrb_state *mrb, mrb_float f)

298369

#ifndef MRB_WORDBOX_NO_FLOAT_TRUNCATE

299370

/*

300371

* Unboxes an `mrb_value` to an `mrb_float`.

301-

* - If `MRB_USE_FLOAT32`: right-shift by 2 to retrieve the float.

302-

* - Otherwise (rotation encoding): decode inline floats via rotation,

303-

* or read from heap RFloat for edge cases.

372+

* - 64-bit + float32: right-shift by 2 to retrieve the float.

373+

* - 64-bit + float64 / 32-bit + float32 (rotation encoding):

374+

* decode inline floats via rotation, or read from heap RFloat.

304375

*/

305376

MRB_API mrb_float

306377

mrb_word_boxing_value_float(mrb_value v)

@@ -331,11 +402,26 @@ mrb_word_boxing_value_float(mrb_value v)

331402

return u.fp->f;

332403

}

333404

#else

334-

/* 32-bit + float32: clear tag bits */

335-

union mrb_value_ u;

336-

u.value = v;

337-

u.w &= ~3;

338-

return u.f;

405+

/* 32-bit + float32: rotation decoding */

406+

if ((v.w & WORDBOX_FLOAT_MASK) == WORDBOX_FLOAT_FLAG) {

407+

if (v.w <= WORDBOX_FLOAT_SENTINEL_MAX) {

408+

switch (v.w) {

409+

case WORDBOX_FLOAT_PZERO: return (mrb_float)( 0.0f);

410+

case WORDBOX_FLOAT_NZERO: return (mrb_float)(-0.0f);

411+

case WORDBOX_FLOAT_PINF: return (mrb_float)( INFINITY);

412+

case WORDBOX_FLOAT_NINF: return (mrb_float)(-INFINITY);

413+

case WORDBOX_FLOAT_NAN: return (mrb_float) NAN;

414+

default: break; /* not reached */

415+

}

416+

}

417+

return (mrb_float)wordbox_u32_to_float32(

418+

wordbox_rotl32((uint32_t)v.w, 32 - WORDBOX_FLOAT_ROTATE) + WORDBOX_FLOAT32_ADDEND);

419+

}

420+

else {

421+

union mrb_value_ u;

422+

u.value = v;

423+

return u.fp->f;

424+

}

339425

#endif

340426

}

341427

#endif

Read the original on github.com ↗