浏览代码

some changes in 'hashfloat' to avoid undefined (in ISO C) numerical
conversions

Roberto Ierusalimschy 10 年之前
父节点
当前提交
e0306e386f
共有 1 个文件被更改,包括 21 次插入8 次删除
  1. 21 8
      ltable.c

+ 21 - 8
ltable.c

@@ -1,5 +1,5 @@
 /*
-** $Id: ltable.c,v 2.100 2015/01/05 13:52:37 roberto Exp roberto $
+** $Id: ltable.c,v 2.101 2015/01/16 16:54:37 roberto Exp roberto $
 ** Lua tables (hash)
 ** See Copyright Notice in lua.h
 */
@@ -96,16 +96,29 @@ static int numisinteger (lua_Number x, lua_Integer *p) {
 
 
 /*
-** hash for floating-point numbers
+** Hash for floating-point numbers.
+** The main computation should be just
+**     n = frepx(n, &i); hash = (n * INT_MAX) + i
+** but there are some numerical subtleties.
+** In a two-complement representation, INT_MAX does not has an exact
+** representation as a float, but INT_MIN does; because the absolute
+** value of 'frexp' is smaller than 1 (unless 'n' is inf/NaN), the
+** absolute value of the product 'frexp * -INT_MIN' is smaller or equal
+** to INT_MAX. Next, the use of 'unsigned int' avoids overflows when
+** adding 'i'; the use of '~u' (instead of '-u') avoids problems with
+** INT_MIN.
 */
 static Node *hashfloat (const Table *t, lua_Number n) {
   int i;
-  n = l_mathop(frexp)(n, &i) * cast_num(INT_MAX - DBL_MAX_EXP);
-  i += cast_int(n);
-  if (i < 0) {
-    if (cast(unsigned int, i) == 0u - i)  /* use unsigned to avoid overflows */
-      i = 0;  /* handle INT_MIN */
-    i = -i;  /* must be a positive value */
+  lua_Integer ni;
+  n = l_mathop(frexp)(n, &i) * -cast_num(INT_MIN);
+  if (!lua_numbertointeger(n, &ni)) {  /* is 'n' inf/-inf/NaN? */
+    lua_assert(luai_numisnan(n) || l_mathop(fabs)(n) == HUGE_VAL);
+    i = 0;
+  }
+  else {  /* normal case */
+    unsigned int u = cast(unsigned int, i) + cast(unsigned int, ni);
+    i = (u <= cast(unsigned int, INT_MAX) ? u : ~u);
   }
   return hashmod(t, i);
 }