| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -355,6 +355,15 @@ struct mrb_state { | |||
| 355 | 355 | ||
| 356 | 356 | mrb_gc gc; | |
| 357 | 357 | ||
| 358 | + #if !defined(MRB_NO_FLOAT) && !defined(MRB_WORD_BOXING) | ||
| 359 | + /* Counts the NaNs made so far. A NaN is equal to nothing at all, its own | ||
| 360 | + operand included, so a container searching for one has only the object to | ||
| 361 | + go by; where a Float is a value and not an object, the count is what tells | ||
| 362 | + two of them apart. See the comment above `MRB_NAN_SERIAL_MAX` in | ||
| 363 | + `mruby/value.h`. */ | ||
| 364 | + uint64_t nan_serial; | ||
| 365 | + #endif | ||
| 366 | + | ||
| 358 | 367 | mrb_bool bootstrapping; | |
| 359 | 368 | ||
| 360 | 369 | #ifndef MRB_NO_METHOD_CACHE | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -62,7 +62,14 @@ mrb_nan_boxing_value_float(mrb_value v) | |||
| 62 | 62 | uint64_t uval; \ | |
| 63 | 63 | } float_uint_union; \ | |
| 64 | 64 | if ((v) != (v)) { /* NaN */ \ | |
| 65 | - float_uint_union.uval = 0x7ff8000000000000UL; \ | ||
| 65 | + /* A NaN is equal to nothing at all, its own operand included, so `==` | ||
| 66 | + can never find one and a container searching for the NaN it holds has | ||
| 67 | + only the object to go by. Each NaN takes a count of its own in the | ||
| 68 | + payload, which the tag space leaves free: the tag saying which value | ||
| 69 | + this is begins at bit 48, and everything below is the NaN's to spend. | ||
| 70 | + `mrb_float()` reads any of these back as a NaN. */ \ | ||
| 71 | + float_uint_union.uval = 0x7ff8000000000000UL | \ | ||
| 72 | + (uint64_t)(mrb_nan_serial_next(mrb) & MRB_NAN_SERIAL_MAX); \ | ||
| 66 | 73 | } \ | |
| 67 | 74 | else { \ | |
| 68 | 75 | float_uint_union.fval = (v); \ | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -49,7 +49,17 @@ typedef struct mrb_value { | |||
| 49 | 49 | #define SET_INT_VALUE(mrb,r,n) BOXNO_SET_VALUE(r, MRB_TT_INTEGER, value.i, (n)) | |
| 50 | 50 | #define SET_FIXNUM_VALUE(r,n) BOXNO_SET_VALUE(r, MRB_TT_INTEGER, value.i, (n)) | |
| 51 | 51 | #ifndef MRB_NO_FLOAT | |
| 52 | - #define SET_FLOAT_VALUE(mrb,r,v) BOXNO_SET_VALUE(r, MRB_TT_FLOAT, value.f, (v)) | ||
| 52 | + /* A NaN is equal to nothing at all, its own operand included, so `==` can | ||
| 53 | + never find one and a container searching for the NaN it holds has only the | ||
| 54 | + object to go by. There is no object here, a Float being a value, so each NaN | ||
| 55 | + takes a count of its own in the payload, which no arithmetic reads; | ||
| 56 | + `mrb_obj_eq()` compares what a Float holds bit for bit so that a NaN is the | ||
| 57 | + same object as itself and no other. */ | ||
| 58 | + #define SET_FLOAT_VALUE(mrb,r,v) do { \ | ||
| 59 | + mrb_float boxno_f = (v); \ | ||
| 60 | + if (boxno_f != boxno_f) boxno_f = mrb_nan_serialize(boxno_f, mrb_nan_serial_next(mrb)); \ | ||
| 61 | + BOXNO_SET_VALUE(r, MRB_TT_FLOAT, value.f, boxno_f); \ | ||
| 62 | + } while (0) | ||
| 53 | 63 | #endif | |
| 54 | 64 | #define SET_SYM_VALUE(r,v) BOXNO_SET_VALUE(r, MRB_TT_SYMBOL, value.sym, (v)) | |
| 55 | 65 | #define SET_OBJ_VALUE(r,v) BOXNO_SET_VALUE(r, (((struct RObject*)(v))->tt), value.p, (v)) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -129,7 +129,7 @@ enum mrb_special_consts { | |||
| 129 | 129 | * | |
| 130 | 130 | * 64-bit word with inline float32 (MRB_USE_FLOAT32): | |
| 131 | 131 | * float : ...FFFF FF10 (float32 shifted left by 2) | |
| 132 | - * (other values same as above) | ||
| 132 | + * (other values same as above; a NaN is heap-allocated as RFloat) | ||
| 133 | 133 | * | |
| 134 | 134 | * word boxing without inline float (MRB_WORDBOX_NO_INLINE_FLOAT): | |
| 135 | 135 | * nil : ...0000 0000 (all bits are 0) | |
@@ -211,10 +211,9 @@ mrb_integer_func(mrb_value o) { | |||
| 211 | 211 | #ifndef MRB_NO_FLOAT | |
| 212 | 212 | #ifdef MRB_WORDBOX_NO_INLINE_FLOAT | |
| 213 | 213 | #define mrb_float_p(o) WORDBOX_OBJ_TYPE_P(o, FLOAT) | |
| 214 | - #elif defined(MRB_USE_FLOAT32) && defined(MRB_64BIT) | ||
| 215 | - #define mrb_float_p(o) WORDBOX_SHIFT_VALUE_P(o, FLOAT) | ||
| 216 | 214 | #else | |
| 217 | - /* rotation encoding: most floats inline, edge cases on heap */ | ||
| 215 | + /* most floats inline; a NaN is always on the heap, and the rotation encoding | ||
| 216 | + sends its edge cases there too */ | ||
| 218 | 217 | #define mrb_float_p(o) (WORDBOX_SHIFT_VALUE_P(o, FLOAT) || WORDBOX_OBJ_TYPE_P(o, FLOAT)) | |
| 219 | 218 | #endif | |
| 220 | 219 | #else | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -113,8 +113,63 @@ MRB_API double mrb_float_read(const char *p, char **endp); | |||
| 113 | 113 | #else | |
| 114 | 114 | typedef double mrb_float; | |
| 115 | 115 | #endif | |
| 116 | + | ||
| 117 | + #ifndef MRB_WORD_BOXING | ||
| 118 | + /* A NaN is equal to nothing at all, its own operand included, so `==` can | ||
| 119 | + never find one and a container searching for the NaN it holds has only the | ||
| 120 | + object to go by. Where a Float is a value and not an object, which is every | ||
| 121 | + boxing but `boxing_word.h`, there is no object to be had: what tells one NaN | ||
| 122 | + from another has to be carried by the NaN itself, and the only room for it | ||
| 123 | + is the payload, which no arithmetic reads. Each NaN made takes a count | ||
| 124 | + there. | ||
| 125 | + | ||
| 126 | + The count is given every bit the payload can spare, which is what puts a | ||
| 127 | + wrap out of reach: 51 bits where the boxing leaves that many, 48 where the | ||
| 128 | + nan-boxing tag begins. Two NaNs made that far apart do hold the same bits. | ||
| 129 | + `MRB_USE_FLOAT32` is the one build where the bound is a near one, its | ||
| 130 | + payload being 22 bits and nothing more. `boxing_word.h` needs no count at | ||
| 131 | + all: a NaN is an object there, and two objects are never one. */ | ||
| 132 | + #ifdef MRB_NAN_BOXING | ||
| 133 | + /* bit 48 is where the tag saying which nan-boxed value this is begins */ | ||
| 134 | + # define MRB_NAN_SERIAL_MAX UINT64_C(0xffffffffffff) | ||
| 135 | + #elif defined(MRB_USE_FLOAT32) | ||
| 136 | + typedef uint32_t mrb_float_bits; | ||
| 137 | + # define MRB_NAN_SERIAL_MAX 0x3fffff /* the whole payload under the quiet bit */ | ||
| 138 | + # define MRB_NAN_QUIET_BIT 0x400000 | ||
| 139 | + #else | ||
| 140 | + typedef uint64_t mrb_float_bits; | ||
| 141 | + # define MRB_NAN_SERIAL_MAX UINT64_C(0x7ffffffffffff) | ||
| 142 | + # define MRB_NAN_QUIET_BIT UINT64_C(0x8000000000000) | ||
| 116 | 143 | #endif | |
| 117 | 144 | ||
| 145 | + /* The count lives in `mrb_state`, whose layout is not known here: this header | ||
| 146 | + is where `mrb_float_value()` expands the boxing's own float macro, and that | ||
| 147 | + is above the definition of the struct. */ | ||
| 148 | + MRB_API uint64_t mrb_nan_serial_next(mrb_state *mrb); | ||
| 149 | + | ||
| 150 | + #ifndef MRB_NAN_BOXING | ||
| 151 | + /* `boxing_nan.h` writes the count into a pattern it builds itself, so what | ||
| 152 | + follows is for the boxing that has a whole Float to put it in. */ | ||
| 153 | + static inline mrb_float | ||
| 154 | + mrb_nan_serialize(mrb_float f, uint64_t n) | ||
| 155 | + { | ||
| 156 | + union { mrb_float f; mrb_float_bits u; } x; | ||
| 157 | + | ||
| 158 | + x.f = f; | ||
| 159 | + /* The quiet bit is set as well because that is what keeps the pattern a | ||
| 160 | + NaN. A signaling NaN can carry every bit it has inside the field the | ||
| 161 | + count takes, and clearing those for a count of zero would leave the | ||
| 162 | + exponent alone, which is an infinity. Quieting is what arithmetic reading | ||
| 163 | + such a NaN does with it anyway. */ | ||
| 164 | + x.u = (x.u & ~(mrb_float_bits)MRB_NAN_SERIAL_MAX) | | ||
| 165 | + (mrb_float_bits)MRB_NAN_QUIET_BIT | | ||
| 166 | + (mrb_float_bits)(n & MRB_NAN_SERIAL_MAX); | ||
| 167 | + return x.f; | ||
| 168 | + } | ||
| 169 | + #endif /* MRB_NAN_BOXING */ | ||
| 170 | + #endif /* MRB_WORD_BOXING */ | ||
| 171 | + #endif /* MRB_NO_FLOAT */ | ||
| 172 | + | ||
| 118 | 173 | #if defined _MSC_VER && _MSC_VER < 1900 | |
| 119 | 174 | MRB_API int mrb_msvc_vsnprintf(char *s, size_t n, const char *format, va_list arg); | |
| 120 | 175 | MRB_API int mrb_msvc_snprintf(char *s, size_t n, const char *format, ...); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -86,6 +86,31 @@ def assert_repeated_combination(exp, receiver, *args) | |||
| 86 | 86 | assert_equal [["student", "sam"], ["teacher", "matz"]], b.uniq { |s| s.first } | |
| 87 | 87 | end | |
| 88 | 88 | ||
| 89 | + assert("Array#uniq, Array#- and Array#include? with a NaN") do | ||
| 90 | + # A NaN is equal to no value, its own included, so none of these can find one | ||
| 91 | + # by what it is equal to; they search for the object instead, and every NaN | ||
| 92 | + # made is one of its own, so that two made apart are two objects. | ||
| 93 | + skip unless Object.const_defined?(:Float) | ||
| 94 | + z = [0.0][0] | ||
| 95 | + a = z / z | ||
| 96 | + b = z / z | ||
| 97 | + | ||
| 98 | + assert_equal 1, [a, a].uniq.size | ||
| 99 | + assert_equal 2, [a, b].uniq.size | ||
| 100 | + assert_equal 0, ([a] - [a]).size | ||
| 101 | + assert_equal 1, ([a] - [b]).size | ||
| 102 | + assert_equal 1, ([a] & [a]).size | ||
| 103 | + assert_equal 0, ([a] & [b]).size | ||
| 104 | + assert_true [a].include?(a) | ||
| 105 | + assert_false [a].include?(b) | ||
| 106 | + assert_true [a].member?(a) | ||
| 107 | + | ||
| 108 | + # a Float that is equal to itself is found by what it is equal to | ||
| 109 | + x = z + 1.5 | ||
| 110 | + y = z + 1.5 | ||
| 111 | + assert_true [x].include?(y) | ||
| 112 | + end | ||
| 113 | + | ||
| 89 | 114 | assert("Array#-") do | |
| 90 | 115 | # Test basic functionality | |
| 91 | 116 | a = [1, 2, 3, 1] | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -343,3 +343,17 @@ def each; yield 1; yield 2; yield 2; end | |||
| 343 | 343 | end.new | |
| 344 | 344 | assert_equal 1, one.count(never) | |
| 345 | 345 | end | |
| 346 | + | ||
| 347 | + assert("Array#count with a NaN") do | ||
| 348 | + # A NaN is equal to no value, its own included, so `count` cannot find one by | ||
| 349 | + # what it is equal to; it searches for the object, and every NaN made is one | ||
| 350 | + # of its own, so that two made apart are two objects. | ||
| 351 | + skip unless Object.const_defined?(:Float) | ||
| 352 | + z = [0.0][0] | ||
| 353 | + a = z / z | ||
| 354 | + b = z / z | ||
| 355 | + | ||
| 356 | + assert_equal 1, [a].count(a) | ||
| 357 | + assert_equal 0, [a].count(b) | ||
| 358 | + assert_equal 2, [a, a].count(a) | ||
| 359 | + end | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -162,6 +162,22 @@ def assert_pack tmpl, packed, unpacked | |||
| 162 | 162 | end | |
| 163 | 163 | end | |
| 164 | 164 | ||
| 165 | + assert 'unpack a NaN that signals' do | ||
| 166 | + skip unless Object.const_defined?(:Float) | ||
| 167 | + # A NaN that signals has every bit it is set in the low payload, which is | ||
| 168 | + # where a build that keeps no object for a NaN writes what tells one from | ||
| 169 | + # another. Reading one back has to leave a NaN rather than the infinity an | ||
| 170 | + # empty payload under that exponent would be, and two reads have to leave | ||
| 171 | + # two objects, as they do for a NaN made any other way. | ||
| 172 | + s = "\x01\x00\x00\x00\x00\x00\xf0\x7f" | ||
| 173 | + a = s.unpack1("E") | ||
| 174 | + b = s.unpack1("E") | ||
| 175 | + | ||
| 176 | + assert_predicate(a, :nan?) | ||
| 177 | + assert_predicate(b, :nan?) | ||
| 178 | + assert_false(a.equal?(b)) | ||
| 179 | + end | ||
| 180 | + | ||
| 165 | 181 | assert 'pack/unpack "i"' do | |
| 166 | 182 | int_size = [0].pack('i').size | |
| 167 | 183 | raise "pack('i').size is too small (#{int_size})" if int_size < 2 | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -95,6 +95,18 @@ mrb_obj_to_sym(mrb_state *mrb, mrb_value name) | |||
| 95 | 95 | return 0; /* not reached */ | |
| 96 | 96 | } | |
| 97 | 97 | ||
| 98 | + #if !defined(MRB_NO_FLOAT) && !defined(MRB_WORD_BOXING) | ||
| 99 | + /* | ||
| 100 | + * Hands out the count that tells one NaN from the next. See the comment above | ||
| 101 | + * `MRB_NAN_SERIAL_MAX` in `mruby/value.h`. | ||
| 102 | + */ | ||
| 103 | + MRB_API uint64_t | ||
| 104 | + mrb_nan_serial_next(mrb_state *mrb) | ||
| 105 | + { | ||
| 106 | + return mrb->nan_serial++; | ||
| 107 | + } | ||
| 108 | + #endif | ||
| 109 | + | ||
| 98 | 110 | #if !defined(MRB_NO_FLOAT) && !defined(MRB_NAN_BOXING) | |
| 99 | 111 | static mrb_int | |
| 100 | 112 | mrb_float_id(mrb_float f) | |
@@ -125,7 +137,13 @@ mrb_obj_id(mrb_value obj) | |||
| 125 | 137 | if (mrb_integer_p(obj)) return mrb_integer(obj); | |
| 126 | 138 | #ifndef MRB_NO_FLOAT | |
| 127 | 139 | if (mrb_float_p(obj)) { | |
| 128 | - return mrb_float_id(mrb_float(obj)); | ||
| 140 | + mrb_float f = mrb_float(obj); | ||
| 141 | + /* A NaN is an object of its own here, and two of them hold the same | ||
| 142 | + bits, so what the bits hash to is one id for objects that `equal?` | ||
| 143 | + has already told apart. The object answers for a NaN instead, which | ||
| 144 | + is what the line below does for everything that is not a Float. */ | ||
| 145 | + if (f != f) return (mrb_int)obj.w; | ||
| 146 | + return mrb_float_id(f); | ||
| 129 | 147 | } | |
| 130 | 148 | #endif | |
| 131 | 149 | } | |
@@ -185,6 +203,8 @@ mrb_obj_id(mrb_value obj) | |||
| 185 | 203 | * - 64-bit float64: rotation encoding, lossless for exponents [-255, +256]. | |
| 186 | 204 | * - 32-bit float32: rotation encoding, lossless for exponents [-32, +31]. | |
| 187 | 205 | * Floats outside the inline range are heap-allocated as RFloat. | |
| 206 | + * A NaN is heap-allocated whatever the width, being the one float that has to | ||
| 207 | + * be told from another of the same value. | ||
| 188 | 208 | */ | |
| 189 | 209 | ||
| 190 | 210 | #if !defined(MRB_WORDBOX_NO_INLINE_FLOAT) && \ | |
@@ -197,7 +217,7 @@ mrb_obj_id(mrb_value obj) | |||
| 197 | 217 | * 2 bits == 10 (WORDBOX_FLOAT_FLAG). | |
| 198 | 218 | * Decode: rotl(tagged_value, N-3) + ADDEND recovers the original bits. | |
| 199 | 219 | * | |
| 200 | - * Special values (0.0, -0.0, +Inf, -Inf, NaN) are encoded as small | ||
| 220 | + * Special values (0.0, -0.0, +Inf, -Inf) are encoded as small | ||
| 201 | 221 | * sentinel constants that also have bottom 2 bits == 10. This avoids | |
| 202 | 222 | * heap allocation for these common values. | |
| 203 | 223 | */ | |
@@ -208,8 +228,13 @@ mrb_obj_id(mrb_value obj) | |||
| 208 | 228 | #define WORDBOX_FLOAT_NZERO 0x06 /* -0.0 */ | |
| 209 | 229 | #define WORDBOX_FLOAT_PINF 0x0a /* +Infinity */ | |
| 210 | 230 | #define WORDBOX_FLOAT_NINF 0x0e /* -Infinity */ | |
| 211 | - #define WORDBOX_FLOAT_NAN 0x12 /* NaN (all NaN bit patterns normalize to this) */ | ||
| 212 | - #define WORDBOX_FLOAT_SENTINEL_MAX WORDBOX_FLOAT_NAN | ||
| 231 | + /* A NaN gets no sentinel of its own. It is equal to nothing at all, its own | ||
| 232 | + operand included, so `==` can never find one and a container searching for | ||
| 233 | + the NaN it holds has only the object to go by. A NaN therefore takes the | ||
| 234 | + heap, where the object it is answers for it and two of them are never one. | ||
| 235 | + Every float a sentinel does stand for is equal to itself, so one word does | ||
| 236 | + for all the copies of it there will ever be. */ | ||
| 237 | + #define WORDBOX_FLOAT_SENTINEL_MAX WORDBOX_FLOAT_NINF | ||
| 213 | 238 | ||
| 214 | 239 | #if defined(MRB_USE_FLOAT32) && !defined(MRB_64BIT) | |
| 215 | 240 | /* | |
@@ -291,9 +316,18 @@ mrb_word_boxing_float_value(mrb_state *mrb, mrb_float f) | |||
| 291 | 316 | mrb_rfloat_set(v.fp, f); | |
| 292 | 317 | v.bp->frozen = 1; | |
| 293 | 318 | #elif defined(MRB_64BIT) && defined(MRB_USE_FLOAT32) | |
| 294 | - v.w = 0; | ||
| 295 | - v.f = f; | ||
| 296 | - v.w = (v.w<<2) | 2; | ||
| 319 | + if (f != f) { | ||
| 320 | + /* a NaN is the object it is, and a word holding the whole float has | ||
| 321 | + nothing left to tell one from another with */ | ||
| 322 | + v.p = mrb_obj_alloc(mrb, MRB_TT_FLOAT, mrb->float_class); | ||
| 323 | + mrb_rfloat_set(v.fp, f); | ||
| 324 | + v.bp->frozen = 1; | ||
| 325 | + } | ||
| 326 | + else { | ||
| 327 | + v.w = 0; | ||
| 328 | + v.f = f; | ||
| 329 | + v.w = (v.w<<2) | 2; | ||
| 330 | + } | ||
| 297 | 331 | #elif defined(MRB_64BIT) | |
| 298 | 332 | { | |
| 299 | 333 | uint64_t bits = wordbox_float64_to_u64((double)f); | |
@@ -313,7 +347,7 @@ mrb_word_boxing_float_value(mrb_state *mrb, mrb_float f) | |||
| 313 | 347 | else if (bits == UINT64_C(0xFFF0000000000000)) | |
| 314 | 348 | v.w = WORDBOX_FLOAT_NINF; | |
| 315 | 349 | else | |
| 316 | - v.w = WORDBOX_FLOAT_NAN; | ||
| 350 | + goto float_heap; /* a NaN is the object it is; see the sentinels */ | ||
| 317 | 351 | } | |
| 318 | 352 | else if (exp >= WORDBOX_FLOAT_EXP_MIN && exp <= WORDBOX_FLOAT_EXP_MAX) { | |
| 319 | 353 | uintptr_t w = (uintptr_t)wordbox_rotl64(bits - WORDBOX_FLOAT_ADDEND, WORDBOX_FLOAT_ROTATE); | |
@@ -347,7 +381,7 @@ mrb_word_boxing_float_value(mrb_state *mrb, mrb_float f) | |||
| 347 | 381 | else if (bits == 0xFF800000u) | |
| 348 | 382 | v.w = WORDBOX_FLOAT_NINF; | |
| 349 | 383 | else | |
| 350 | - v.w = WORDBOX_FLOAT_NAN; | ||
| 384 | + goto float_heap; /* a NaN is the object it is; see the sentinels */ | ||
| 351 | 385 | } | |
| 352 | 386 | else if (exp >= WORDBOX_FLOAT32_EXP_MIN && exp <= WORDBOX_FLOAT32_EXP_MAX) { | |
| 353 | 387 | uintptr_t w = (uintptr_t)wordbox_rotl32(bits - WORDBOX_FLOAT32_ADDEND, WORDBOX_FLOAT_ROTATE); | |
@@ -379,8 +413,11 @@ mrb_word_boxing_value_float(mrb_value v) | |||
| 379 | 413 | #if defined(MRB_64BIT) && defined(MRB_USE_FLOAT32) | |
| 380 | 414 | union mrb_value_ u; | |
| 381 | 415 | u.value = v; | |
| 382 | - u.w >>= 2; | ||
| 383 | - return u.f; | ||
| 416 | + if ((v.w & WORDBOX_FLOAT_MASK) == WORDBOX_FLOAT_FLAG) { | ||
| 417 | + u.w >>= 2; | ||
| 418 | + return u.f; | ||
| 419 | + } | ||
| 420 | + return mrb_rfloat_value(u.fp); | ||
| 384 | 421 | #elif defined(MRB_64BIT) | |
| 385 | 422 | if ((v.w & WORDBOX_FLOAT_MASK) == WORDBOX_FLOAT_FLAG) { | |
| 386 | 423 | if (v.w <= WORDBOX_FLOAT_SENTINEL_MAX) { | |
@@ -389,7 +426,6 @@ mrb_word_boxing_value_float(mrb_value v) | |||
| 389 | 426 | case WORDBOX_FLOAT_NZERO: return (mrb_float)(-0.0); | |
| 390 | 427 | case WORDBOX_FLOAT_PINF: return (mrb_float)( INFINITY); | |
| 391 | 428 | case WORDBOX_FLOAT_NINF: return (mrb_float)(-INFINITY); | |
| 392 | - case WORDBOX_FLOAT_NAN: return (mrb_float) NAN; | ||
| 393 | 429 | default: break; /* not reached */ | |
| 394 | 430 | } | |
| 395 | 431 | } | |
@@ -410,7 +446,6 @@ mrb_word_boxing_value_float(mrb_value v) | |||
| 410 | 446 | case WORDBOX_FLOAT_NZERO: return (mrb_float)(-0.0f); | |
| 411 | 447 | case WORDBOX_FLOAT_PINF: return (mrb_float)( INFINITY); | |
| 412 | 448 | case WORDBOX_FLOAT_NINF: return (mrb_float)(-INFINITY); | |
| 413 | - case WORDBOX_FLOAT_NAN: return (mrb_float) NAN; | ||
| 414 | 449 | default: break; /* not reached */ | |
| 415 | 450 | } | |
| 416 | 451 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -410,9 +410,17 @@ obj_eql(mrb_state *mrb, mrb_value a, mrb_value b, struct RHash *h) | |||
| 410 | 410 | return mrb_integer(a) == mrb_integer(b); | |
| 411 | 411 | ||
| 412 | 412 | #ifndef MRB_NO_FLOAT | |
| 413 | - case MRB_TT_FLOAT: | ||
| 413 | + case MRB_TT_FLOAT: { | ||
| 414 | 414 | if (!mrb_float_p(b)) return FALSE; | |
| 415 | - return mrb_float(a) == mrb_float(b); | ||
| 415 | + mrb_float fa = mrb_float(a); | ||
| 416 | + if (fa == mrb_float(b)) return TRUE; | ||
| 417 | + /* A NaN is equal to no value, its own key included, so a Hash handed the | ||
| 418 | + very key it stored would not find it again. The key it holds is that | ||
| 419 | + object, and an object is the same key as itself. Only a key that is not | ||
| 420 | + equal to itself asks, so what an ordinary key pays is the comparison | ||
| 421 | + against the value already in hand. */ | ||
| 422 | + return fa != fa && mrb_obj_eq(mrb, a, b); | ||
| 423 | + } | ||
| 416 | 424 | #endif | |
| 417 | 425 | ||
| 418 | 426 | default: | |
| Back | FazBrowse Home | New Git URL |
0 commit comments