mpint: Fix misbehaviour on larger-than-mpw integer types.
authorMark Wooding <mdw@distorted.org.uk>
Tue, 4 Apr 2006 16:20:05 +0000 (17:20 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Tue, 16 Jan 2007 22:20:06 +0000 (22:20 +0000)
The old implementation of MP_FROMINT was grievously broken, it turns
out.  Handle positive and negative numbers separately.

mpint.h
tests/mpint

diff --git a/mpint.h b/mpint.h
index 23378cd..e44c39e 100644 (file)
--- a/mpint.h
+++ b/mpint.h
   MP_DEST(_d, _sz, 0);                                                 \
   _d->f &= ~(MP_NEG | MP_UNDEF);                                       \
                                                                        \
-  /* --- Set the sign on the MP --- *                                  \
-   *                                                                   \
-   * If the input integer is *not* negative, then negate it.  This     \
-   * fixes a problem with two's complement machines where the most     \
-   * negative value actually has larger magnitude than the most                \
-   * positive, and hence -TYPE_MIN == TYPE_MIN but TYPE_MIN != 0.  If  \
-   * all the work is carried out on negative numbers there isn't a     \
-   * problem.                                                          \
-   */                                                                  \
-                                                                       \
-  if (_i >= 0)                                                         \
-    _i = -_i;                                                          \
-  else                                                                 \
+  if (_i >= 0) {                                                       \
+    while (_i) {                                                       \
+      if (_o == _sz) {                                                 \
+       _sz <<= 1;                                                      \
+       MP_ENSURE(_d, _sz);                                             \
+      }                                                                        \
+      _d->v[_o++] = MPW(_i);                                           \
+      if (_i < MPW_MAX)                                                        \
+       break;                                                          \
+      else                                                             \
+       _i /= (type)MPW_MAX + 1;                                        \
+    }                                                                  \
+  } else {                                                             \
     _d->f |= MP_NEG;                                                   \
-                                                                       \
-  while (_i) {                                                         \
-    if (_o == _sz) {                                                   \
-      _sz <<= 1;                                                       \
-      MP_ENSURE(_d, _sz);                                              \
+    while (_i) {                                                       \
+      if (_o == _sz) {                                                 \
+       _sz <<= 1;                                                      \
+       MP_ENSURE(_d, _sz);                                             \
+      }                                                                        \
+      _d->v[_o++] = MPW(-_i);                                          \
+      if (_i > -MPW_MAX)                                               \
+       break;                                                          \
+      else                                                             \
+       _i /= (type)MPW_MAX + 1;                                        \
     }                                                                  \
-    _d->v[_o++] = MPW(-_i);                                            \
-                                                                       \
-    /* --- More subtlety --- *                                         \
-     *                                                                 \
-     * Ideally, I'd like to just shift @i@ right by @MPW_BITS@.  But I \
-     * can't, because that might be more than I'm allowed.  I can't    \
-     * divide by @MPW_MAX + 1@ because that might turn out to be zero  \
-     * in my current type, and besides which it's unsigned which messes        \
-     * up all of my negative arithmetic.  So do an explicit test here. \
-     */                                                                        \
-                                                                       \
-    if (_i >= -MPW_MAX)                                                        \
-      break;                                                           \
-    else                                                               \
-      _i /= (type)MPW_MAX + 1;                                         \
   }                                                                    \
+                                                                       \
   _d->vl = _d->v + _o;                                                 \
   (d) = _d;                                                            \
 } while (0)
index c4faa39..49157ba 100644 (file)
@@ -9,6 +9,9 @@ fromuint {
   0 0;
   1 1;
   -5 0xfffffffb;
+  0x7ffff 0x7ffff;
+  0x80000 0x80000;
+  0xfffff 0xfffff;
   0x7fffffff 0x7fffffff;
   0x80000000 0x80000000;               # Bastard torture test
   0xffffffff 0xffffffff;
@@ -18,6 +21,9 @@ fromint {
   0 0;
   1 1;
   -5 -5;
+  0x7ffff 0x7ffff;
+  0x80000 0x80000;
+  0xfffff 0xfffff;
   0x7fffffff 0x7fffffff;
   -0x80000000 -0x80000000;             # Bastard torture test
 }
@@ -26,6 +32,9 @@ touint {
   0 0;
   1 1;
   -5 -5;
+  0x7ffff 0x7ffff;
+  0x80000 0x80000;
+  0xfffff 0xfffff;
   0x7fffffff 0x7fffffff;
   0x80000000 -0x80000000;              # Bastard torture test
   0xffffffff 0xffffffff;
@@ -35,6 +44,9 @@ toint {
   0 0;
   1 1;
   -5 -5;
+  0x7ffff 0x7ffff;
+  0x80000 0x80000;
+  0xfffff 0xfffff;
   0x7fffffff 0x7fffffff;
   -0x80000000 -0x80000000;             # Bastard torture test
 }