changeset 2013:f2fdfc131770

[xemacs-hg @ 2004-04-14 22:50:52 by james] Work around GMP string to number limitations.
author james
date Wed, 14 Apr 2004 22:50:54 +0000
parents dd25e95fdbe1
children 92f7301e4a23
files src/ChangeLog src/data.c src/lread.c src/number.c
diffstat 4 files changed, 81 insertions(+), 14 deletions(-) [+]
line wrap: on
line diff
--- a/src/ChangeLog	Wed Apr 14 21:50:42 2004 +0000
+++ b/src/ChangeLog	Wed Apr 14 22:50:54 2004 +0000
@@ -1,3 +1,10 @@
+2004-04-14  Jerry James  <james@xemacs.org>
+
+	* data.c (Fstring_to_number): Work around limitations of GMP
+	string to number converting functions.
+	* lread.c (read_atom): Comment fix: the behavior is not random.
+	* number.c (string_to_bignum): Ditto.
+
 2004-04-13  Jerry James  <james@xemacs.org>
 
 	* data.c (Fstring_to_number): Skip leading + sign to avoid
--- a/src/data.c	Wed Apr 14 21:50:42 2004 +0000
+++ b/src/data.c	Wed Apr 14 22:50:54 2004 +0000
@@ -1271,7 +1271,10 @@
 #ifdef HAVE_BIGFLOAT
       else
 	{
-	  /* GMP bigfloat_set_string returns random values with initial + */
+	  /* The GMP version of bigfloat_set_string (mpf_set_str) has the
+	     following limitation: if p starts with a '+' sign, it does
+	     nothing; i.e., it leaves its bigfloat argument untouched.
+	     Therefore, move p past any leading '+' signs. */
 	  if (*p == '+')
 	    p++;
 	  bigfloat_set_prec (scratch_bigfloat, bigfloat_get_default_prec ());
@@ -1284,24 +1287,81 @@
 #ifdef HAVE_RATIO
   if (qxestrchr (p, '/') != NULL)
     {
-      /* GMP ratio_set_string returns random values with initial + sign */
+      /* The GMP version of ratio_set_string (mpq_set_str) has the following
+	 limitations:
+	 - If p starts with a '+' sign, it does nothing; i.e., it leaves its
+	   ratio argument untouched.
+	 - If p has a '+' sign after the '/' (e.g., 300/+400), it sets the
+	   numerator from the string, but *leaves the denominator unchanged*.
+         - If p has trailing nonnumeric characters, it sets the numerator from
+           the string, but leaves the denominator unchanged.
+         - If p has more than one '/', (e.g., 1/2/3), then it sets the
+           numerator from the string, but leaves the denominator unchanged.
+
+         Therefore, move p past any leading '+' signs, temporarily drop a null
+         after the numeric characters we are trying to convert, and then put
+         the nulled character back afterward.  I am not going to fix problem
+         #2; just don't write ratios that look like that. */
+      Ibyte *end, save;
+
       if (*p == '+')
 	p++;
+
+      for (end = p;
+	   (*end >= '0' && *end <= '9') ||
+	     (b > 10 && *end >= 'a' && *end <= 'a' + b - 11) ||
+	     (b > 10 && *end >= 'A' && *end <= 'A' + b - 11);
+	   end++);
+      if (*end == '/')
+	for (end++;
+	     (*end >= '0' && *end <= '9') ||
+	       (b > 10 && *end >= 'a' && *end <= 'a' + b - 11) ||
+	       (b > 10 && *end >= 'A' && *end <= 'A' + b - 11);
+	     end++);
+
+      save = *end;
+      *end = '\0';
       ratio_set_string (scratch_ratio, (const char *) p, b);
+      *end = save;
+      ratio_canonicalize (scratch_ratio);
       return make_ratio_rt (scratch_ratio);
     }
 #endif /* HAVE_RATIO */
 
 #ifdef HAVE_BIGNUM
-  /* GMP bignum_set_string returns random values when the string starts with a
-     plus sign */
-  if (*p == '+')
-    p++;
-  /* GMP bignum_set_string returns random values when fed an empty string */
-  if (*p == '\0')
-    return make_int (0);
-  bignum_set_string (scratch_bignum, (const char *) p, b);
-  return Fcanonicalize_number (make_bignum_bg (scratch_bignum));
+  {
+    /* The GMP version of bignum_set_string (mpz_set_str) has the following
+       limitations:
+       - If p starts with a '+' sign, it does nothing; i.e., it leaves its
+         bignum argument untouched.
+       - If p is the empty string, it does nothing.
+       - If p has trailing nonnumeric characters, it does nothing.
+
+       Therefore, move p past any leading '+' signs, temporarily drop a null
+       after the numeric characters we are trying to convert, special case the
+       empty string, and then put the nulled character back afterward. */
+    Ibyte *end, save;
+    Lisp_Object retval;
+
+    if (*p == '+')
+      p++;
+    for (end = p;
+	 (*end >= '0' && *end <= '9') ||
+	   (b > 10 && *end >= 'a' && *end <= 'a' + b - 11) ||
+	   (b > 10 && *end >= 'A' && *end <= 'A' + b - 11);
+	 end++);
+    save = *end;
+    *end = '\0';
+    if (*p == '\0')
+      retval = make_int (0);
+    else
+      {
+	bignum_set_string (scratch_bignum, (const char *) p, b);
+	retval = Fcanonicalize_number (make_bignum_bg (scratch_bignum));
+      }
+    *end = save;
+    return retval;
+  }
 #else
   if (b == 10)
     {
--- a/src/lread.c	Wed Apr 14 21:50:42 2004 +0000
+++ b/src/lread.c	Wed Apr 14 22:50:54 2004 +0000
@@ -1851,7 +1851,7 @@
 #ifdef HAVE_RATIO
       if (isratio_string (read_ptr))
 	{
-	  /* GMP ratio_set_string returns random values with initial + sign */
+	  /* GMP ratio_set_string has no effect with initial + sign */
 	  if (*read_ptr == '+')
 	    read_ptr++;
 	  ratio_set_string (scratch_ratio, read_ptr, 0);
--- a/src/number.c	Wed Apr 14 21:50:42 2004 +0000
+++ b/src/number.c	Wed Apr 14 22:50:54 2004 +0000
@@ -85,10 +85,10 @@
 string_to_bignum (const Ibyte *str, Bytecount len, int base)
 {
   Lisp_Object b = make_bignum (0L);
-  /* GMP bignum_set_string returns random values with initial + sign */
+  /* GMP bignum_set_string has no effect with initial + sign */
   if (*str == '+')
     str++;
-  /* GMP bignum_set_string returns random values when fed an empty string */
+  /* GMP bignum_set_string has no effect when fed an empty string */
   if (*str == '\0')
     return make_int (0);
   return (bignum_set_string (XBIGNUM_DATA (b), (const char *) str, base) < 0)