]> code.delx.au - gnu-emacs/blobdiff - src/floatfns.c
(FLOAT_TO_INT, FLOAT_TO_INT2, range_error2): New macros.
[gnu-emacs] / src / floatfns.c
index 5cc499979f8f8592448670ab0d0d2de7223c6057..145cae047419483b1d65f2bc947f7d1963f39418 100644 (file)
@@ -53,6 +53,16 @@ Lisp_Object Qarith_error;
 
 #ifdef LISP_FLOAT_TYPE
 
+#if 0 /* That is untrue--XINT is used below, and it uses INTBITS.
+        What in the world is values.h, anyway?  */
+#ifdef MSDOS
+/* These are redefined in <values.h> and not used here */
+#undef INTBITS
+#undef LONGBITS
+#undef SHORTBITS
+#endif
+#endif
+
 /* Work around a problem that happens because math.h on hpux 7
    defines two static variables--which, in Emacs, are not really static,
    because `static' is defined as nothing.  The problem is that they are
@@ -65,17 +75,19 @@ Lisp_Object Qarith_error;
 
 #include <math.h>
 
-#ifndef hpux
-/* These declarations are omitted on some systems, like Ultrix.  */
+/* This declaration is omitted on some systems, like Ultrix.  */
+#if !defined (hpux) && defined (HAVE_LOGB)
 extern double logb ();
-#endif
+#endif /* !hpux && HAVE_LOGB */
 
+#ifndef MSDOS
 #if defined(DOMAIN) && defined(SING) && defined(OVERFLOW)
     /* If those are defined, then this is probably a `matherr' machine. */
 # ifndef HAVE_MATHERR
 #  define HAVE_MATHERR
 # endif
 #endif
+#endif
 
 #ifdef NO_MATHERR
 #undef HAVE_MATHERR
@@ -168,14 +180,37 @@ static char *float_error_fn_name;
 #define IN_FLOAT2(d, name, num, num2) (in_float = 1, (d), in_float = 0)
 #endif
 
+/* Convert float to Lisp_Int if it fits, else signal a range error
+   using the given arguments.  */
+#define FLOAT_TO_INT(x, i, name, num)                                  \
+  do                                                                   \
+    {                                                                  \
+      if ((x) >= (1 << (VALBITS-1)) || (x) <= - (1 << (VALBITS-1)) - 1)        \
+       range_error (name, num);                                        \
+      XSET (i, Lisp_Int,  (int)(x));                                   \
+    }                                                                  \
+  while (0)
+#define FLOAT_TO_INT2(x, i, name, num1, num2)                          \
+  do                                                                   \
+    {                                                                  \
+      if ((x) >= (1 << (VALBITS-1)) || (x) <= - (1 << (VALBITS-1)) - 1)        \
+       range_error2 (name, num1, num2);                                \
+      XSET (i, Lisp_Int,  (int)(x));                                   \
+    }                                                                  \
+  while (0)
+
 #define arith_error(op,arg) \
   Fsignal (Qarith_error, Fcons (build_string ((op)), Fcons ((arg), Qnil)))
 #define range_error(op,arg) \
   Fsignal (Qrange_error, Fcons (build_string ((op)), Fcons ((arg), Qnil)))
+#define range_error2(op,a1,a2) \
+  Fsignal (Qrange_error, Fcons (build_string ((op)), \
+                               Fcons ((a1), Fcons ((a2), Qnil))))
 #define domain_error(op,arg) \
   Fsignal (Qdomain_error, Fcons (build_string ((op)), Fcons ((arg), Qnil)))
 #define domain_error2(op,a1,a2) \
-  Fsignal (Qdomain_error, Fcons (build_string ((op)), Fcons ((a1), Fcons ((a2), Qnil))))
+  Fsignal (Qdomain_error, Fcons (build_string ((op)), \
+                                Fcons ((a1), Fcons ((a2), Qnil))))
 
 /* Extract a Lisp number as a `double', or signal an error.  */
 
@@ -433,7 +468,6 @@ DEFUN ("expt", Fexpt, Sexpt, 2, 2, 0,
        }
       else
        {
-         for (; y > 0; y--)
          while (y > 0)
            {
              if (y & 1)
@@ -645,23 +679,40 @@ This is the same as the exponent of a float.")
   int value;
   double f = extract_float (arg);
 
+  if (f == 0.0)
+    value = -(VALMASK >> 1);
+  else
+    {
 #ifdef HAVE_LOGB
-  IN_FLOAT (value = logb (f), "logb", arg);
-  XSET (val, Lisp_Int, value);
+      IN_FLOAT (value = logb (f), "logb", arg);
 #else
 #ifdef HAVE_FREXP
-  {
-    int exp;  
-
-    IN_FLOAT (frexp (f, &exp), "logb", arg);
-    XSET (val, Lisp_Int, exp-1);
-  }
+      IN_FLOAT (frexp (f, &value), "logb", arg);
+      value--;
 #else
-  /* Would someone like to write code to emulate logb?  */
-  error ("`logb' not implemented on this operating system");
+      int i;
+      double d;
+      if (f < 0.0)
+       f = -f;
+      value = -1;
+      while (f < 0.5)
+       {
+         for (i = 1, d = 0.5; d * d >= f; i += i)
+           d *= d;
+         f /= d;
+         value -= i;
+       }
+      while (f >= 1.0)
+       {
+         for (i = 1, d = 2.0; d * d <= f; i += i)
+           d *= d;
+         f /= d;
+         value += i;
+       }
 #endif
 #endif
-
+    }
+  XSET (val, Lisp_Int, value);
   return val;
 }
 
@@ -675,7 +726,12 @@ DEFUN ("ceiling", Fceiling, Sceiling, 1, 1, 0,
   CHECK_NUMBER_OR_FLOAT (arg, 0);
 
   if (XTYPE (arg) == Lisp_Float)
-    IN_FLOAT (XSET (arg, Lisp_Int, ceil (XFLOAT (arg)->data)), "ceiling", arg);
+    {
+      double d;
+
+      IN_FLOAT (d = ceil (XFLOAT (arg)->data), "ceiling", arg);
+      FLOAT_TO_INT (d, arg, "ceiling", arg);
+    }
 
   return arg;
 }
@@ -708,8 +764,8 @@ With optional DIVISOR, return the largest integer no greater than ARG/DIVISOR.")
          if (f2 == 0)
            Fsignal (Qarith_error, Qnil);
 
-         IN_FLOAT2 (XSET (arg, Lisp_Int, floor (f1 / f2)),
-                    "floor", arg, divisor);
+         IN_FLOAT2 (f1 = floor (f1 / f2), "floor", arg, divisor);
+         FLOAT_TO_INT2 (f1, arg, "floor", arg, divisor);
          return arg;
        }
 #endif
@@ -732,7 +788,11 @@ With optional DIVISOR, return the largest integer no greater than ARG/DIVISOR.")
 
 #ifdef LISP_FLOAT_TYPE
   if (XTYPE (arg) == Lisp_Float)
-    IN_FLOAT (XSET (arg, Lisp_Int, floor (XFLOAT (arg)->data)), "floor", arg);
+    {
+      double d;
+      IN_FLOAT (d = floor (XFLOAT (arg)->data), "floor", arg);
+      FLOAT_TO_INT (d, arg, "floor", arg);
+    }
 #endif
 
   return arg;
@@ -748,8 +808,13 @@ DEFUN ("round", Fround, Sround, 1, 1, 0,
   CHECK_NUMBER_OR_FLOAT (arg, 0);
 
   if (XTYPE (arg) == Lisp_Float)
-    /* Screw the prevailing rounding mode.  */
-    IN_FLOAT (XSET (arg, Lisp_Int, rint (XFLOAT (arg)->data)), "round", arg);
+    {
+      double d;
+
+      /* Screw the prevailing rounding mode.  */
+      IN_FLOAT (d = rint (XFLOAT (arg)->data), "round", arg);
+      FLOAT_TO_INT (d, arg, "round", arg);
+    }
 
   return arg;
 }
@@ -763,12 +828,16 @@ Rounds the value toward zero.")
   CHECK_NUMBER_OR_FLOAT (arg, 0);
 
   if (XTYPE (arg) == Lisp_Float)
-    XSET (arg, Lisp_Int, (int) XFLOAT (arg)->data);
+    {
+      double d;
+
+      d = XFLOAT (arg)->data;
+      FLOAT_TO_INT (d, arg, "truncate", arg);
+    }
 
   return arg;
 }
 \f
-#if 0
 /* It's not clear these are worth adding.  */
 
 DEFUN ("fceiling", Ffceiling, Sfceiling, 1, 1, 0,
@@ -799,7 +868,7 @@ DEFUN ("fround", Ffround, Sfround, 1, 1, 0,
      register Lisp_Object arg;
 {
   double d = extract_float (arg);
-  IN_FLOAT (d = rint (XFLOAT (arg)->data), "fround", arg);
+  IN_FLOAT (d = rint (d), "fround", arg);
   return make_float (d);
 }
 
@@ -813,10 +882,9 @@ Rounds the value toward zero.")
   if (d >= 0.0)
     IN_FLOAT (d = floor (d), "ftruncate", arg);
   else
-    IN_FLOAT (d = ceil (d), arg);
+    IN_FLOAT (d = ceil (d), "ftruncate", arg);
   return make_float (d);
 }
-#endif
 \f
 #ifdef FLOAT_CATCH_SIGILL
 static SIGTYPE
@@ -918,11 +986,11 @@ syms_of_floatfns ()
   defsubr (&Serfc);
   defsubr (&Slog_gamma);
   defsubr (&Scube_root);
+#endif
   defsubr (&Sfceiling);
   defsubr (&Sffloor);
   defsubr (&Sfround);
   defsubr (&Sftruncate);
-#endif
   defsubr (&Sexp);
   defsubr (&Sexpt);
   defsubr (&Slog);