changeset 4678:b5e1d4f6b66f

Make #'floor, #'ceiling, #'round, #'truncate conform to Common Lisp. lisp/ChangeLog addition: 2009-08-11 Aidan Kehoe <kehoea@parhasard.net> * cl-extra.el (ceiling*, floor*, round*, truncate*): Implement these in terms of the C functions; mark them as obsolete. (mod*, rem*): Use #'nth-value with the C functions, not #'nth with the CL emulation functions. man/ChangeLog addition: 2009-08-11 Aidan Kehoe <kehoea@parhasard.net> * lispref/numbers.texi (Bigfloat Basics): Correct this documentation (ignoring for the moment that it breaks off in mid-sentence). tests/ChangeLog addition: 2009-08-11 Aidan Kehoe <kehoea@parhasard.net> * automated/lisp-tests.el: Test the new Common Lisp-compatible rounding functions available in C. (generate-rounding-output): Provide a function useful for generating the data for the rounding functions tests. src/ChangeLog addition: 2009-08-11 Aidan Kehoe <kehoea@parhasard.net> * floatfns.c (ROUNDING_CONVERT, CONVERT_WITH_NUMBER_TYPES) (CONVERT_WITHOUT_NUMBER_TYPES, MAYBE_TWO_ARGS_BIGNUM) (MAYBE_ONE_ARG_BIGNUM, MAYBE_TWO_ARGS_RATIO) (MAYBE_ONE_ARG_RATIO, MAYBE_TWO_ARGS_BIGFLOAT) (MAYBE_ONE_ARG_BIGFLOAT, MAYBE_EFF, MAYBE_CHAR_OR_MARKER): New macros, used in the implementation of the rounding functions. (ceiling_two_fixnum, ceiling_two_bignum, ceiling_two_ratio) (ceiling_two_bigfloat, ceiling_one_ratio, ceiling_one_bigfloat) (ceiling_two_float, ceiling_one_float, ceiling_one_mundane_arg) (floor_two_fixnum, floor_two_bignum, floor_two_ratio) (floor_two_bigfloat, floor_one_ratio, floor_one_bigfloat) (floor_two_float, floor_one_mundane_arg, round_two_fixnum) (round_two_bignum_1, round_two_bignum, round_two_ratio) (round_one_bigfloat_1, round_two_bigfloat, round_one_ratio) (round_one_bigfloat, round_two_float, round_one_float) (round_one_mundane_arg, truncate_two_fixnum) (truncate_two_bignum, truncate_two_ratio, truncate_two_bigfloat) (truncate_one_ratio, truncate_one_bigfloat, truncate_two_float) (truncate_one_float, truncate_one_mundane_arg): New functions, used in the implementation of the rounding functions. (Fceiling, Ffloor, Fround, Ftruncate, Ffceiling, Fffloor) (Ffround, Fftruncate): Revise to fully support Common Lisp conventions. This means: -- All functions have optional DIVISOR arguments -- All functions return multiple values; see #'values -- All functions do their arithmetic with the correct number types according to the contamination rules. -- #'round and #'fround always round towards the even number in ambiguous cases. * doprnt.c (emacs_doprnt_1): * number.c (internal_coerce_number): Call Ftruncate with two arguments, not one. * floatfns.c (Ffloat): Correct this, if NUMBER is a bignum. * lisp.h: Declare Ftruncate as taking two arguments. * number.c: Provide scratch_ratio2, init it appropriately. * number.h: Make scratch_ratio2 available. * number.h (BIGFLOAT_ARITH_RETURN): * number.h (BIGFLOAT_ARITH_RETURN1): Correct these functions.
author Aidan Kehoe <kehoea@parhasard.net>
date Tue, 11 Aug 2009 17:59:23 +0100
parents 8f1ee2d15784
children 2c64d2bbb316
files lisp/ChangeLog lisp/cl-compat.el lisp/cl-extra.el man/ChangeLog man/lispref/numbers.texi src/ChangeLog src/bytecode.c src/doprnt.c src/floatfns.c src/lisp.h src/number.c src/number.h tests/ChangeLog tests/automated/lisp-tests.el
diffstat 14 files changed, 2174 insertions(+), 295 deletions(-) [+]
line wrap: on
line diff
--- a/lisp/ChangeLog	Sun Aug 16 20:55:49 2009 +0100
+++ b/lisp/ChangeLog	Tue Aug 11 17:59:23 2009 +0100
@@ -12,6 +12,14 @@
 
 2009-08-11  Aidan Kehoe  <kehoea@parhasard.net>
 
+	* cl-extra.el (ceiling*, floor*, round*, truncate*): 
+	Implement these in terms of the C functions; mark them as
+	obsolete. 
+	(mod*, rem*): Use #'nth-value with the C functions, not #'nth with
+	the CL emulation functions. 
+
+2009-08-11  Aidan Kehoe  <kehoea@parhasard.net>
+
 	* bytecomp.el :
 	Update this file to support full C-level multiple values. This
 	involves:
--- a/lisp/cl-compat.el	Sun Aug 16 20:55:49 2009 +0100
+++ b/lisp/cl-compat.el	Tue Aug 11 17:59:23 2009 +0100
@@ -82,12 +82,11 @@
     (if test-not (not (funcall test-not item elt))
       (funcall (or test 'eql) item elt))))
 
-;;; Rounding functions with old-style multiple value returns.
-
-(defun cl-floor (a &optional b) (values-list (floor* a b)))
-(defun cl-ceiling (a &optional b) (values-list (ceiling* a b)))
-(defun cl-round (a &optional b) (values-list (round* a b)))
-(defun cl-truncate (a &optional b) (values-list (truncate* a b)))
+;; The rounding functions in C now have all the functionality this package
+;; used to:
+(loop
+  for symbol in '(floor ceiling round truncate)
+  do (defalias (intern (format "cl-%s" symbol)) symbol))
 
 (defun safe-idiv (a b)
   (let* ((q (/ (abs a) (abs b)))
--- a/lisp/cl-extra.el	Sun Aug 16 20:55:49 2009 +0100
+++ b/lisp/cl-extra.el	Tue Aug 11 17:59:23 2009 +0100
@@ -394,56 +394,41 @@
 (or (and (fboundp 'expt) (subrp (symbol-function 'expt)))
     (defalias 'expt 'cl-expt))
 
-(defun floor* (x &optional y)
-  "Return a list of the floor of X and the fractional part of X.
-With two arguments, return floor and remainder of their quotient."
-  (let ((q (floor x y)))
-    (list q (- x (if y (* y q) q)))))
-
-(defun ceiling* (x &optional y)
-  "Return a list of the ceiling of X and the fractional part of X.
-With two arguments, return ceiling and remainder of their quotient."
-  (let ((res (floor* x y)))
-    (if (= (car (cdr res)) 0) res
-      (list (1+ (car res)) (- (car (cdr res)) (or y 1))))))
+;; We can't use macrolet in this file; whence the literal macro
+;; definition-and-call:
+((macro . (lambda (&rest symbols)
+   "Make some old CL package truncate and round functions available.
 
-(defun truncate* (x &optional y)
-  "Return a list of the integer part of X and the fractional part of X.
-With two arguments, return truncation and remainder of their quotient."
-  (if (eq (>= x 0) (or (null y) (>= y 0)))
-      (floor* x y) (ceiling* x y)))
-
-(defun round* (x &optional y)
-  "Return a list of X rounded to the nearest integer and the remainder.
-With two arguments, return rounding and remainder of their quotient."
-  (if y
-      (if (and (integerp x) (integerp y))
-	  (let* ((hy (/ y 2))
-		 (res (floor* (+ x hy) y)))
-	    (if (and (= (car (cdr res)) 0)
-		     (= (+ hy hy) y)
-		     (/= (% (car res) 2) 0))
-		(list (1- (car res)) hy)
-	      (list (car res) (- (car (cdr res)) hy))))
-	(let ((q (round (/ x y))))
-	  (list q (- x (* q y)))))
-    (if (integerp x) (list x 0)
-      (let ((q (round x)))
-	(list q (- x q))))))
+These functions are now implemented in C; their Lisp implementations in this
+XEmacs are trivial, so we provide them and mark them obsolete."
+   (let (symbol result)
+     (while symbols
+       (setq symbol (car symbols)
+	     symbols (cdr symbols))
+       (push `(make-obsolete ',(intern (format "%s*" symbol))
+	       ',symbol "21.5.29")
+	     result) 
+       (push
+	`(defun ,(intern (format "%s*" symbol)) (number &optional divisor)
+	  ,(format "See `%s'. This returns a list, not multiple values."
+		   symbol)
+	  (multiple-value-list (,symbol number divisor)))
+	result))
+     (cons 'progn result))))
+ ceiling floor round truncate)
 
 (defun mod* (x y)
   "The remainder of X divided by Y, with the same sign as Y."
-  (nth 1 (floor* x y)))
+  (nth-value 1 (floor x y)))
 
 (defun rem* (x y)
   "The remainder of X divided by Y, with the same sign as X."
-  (nth 1 (truncate* x y)))
+  (nth-value 1 (truncate x y)))
 
 (defun signum (a)
   "Return 1 if A is positive, -1 if negative, 0 if zero."
   (cond ((> a 0) 1) ((< a 0) -1) (t 0)))
 
-
 ;; Random numbers.
 
 (defvar *random-state*)
--- a/man/ChangeLog	Sun Aug 16 20:55:49 2009 +0100
+++ b/man/ChangeLog	Tue Aug 11 17:59:23 2009 +0100
@@ -1,3 +1,9 @@
+2009-08-11  Aidan Kehoe  <kehoea@parhasard.net>
+
+	* lispref/numbers.texi (Bigfloat Basics): 
+	Correct this documentation (ignoring for the moment that it breaks
+	off in mid-sentence).
+
 2009-08-11  Aidan Kehoe  <kehoea@parhasard.net>
 
 	* cl.texi (Organization): 
--- a/man/lispref/numbers.texi	Sun Aug 16 20:55:49 2009 +0100
+++ b/man/lispref/numbers.texi	Tue Aug 11 17:59:23 2009 +0100
@@ -410,7 +410,8 @@
 
 It is possible to make bigfloat the default floating point format by
 setting @code{default-float-precision} to a non-zero value.  Precision
-is given in bits, with a maximum precision of @code{bigfloat-max-prec}.
+is given in bits, with a maximum precision of
+@code{bigfloat-maximum-precision}.
 @c #### is this true?
 Bigfloats are created automatically when a number with yes
 
--- a/src/ChangeLog	Sun Aug 16 20:55:49 2009 +0100
+++ b/src/ChangeLog	Tue Aug 11 17:59:23 2009 +0100
@@ -1,3 +1,50 @@
+2009-08-11  Aidan Kehoe  <kehoea@parhasard.net>
+
+	* floatfns.c (ROUNDING_CONVERT, CONVERT_WITH_NUMBER_TYPES)
+	(CONVERT_WITHOUT_NUMBER_TYPES, MAYBE_TWO_ARGS_BIGNUM)
+	(MAYBE_ONE_ARG_BIGNUM, MAYBE_TWO_ARGS_RATIO)
+	(MAYBE_ONE_ARG_RATIO, MAYBE_TWO_ARGS_BIGFLOAT)
+	(MAYBE_ONE_ARG_BIGFLOAT, MAYBE_EFF, MAYBE_CHAR_OR_MARKER):
+	New macros, used in the implementation of the rounding functions.
+	(ceiling_two_fixnum, ceiling_two_bignum, ceiling_two_ratio)
+	(ceiling_two_bigfloat, ceiling_one_ratio, ceiling_one_bigfloat)
+	(ceiling_two_float, ceiling_one_float, ceiling_one_mundane_arg)
+	(floor_two_fixnum, floor_two_bignum, floor_two_ratio)
+	(floor_two_bigfloat, floor_one_ratio, floor_one_bigfloat)
+	(floor_two_float, floor_one_mundane_arg, round_two_fixnum)
+	(round_two_bignum_1, round_two_bignum, round_two_ratio)
+	(round_one_bigfloat_1, round_two_bigfloat, round_one_ratio)
+	(round_one_bigfloat, round_two_float, round_one_float)
+	(round_one_mundane_arg, truncate_two_fixnum)
+	(truncate_two_bignum, truncate_two_ratio, truncate_two_bigfloat)
+	(truncate_one_ratio, truncate_one_bigfloat, truncate_two_float)
+	(truncate_one_float, truncate_one_mundane_arg): 
+	New functions, used in the implementation of the rounding
+	functions. 
+	(Fceiling, Ffloor, Fround, Ftruncate, Ffceiling, Fffloor)
+	(Ffround, Fftruncate): 
+	Revise to fully support Common Lisp conventions. This means:
+	-- All functions have optional DIVISOR arguments
+	-- All functions return multiple values; see #'values
+	-- All functions do their arithmetic with the correct number types
+	according to the contamination rules.
+	-- #'round and #'fround always round towards the even number
+	in ambiguous cases.
+	* doprnt.c (emacs_doprnt_1): 
+	* number.c (internal_coerce_number): 
+	Call Ftruncate with two arguments, not one.
+	* floatfns.c (Ffloat): 
+	Correct this, if NUMBER is a bignum.
+	* lisp.h: 
+	Declare Ftruncate as taking two arguments. 
+	* number.c: 
+	Provide scratch_ratio2, init it appropriately. 
+	* number.h: 
+	Make scratch_ratio2 available.
+	* number.h (BIGFLOAT_ARITH_RETURN):
+	* number.h (BIGFLOAT_ARITH_RETURN1):
+	Correct these functions.
+
 2009-08-11  Aidan Kehoe  <kehoea@parhasard.net>
 
 	* bytecode.c (enum Opcode /* Byte codes */): 
--- a/src/bytecode.c	Sun Aug 16 20:55:49 2009 +0100
+++ b/src/bytecode.c	Tue Aug 11 17:59:23 2009 +0100
@@ -301,8 +301,8 @@
 #ifdef HAVE_RATIO
   if (RATIOP (obj)) RATIO_ARITH_RETURN (obj, neg);
 #endif
-#ifdef HAVE_BIG_FLOAT
-  if (BIGFLOAT_P (obj)) BIGFLOAT_ARITH_RETURN (obj, neg);
+#ifdef HAVE_BIGFLOAT
+  if (BIGFLOATP (obj)) BIGFLOAT_ARITH_RETURN (obj, neg);
 #endif
 
   obj = wrong_type_argument (Qnumber_char_or_marker_p, obj);
--- a/src/doprnt.c	Sun Aug 16 20:55:49 2009 +0100
+++ b/src/doprnt.c	Tue Aug 11 17:59:23 2009 +0100
@@ -638,7 +638,7 @@
 	      else
 		{
 		  if (FLOATP (obj))
-		    obj = Ftruncate (obj);
+		    obj = Ftruncate (obj, Qnil);
 #ifdef HAVE_BIGFLOAT
 		  else if (BIGFLOATP (obj))
 		    {
--- a/src/floatfns.c	Sun Aug 16 20:55:49 2009 +0100
+++ b/src/floatfns.c	Tue Aug 11 17:59:23 2009 +0100
@@ -769,7 +769,7 @@
     return make_float ((double) XINT (number));
 
 #ifdef HAVE_BIGNUM
-  if (BIGFLOATP (number))
+  if (BIGNUMP (number))
     {
 #ifdef HAVE_BIGFLOAT
       if (ZEROP (Vdefault_float_precision))
@@ -848,347 +848,1602 @@
 #endif /* ! HAVE_LOGB */
 }
 
-DEFUN ("ceiling", Fceiling, 1, 1, 0, /*
-Return the smallest integer no less than NUMBER.  (Round toward +inf.)
-*/
-       (number))
+#ifdef WITH_NUMBER_TYPES
+#define ROUNDING_CONVERT(conversion, return_float)      \
+  CONVERT_WITH_NUMBER_TYPES(conversion, return_float)
+#else
+#define ROUNDING_CONVERT(conversion, return_float)      \
+  CONVERT_WITHOUT_NUMBER_TYPES(conversion, return_float)
+#endif
+
+#define CONVERT_WITH_NUMBER_TYPES(conversion, return_float)     \
+  if (!NILP (divisor))                                          \
+    {                                                           \
+      switch (promote_args (&number, &divisor))                 \
+        {                                                       \
+        case FIXNUM_T:                                          \
+          return conversion##_two_fixnum (number, divisor,      \
+                                          return_float);        \
+          MAYBE_TWO_ARGS_WITH_NUMBER_TYPES (conversion,         \
+                                            BIGNUM,             \
+                                            return_float);      \
+          MAYBE_TWO_ARGS_WITH_NUMBER_TYPES (conversion,         \
+                                            RATIO,              \
+                                            return_float);      \
+          MAYBE_TWO_ARGS_WITH_NUMBER_TYPES (conversion,         \
+                                            BIGFLOAT,           \
+                                            return_float);      \
+          default: /* FLOAT_T */                                \
+            return conversion##_two_float (number,divisor,      \
+                                           return_float);       \
+        }                                                       \
+     }                                                          \
+                                                                \
+  /* Try this first, the arg is probably a float:  */           \
+  if (FLOATP (number))                                          \
+    return conversion##_one_float (number, return_float);       \
+                                                                \
+  MAYBE_ONE_ARG_WITH_NUMBER_TYPES (conversion,                  \
+                                   RATIO, return_float);        \
+  MAYBE_ONE_ARG_WITH_NUMBER_TYPES (conversion,                  \
+                                   BIGFLOAT, return_float);     \
+  return conversion##_one_mundane_arg (number, divisor,         \
+                                       return_float)
+      
+
+#define CONVERT_WITHOUT_NUMBER_TYPES(conversion, return_float)  \
+  if (!NILP (divisor))						\
+    {                                                           \
+      /* The promote_args call if number types are available    \
+         does these conversions, we do them too for symmetry: */\
+      if (CHARP (number))                                       \
+        {                                                       \
+          number = make_int (XCHAR (number));                   \
+        }                                                       \
+      else if (MARKERP (number))				\
+        {                                                       \
+          number = make_int (marker_position (number));         \
+        }                                                       \
+                                                                \
+      if (CHARP (divisor))                                      \
+        {                                                       \
+          divisor = make_int (XCHAR (divisor));                 \
+        }                                                       \
+      else if (MARKERP (divisor))				\
+        {                                                       \
+          divisor = make_int (marker_position (divisor));       \
+        }                                                       \
+                                                                \
+      CHECK_INT_OR_FLOAT (divisor);                             \
+      if (INTP (number) && INTP (divisor))                      \
+        {                                                       \
+          return conversion##_two_fixnum (number, divisor,      \
+                                        return_float);          \
+        }                                                       \
+      else                                                      \
+        {                                                       \
+          return conversion##_two_float (number, divisor,       \
+                                           return_float);       \
+        }                                                       \
+    }                                                           \
+                                                                \
+  /* Try this first, the arg is probably a float:  */           \
+  if (FLOATP (number))                                          \
+    return conversion##_one_float (number, return_float);       \
+                                                                \
+  return conversion##_one_mundane_arg (number, divisor,		\
+				       return_float)		\
+
+#ifdef WITH_NUMBER_TYPES
+
+#ifdef HAVE_BIGNUM
+#define MAYBE_TWO_ARGS_BIGNUM(conversion, return_float)               \
+  case BIGNUM_T:                                                      \
+  return conversion##_two_bignum (number, divisor, return_float)
+
+#define MAYBE_ONE_ARG_BIGNUM(converse, return_float)                \
+  if (BIGNUM_P (number))                                            \
+    return conversion##_one_bignum (number, divisor, return_float) 
+#else
+#define MAYBE_TWO_ARGS_BIGNUM(conversion, return_float)
+#define MAYBE_ONE_ARG_BIGNUM(converse, return_float) 
+#endif
+
+#ifdef HAVE_RATIO 
+#define MAYBE_TWO_ARGS_RATIO(conversion, return_float)          \
+  case RATIO_T:                                                 \
+  return conversion##_two_ratio (number, divisor, return_float)
+
+#define MAYBE_ONE_ARG_RATIO(conversion, return_float)               \
+  if (RATIOP (number))                                              \
+    return conversion##_one_ratio (number, divisor, return_float) 
+#else
+#define MAYBE_TWO_ARGS_RATIO(conversion, return_float)
+#define MAYBE_ONE_ARG_RATIO(converse, return_float) 
+#endif
+
+#ifdef HAVE_BIGFLOAT
+#define MAYBE_TWO_ARGS_BIGFLOAT(conversion, return_float)           \
+  case BIGFLOAT_T:                                                  \
+  return conversion##_two_bigfloat (number, divisor, return_float)
+
+#define MAYBE_ONE_ARG_BIGFLOAT(conversion, return_float)            \
+  if (BIGFLOATP (number))                                           \
+    return conversion##_one_bigfloat (number, divisor, return_float) 
+#else
+#define MAYBE_TWO_ARGS_BIGFLOAT(conversion, return_float)
+#define MAYBE_ONE_ARG_BIGFLOAT(converse, return_float) 
+#endif
+
+#define MAYBE_TWO_ARGS_WITH_NUMBER_TYPES(convers, upcase, return_float) \
+  MAYBE_TWO_ARGS_##upcase(convers, return_float)
+
+#define MAYBE_ONE_ARG_WITH_NUMBER_TYPES(convers, upcase, return_float) \
+  MAYBE_ONE_ARG_##upcase(convers, return_float)
+
+#endif /* WITH_NUMBER_TYPES */
+
+#define MAYBE_EFF(str) (return_float ? "f" str : str)
+
+/* The WITH_NUMBER_TYPES code calls promote_args, which accepts chars and
+   markers as equivalent to ints. This block does the same for
+   single-argument calls. */
+#define MAYBE_CHAR_OR_MARKER(conversion) do {                           \
+  if (CHARP (number))                                                   \
+    {                                                                   \
+      return conversion##_one_mundane_arg (make_int (XCHAR (number)),   \
+                                           divisor, return_float);      \
+    }                                                                   \
+                                                                        \
+  if (MARKERP (number))                                                 \
+    {                                                                   \
+      return conversion##_one_mundane_arg (make_int                     \
+                                           (marker_position(number)),   \
+                                           divisor, return_float);      \
+    }                                                                   \
+  } while (0)
+
+
+/* The guts of the implementations of the various rounding functions: */
+
+static Lisp_Object
+ceiling_two_fixnum (Lisp_Object number, Lisp_Object divisor,
+		    int return_float)
 {
-  if (FLOATP (number))
+  EMACS_INT i1 = XREALINT (number);
+  EMACS_INT i2 = XREALINT (divisor);
+  EMACS_INT i3 = 0, i4 = 0;
+
+  if (i2 == 0)
+    Fsignal (Qarith_error, Qnil);
+
+  /* With C89's integer /, the result is implementation-defined if either
+     operand is negative, so use only nonnegative operands. Here we do
+     basically the opposite of what floor_two_fixnum does, we add one in the
+     non-negative case: */
+
+  /* Make sure we use the same signs for the modulus calculation as for the
+     quotient calculation: */
+  if (i2 < 0)
+    {
+      if (i1 <= 0)
+	{
+	  i3 = -i1 / -i2;
+	  /* Quotient is positive; add one to give the figure for
+	     ceiling. */
+	  if (0 != (-i1 % -i2))
+	    {
+	      ++i3;
+	    }
+	}
+      else
+	{
+	  /* Quotient is negative; no need to add one. */
+	  i3 = -(i1 / -i2);
+	}
+    }
+  else
+    {
+      if (i1 < 0)
+	{
+	  /* Quotient is negative; no need to add one. */
+	  i3 = -(-i1 / i2);
+	}
+      else
+	{
+	  i3 = i1 / i2;
+	  /* Quotient is positive; add one to give the figure for
+	     ceiling. */
+	  if (0 != (i1 % i2))
+	    {
+	      ++i3;
+	    }
+	}
+    }
+
+  i4 = i1 - (i3 * i2);
+
+  if (!return_float)
+    {
+      return values2 (make_int (i3), make_int (i4));
+    }
+
+  return values2 (make_float ((double)i3),
+		  make_int (i4));
+}
+
+#ifdef HAVE_BIGNUM
+static Lisp_Object
+ceiling_two_bignum (Lisp_Object number, Lisp_Object divisor,
+		    int return_float)
+{
+  Lisp_Object res0, res1;
+
+  if (bignum_sign (XBIGNUM_DATA (divisor)) == 0)
+    {
+      Fsignal (Qarith_error, Qnil);
+    }
+
+  bignum_ceil (scratch_bignum, XBIGNUM_DATA (number), XBIGNUM_DATA (divisor));
+
+  res0 = return_float ? make_float (bignum_to_double (scratch_bignum)) :
+	  Fcanonicalize_number (make_bignum_bg (scratch_bignum));
+
+  if (bignum_divisible_p (XBIGNUM_DATA (number), XBIGNUM_DATA (divisor)))
+    {
+      res1 = Qzero;
+    }
+  else
     {
-      double d;
-      IN_FLOAT ((d = ceil (XFLOAT_DATA (number))), "ceiling", number);
-      return (float_to_int (d, "ceiling", number, Qunbound));
+      bignum_mul (scratch_bignum, scratch_bignum, XBIGNUM_DATA (divisor));
+      bignum_sub (scratch_bignum, XBIGNUM_DATA (number), scratch_bignum);
+      res1 = Fcanonicalize_number (make_bignum_bg (scratch_bignum));
+    }
+
+  return values2 (res0, res1);
+}
+#endif /* HAVE_BIGNUM */
+
+#ifdef HAVE_RATIO
+static Lisp_Object
+ceiling_two_ratio (Lisp_Object number, Lisp_Object divisor,
+		   int return_float)
+{
+  Lisp_Object res0, res1;
+
+  if (ratio_sign (XRATIO_DATA (divisor)) == 0)
+    {
+      Fsignal (Qarith_error, Qnil);
+    }
+
+  ratio_div (scratch_ratio, XRATIO_DATA (number), XRATIO_DATA (divisor));
+
+  bignum_ceil (scratch_bignum, ratio_numerator (scratch_ratio),
+	       ratio_denominator (scratch_ratio));
+
+  res0 = return_float ? make_float (bignum_to_double (scratch_bignum)) :
+	  Fcanonicalize_number (make_bignum_bg (scratch_bignum));
+
+  if (bignum_divisible_p (ratio_numerator (scratch_ratio),
+			  ratio_denominator (scratch_ratio)))
+    {
+      res1 = Qzero;
+    }
+  else
+    {
+      ratio_set_bignum (scratch_ratio, scratch_bignum);
+      ratio_mul (scratch_ratio2, scratch_ratio, XRATIO_DATA (divisor));
+      ratio_sub (scratch_ratio, XRATIO_DATA (number), scratch_ratio2);
+      res1 = Fcanonicalize_number (make_ratio_rt (scratch_ratio));
+    }
+
+  return values2 (res0, res1);
+}
+#endif /* HAVE_RATIO */
+
+#ifdef HAVE_BIGFLOAT
+static Lisp_Object
+ceiling_two_bigfloat (Lisp_Object number, Lisp_Object divisor,
+		      int return_float)
+{
+  Lisp_Object res0;
+
+  if (bigfloat_sign (XBIGFLOAT_DATA (divisor)) == 0)
+    {
+      Fsignal (Qarith_error, Qnil);
+    }
+
+  bigfloat_set_prec (scratch_bigfloat, max (XBIGFLOAT_GET_PREC (number),
+					    XBIGFLOAT_GET_PREC (divisor)));
+  bigfloat_div (scratch_bigfloat, XBIGFLOAT_DATA (number),
+		XBIGFLOAT_DATA (divisor));
+  bigfloat_ceil (scratch_bigfloat, scratch_bigfloat);
+
+  if (return_float)
+    {
+      res0 = make_bigfloat_bf (scratch_bigfloat);
+    }
+  else
+    {
+#ifdef HAVE_BIGNUM
+      bignum_set_bigfloat (scratch_bignum, scratch_bigfloat);
+      res0 = Fcanonicalize_number (make_bignum_bg (scratch_bignum));
+#else
+      res0 = make_int ((EMACS_INT) bigfloat_to_long (scratch_bigfloat));
+#endif /* HAVE_BIGNUM */
+    }
+
+  bigfloat_mul (scratch_bigfloat, scratch_bigfloat, XBIGFLOAT_DATA (divisor));
+  bigfloat_sub (scratch_bigfloat, XBIGFLOAT_DATA (number), scratch_bigfloat);
+  return values2 (res0,
+		  Fcanonicalize_number (make_bigfloat_bf (scratch_bigfloat)));
+}
+#endif /* HAVE_BIGFLOAT */
+
+#ifdef HAVE_RATIO
+static Lisp_Object
+ceiling_one_ratio (Lisp_Object number, Lisp_Object UNUSED (divisor),
+		   int return_float)
+{
+  Lisp_Object res0, res1;
+
+  bignum_ceil (scratch_bignum, XRATIO_NUMERATOR (number),
+	       XRATIO_DENOMINATOR (number));
+
+  res0 = return_float ? make_float (bignum_to_double (scratch_bignum)) :
+	  Fcanonicalize_number (make_bignum_bg (scratch_bignum));
+
+  if (bignum_divisible_p (XRATIO_NUMERATOR (number),
+			  XRATIO_DENOMINATOR (number)))
+    {
+      res1 = Qzero;
+    }
+  else
+    {
+      ratio_set_bignum (scratch_ratio2, scratch_bignum);
+      ratio_sub (scratch_ratio, XRATIO_DATA (number), scratch_ratio2);
+      res1 = Fcanonicalize_number (make_ratio_rt (scratch_ratio));
     }
 
+  return values2 (res0, res1);
+}
+#endif /* HAVE_RATIO */
+
+#ifdef HAVE_BIGFLOAT
+static Lisp_Object
+ceiling_one_bigfloat (Lisp_Object number, Lisp_Object UNUSED (divisor),
+		      int return_float)
+{
+  Lisp_Object res0, res1;
+
+  bigfloat_set_prec (scratch_bigfloat, XBIGFLOAT_GET_PREC (number));
+  bigfloat_ceil (scratch_bigfloat, XBIGFLOAT_DATA (number));
+
+  if (return_float)
+    {
+      res0 = make_bigfloat_bf (scratch_bigfloat);
+    }
+  else
+    {
+#ifdef HAVE_BIGNUM
+      bignum_set_bigfloat (scratch_bignum, scratch_bigfloat);
+      res0 = Fcanonicalize_number (make_bignum_bg (scratch_bignum));
+#else
+      res0 = make_int ((EMACS_INT) bigfloat_to_long (scratch_bigfloat));
+#endif /* HAVE_BIGNUM */
+    }
+
+  bigfloat_sub (scratch_bigfloat2, XBIGFLOAT_DATA (number), scratch_bigfloat);
+
+  res1 = make_bigfloat_bf (scratch_bigfloat2);
+  return values2 (res0, res1);
+}
+#endif /* HAVE_BIGFLOAT */
+
+static Lisp_Object
+ceiling_two_float (Lisp_Object number, Lisp_Object divisor,
+		   int return_float)
+{
+  double f1 = extract_float (number);
+  double f2 = extract_float (divisor);
+  double f0, remain;
+  Lisp_Object res0;
+	    
+  if (f2 == 0.0)
+    {
+      Fsignal (Qarith_error, Qnil);
+    }
+	    
+  IN_FLOAT2 (f0 = ceil (f1 / f2), MAYBE_EFF("ceiling"), number, divisor);
+  IN_FLOAT2 (remain = f1 - (f0 * f2), MAYBE_EFF("ceiling"), number, divisor);
+
+  if (return_float)
+    {
+      res0 = make_float(f0);
+    }
+  else
+    {
+      res0 = float_to_int (f0, MAYBE_EFF("ceiling"), number, divisor);
+    }
+
+  return values2 (res0, make_float (remain));
+}
+
+static Lisp_Object
+ceiling_one_float (Lisp_Object number, int return_float)
+{
+  double d, remain;
+  Lisp_Object res0;
+
+  IN_FLOAT ((d = ceil (XFLOAT_DATA (number))), MAYBE_EFF("ceiling"), number);
+  IN_FLOAT ((remain = XFLOAT_DATA (number) - d), MAYBE_EFF("ceiling"), number);
+
+  if (return_float)
+    {
+      res0 = make_float (d);
+    }
+  else
+    {
+      res0 = float_to_int (d, MAYBE_EFF("ceiling"), number, Qunbound);
+    }
+  return values2 (res0, make_float (remain));
+}
+
+EXFUN (Fceiling, 2);
+EXFUN (Ffceiling, 2);
+
+static Lisp_Object
+ceiling_one_mundane_arg (Lisp_Object number, Lisp_Object divisor,
+			 int return_float)
+{
+
+  if (return_float)
+    {
+      if (INTP (number))
+	{
+	  return values2 (make_float ((double) XINT (number)), Qzero);
+	}
+#ifdef HAVE_BIGNUM
+      else if (BIGNUMP (number))
+	{
+	  return values2 (make_float 
+			  (bignum_to_double (XBIGNUM_DATA (number))),
+			  Qzero);
+	}
+#endif
+    }
+  else
+    {
+#ifdef HAVE_BIGNUM
+      if (INTEGERP (number))
+#else
+      if (INTP (number))
+#endif
+	{
+	  return values2 (number, Qzero);
+	}
+    }
+  
+  MAYBE_CHAR_OR_MARKER (ceiling);
+
+  return Ffceiling (wrong_type_argument (Qnumberp, number), divisor);
+}
+
+static Lisp_Object
+floor_two_fixnum (Lisp_Object number, Lisp_Object divisor,
+		  int return_float)
+{
+  EMACS_INT i1 = XREALINT (number);
+  EMACS_INT i2 = XREALINT (divisor);
+  EMACS_INT i3 = 0, i4 = 0;
+  Lisp_Object res0;
+
+  if (i2 == 0)
+    {
+      Fsignal (Qarith_error, Qnil);
+    }
+
+  /* With C89's integer /, the result is implementation-defined if either
+     operand is negative, so use only nonnegative operands. Notice also that
+     we're forcing the quotient of any negative numbers towards minus
+     infinity. */
+  i3 = (i2 < 0
+	? (i1 <= 0  ?  -i1 / -i2  :  -1 - ((i1 - 1) / -i2))
+	: (i1 < 0  ?  -1 - ((-1 - i1) / i2)  :  i1 / i2));
+
+  i4 = i1 - (i3 * i2);
+
+  if (return_float)
+    {
+      res0 = make_float ((double)i3);
+    }
+  else
+    {
+      res0 = make_int (i3);
+    }
+
+  return values2 (res0, make_int (i4));
+}
+
+#ifdef HAVE_BIGNUM
+static Lisp_Object
+floor_two_bignum (Lisp_Object number, Lisp_Object divisor,
+		  int return_float)
+{
+  Lisp_Object res0, res1;
+
+  if (bignum_sign (XBIGNUM_DATA (divisor)) == 0)
+    {
+      Fsignal (Qarith_error, Qnil);
+    }
+
+  bignum_floor (scratch_bignum, XBIGNUM_DATA (number),
+		XBIGNUM_DATA (divisor));
+
+  if (return_float)
+    {
+      res0 = make_float (bignum_to_double (scratch_bignum));
+    }
+  else
+    {
+      res0 = Fcanonicalize_number (make_bignum_bg (scratch_bignum));
+    }
+
+  if (bignum_divisible_p (XBIGNUM_DATA (number), XBIGNUM_DATA (divisor)))
+    {
+      res1 = Qzero;
+    }
+  else
+    {
+      bignum_mul (scratch_bignum, scratch_bignum, XBIGNUM_DATA (divisor));
+      bignum_sub (scratch_bignum, XBIGNUM_DATA (number), scratch_bignum);
+      res1 = Fcanonicalize_number (make_bignum_bg (scratch_bignum));
+    }
+
+  return values2 (res0, res1);
+}
+#endif /* HAVE_BIGNUM */
+
+#ifdef HAVE_RATIO
+static Lisp_Object
+floor_two_ratio (Lisp_Object number, Lisp_Object divisor,
+		 int return_float)
+{
+  Lisp_Object res0, res1;
+
+  if (ratio_sign (XRATIO_DATA (divisor)) == 0)
+    {
+      Fsignal (Qarith_error, Qnil);
+    }
+
+  ratio_div (scratch_ratio, XRATIO_DATA (number), XRATIO_DATA (divisor));
+
+  bignum_floor (scratch_bignum, ratio_numerator (scratch_ratio),
+		ratio_denominator (scratch_ratio));
+
+  res0 = return_float ? make_float (bignum_to_double (scratch_bignum)) :
+	  Fcanonicalize_number (make_bignum_bg (scratch_bignum));
+
+  if (bignum_divisible_p (ratio_numerator (scratch_ratio),
+			  ratio_denominator (scratch_ratio)))
+    {
+      res1 = Qzero;
+    }
+  else
+    {
+      ratio_set_bignum (scratch_ratio, scratch_bignum);
+      ratio_mul (scratch_ratio, scratch_ratio, XRATIO_DATA (divisor));
+      ratio_sub (scratch_ratio, XRATIO_DATA (number), scratch_ratio);
+      res1 = Fcanonicalize_number (make_ratio_rt (scratch_ratio));
+    }
+
+  return values2 (res0, res1);
+}
+#endif /* HAVE_RATIO */
+
+#ifdef HAVE_BIGFLOAT
+static Lisp_Object
+floor_two_bigfloat (Lisp_Object number, Lisp_Object divisor,
+		    int return_float)
+{
+  Lisp_Object res0;
+
+  if (bigfloat_sign (XBIGFLOAT_DATA (divisor)) == 0)
+    {
+      Fsignal (Qarith_error, Qnil);
+    }
+
+  bigfloat_set_prec (scratch_bigfloat, max (XBIGFLOAT_GET_PREC (number),
+					    XBIGFLOAT_GET_PREC (divisor)));
+  bigfloat_div (scratch_bigfloat, XBIGFLOAT_DATA (number),
+		XBIGFLOAT_DATA (divisor));
+  bigfloat_floor (scratch_bigfloat, scratch_bigfloat);
+
+  if (return_float)
+    {
+      res0 = make_bigfloat_bf (scratch_bigfloat);
+    }
+  else
+    {
+#ifdef HAVE_BIGNUM
+      bignum_set_bigfloat (scratch_bignum, scratch_bigfloat);
+      res0 = Fcanonicalize_number (make_bignum_bg (scratch_bignum));
+#else
+      res0 = make_int ((EMACS_INT) bigfloat_to_long (scratch_bigfloat));
+#endif /* HAVE_BIGNUM */
+    }
+
+  bigfloat_mul (scratch_bigfloat2, scratch_bigfloat,
+		XBIGFLOAT_DATA (divisor));
+  bigfloat_sub (scratch_bigfloat, XBIGFLOAT_DATA (number), scratch_bigfloat2);
+
+  return values2 (res0, make_bigfloat_bf (scratch_bigfloat));
+}
+#endif /* HAVE_BIGFLOAT */
+
+#ifdef HAVE_RATIO
+static Lisp_Object
+floor_one_ratio (Lisp_Object number, Lisp_Object UNUSED (divisor),
+		 int return_float)
+{
+  Lisp_Object res0, res1;
+
+  bignum_floor (scratch_bignum, XRATIO_NUMERATOR (number),
+		XRATIO_DENOMINATOR (number));
+
+  res0 = return_float ? make_float (bignum_to_double (scratch_bignum)) :
+	  Fcanonicalize_number (make_bignum_bg (scratch_bignum));
+
+  if (bignum_divisible_p (XRATIO_NUMERATOR (number),
+			  XRATIO_DENOMINATOR (number)))
+    {
+      res1 = Qzero;
+    }
+  else
+    {
+      ratio_set_bignum (scratch_ratio2, scratch_bignum);
+      ratio_sub (scratch_ratio, XRATIO_DATA (number), scratch_ratio2);
+      res1 = Fcanonicalize_number (make_ratio_rt (scratch_ratio));
+    }
+
+  return values2 (res0, res1);
+}
+#endif /* HAVE_RATIO */
+
+#ifdef HAVE_BIGFLOAT
+static Lisp_Object
+floor_one_bigfloat (Lisp_Object number, Lisp_Object UNUSED (divisor),
+		    int return_float)
+{
+  Lisp_Object res0;
+
+  bigfloat_set_prec (scratch_bigfloat, XBIGFLOAT_GET_PREC (number));
+  bigfloat_floor (scratch_bigfloat, XBIGFLOAT_DATA (number));
+
+  if (return_float)
+    {
+      res0 = make_bigfloat_bf (scratch_bigfloat);
+    }
+  else
+    {
+#ifdef HAVE_BIGNUM
+      bignum_set_bigfloat (scratch_bignum, scratch_bigfloat);
+      res0 = Fcanonicalize_number (make_bignum_bg (scratch_bignum));
+#else
+      res0 = make_int ((EMACS_INT) bigfloat_to_long (scratch_bigfloat));
+#endif /* HAVE_BIGNUM */
+    }
+
+  bigfloat_sub (scratch_bigfloat2, XBIGFLOAT_DATA (number), scratch_bigfloat);
+  return values2 (res0, make_bigfloat_bf (scratch_bigfloat2));
+}
+#endif /* HAVE_BIGFLOAT */
+
+static Lisp_Object
+floor_two_float (Lisp_Object number, Lisp_Object divisor,
+		 int return_float)
+{
+  double f1 = extract_float (number);
+  double f2 = extract_float (divisor);
+  double f0, remain;
+	    
+  if (f2 == 0.0)
+    {
+      Fsignal (Qarith_error, Qnil);
+    }
+	    
+  IN_FLOAT2 (f0 = floor (f1 / f2), MAYBE_EFF ("floor"), number, divisor);
+  IN_FLOAT2 (remain = f1 - (f0 * f2), MAYBE_EFF ("floor"), number, divisor);
+
+  if (return_float)
+    {
+      return values2 (make_float (f0), make_float (remain));
+    }
+
+  return values2 (float_to_int (f0, MAYBE_EFF ("floor"), number, divisor),
+		  make_float (remain));
+}
+
+static Lisp_Object
+floor_one_float (Lisp_Object number, int return_float)
+{
+  double d, d1;
+
+  IN_FLOAT ((d = floor (XFLOAT_DATA (number))), MAYBE_EFF ("floor"), number);
+  IN_FLOAT ((d1 = XFLOAT_DATA (number) - d), MAYBE_EFF ("floor"), number);
+
+  if (return_float)
+    {
+      return values2 (make_float (d), make_float (d1));
+    }
+  else
+    {
+      return values2 (float_to_int (d, MAYBE_EFF ("floor"), number, Qunbound),
+                      make_float (d1));
+    }
+}
+
+EXFUN (Ffloor, 2);
+EXFUN (Fffloor, 2);
+
+static Lisp_Object
+floor_one_mundane_arg (Lisp_Object number, Lisp_Object divisor,
+		       int return_float)
+{
 #ifdef HAVE_BIGNUM
   if (INTEGERP (number))
 #else
   if (INTP (number))
 #endif
-    return number;
-
-#ifdef HAVE_RATIO
-  if (RATIOP (number))
     {
-      bignum_ceil (scratch_bignum, XRATIO_NUMERATOR (number),
-		   XRATIO_DENOMINATOR (number));
-      return Fcanonicalize_number (make_bignum_bg (scratch_bignum));
+      if (return_float)
+	{
+	  return values2 (make_float (extract_float (number)), Qzero);
+	}
+      else
+	{
+	  return values2 (number, Qzero);
+	}
     }
-#endif
+
+  MAYBE_CHAR_OR_MARKER (floor);
+
+  if (return_float)
+    {
+      return Fffloor (wrong_type_argument (Qnumberp, number), divisor);
+    }
+  else
+    {
+      return Ffloor (wrong_type_argument (Qnumberp, number), divisor);
+    }
+}
 
-#ifdef HAVE_BIGFLOAT
-  if (BIGFLOATP (number))
+/* Algorithm taken from cl-extra.el, now to be found as cl-round in
+   tests/automated/lisp-tests.el.  */
+static Lisp_Object
+round_two_fixnum (Lisp_Object number, Lisp_Object divisor, 
+		  int return_float)
+{
+  EMACS_INT i1 = XREALINT (number);
+  EMACS_INT i2 = XREALINT (divisor);
+  EMACS_INT i0, hi2, flooring, floored, flsecond;
+
+  if (i2 == 0)
     {
-      bigfloat_set_prec (scratch_bigfloat, XBIGFLOAT_GET_PREC (number));
-      bigfloat_ceil (scratch_bigfloat, XBIGFLOAT_DATA (number));
-#ifdef HAVE_BIGNUM
-      bignum_set_bigfloat (scratch_bignum, scratch_bigfloat);
-      return Fcanonicalize_number (make_bignum_bg (scratch_bignum));
-#else
-      return make_int ((EMACS_INT) bigfloat_to_long (scratch_bigfloat));
-#endif /* HAVE_BIGNUM */
+      Fsignal (Qarith_error, Qnil);
     }
-#endif /* HAVE_BIGFLOAT */
+
+  hi2 = i2 < 0 ? -( -i2 / 2) : i2 / 2;
+
+  flooring = hi2 + i1;
+
+  floored = (i2 < 0
+	? (flooring <= 0  ?  -flooring / -i2  :  -1 - ((flooring - 1) / -i2))
+	: (flooring < 0  ?  -1 - ((-1 - flooring) / i2)  :  flooring / i2));
+
+  flsecond = flooring - (floored * i2);
 
-  return Fceiling (wrong_type_argument (Qnumberp, number));
+  if (0 == flsecond
+      && (i2 == (hi2 + hi2))
+      && (0 != (floored % 2)))
+    {
+      i0 = floored - 1;
+      return values2 (return_float ? make_float ((double)i0) :
+		      make_int (i0), make_int (hi2));
+    }
+  else
+    {
+      return values2 (return_float ? make_float ((double)floored) :
+		      make_int (floored),
+		      make_int (flsecond - hi2));
+    }
 }
 
+#ifdef HAVE_BIGNUM
+static void
+round_two_bignum_1 (bignum number, bignum divisor,
+		    Lisp_Object *res, Lisp_Object *remain)
+{
+  bignum flooring, floored, hi2, flsecond;
 
-DEFUN ("floor", Ffloor, 1, 2, 0, /*
-Return the largest integer no greater than NUMBER.  (Round towards -inf.)
-With optional second argument DIVISOR, return the largest integer no
-greater than NUMBER/DIVISOR.
-*/
-       (number, divisor))
-{
-#ifdef WITH_NUMBER_TYPES
-  CHECK_REAL (number);
-  if (NILP (divisor))
+  if (bignum_divisible_p (number, divisor))
     {
-      if (FLOATP (number))
-	{
-	  double d;
-	  IN_FLOAT ((d = floor (XFLOAT_DATA (number))), "floor", number);
-	  return (float_to_int (d, "floor", number, Qunbound));
-	}
-#ifdef HAVE_RATIO
-      else if (RATIOP (number))
-	{
-	  bignum_floor (scratch_bignum, XRATIO_NUMERATOR (number),
-			XRATIO_DENOMINATOR (number));
-	  return Fcanonicalize_number (make_bignum_bg (scratch_bignum));
-	}
-#endif
-#ifdef HAVE_BIGFLOAT
-      else if (BIGFLOATP (number))
-	{
-	  bigfloat_set_prec (scratch_bigfloat, XBIGFLOAT_GET_PREC (number));
-	  bigfloat_floor (scratch_bigfloat, XBIGFLOAT_DATA (number));
-	  return make_bigfloat_bf (scratch_bigfloat);
-	}
-#endif
-      return number;
+      bignum_div (scratch_bignum, number, divisor);
+      *res = make_bignum_bg (scratch_bignum);
+      *remain = Qzero;
+      return;
+    }
+
+  bignum_set_long (scratch_bignum, 2);
+
+  bignum_div (scratch_bignum2, divisor, scratch_bignum);
+
+  bignum_init (hi2);
+  bignum_set (hi2, scratch_bignum2);
+
+  bignum_add (scratch_bignum, scratch_bignum2, number);
+  bignum_init (flooring);
+  bignum_set (flooring, scratch_bignum);
+
+  bignum_floor (scratch_bignum, flooring, divisor);
+  bignum_init (floored);
+  bignum_set (floored, scratch_bignum);
+
+  bignum_mul (scratch_bignum2, scratch_bignum, divisor);
+  bignum_sub (scratch_bignum, flooring, scratch_bignum2);
+  bignum_init (flsecond);
+  bignum_set (flsecond, scratch_bignum);
+
+  bignum_set_long (scratch_bignum, 2);
+  bignum_mul (scratch_bignum2, scratch_bignum, hi2);
+
+  if (bignum_sign (flsecond) == 0
+      && bignum_eql (divisor, scratch_bignum2)
+      && (1 == bignum_testbit (floored, 0)))
+    {
+      bignum_set_long (scratch_bignum, 1);
+      bignum_sub (floored, floored, scratch_bignum);
+      *res = make_bignum_bg (floored);
+      *remain = make_bignum_bg (hi2);
+    }
+  else
+    {
+      bignum_sub (scratch_bignum, flsecond,
+		  hi2);
+      *res = make_bignum_bg (floored);
+      *remain = make_bignum_bg (scratch_bignum);
+    }
+}
+
+static Lisp_Object
+round_two_bignum (Lisp_Object number, Lisp_Object divisor, 
+		  int return_float)
+{
+  Lisp_Object res0, res1;
+
+  if (bignum_sign (XBIGNUM_DATA (divisor)) == 0)
+    {
+      Fsignal (Qarith_error, Qnil);
+    }
+
+  round_two_bignum_1 (XBIGNUM_DATA (number), XBIGNUM_DATA (divisor),
+		      &res0, &res1);
+
+  if (return_float)
+    {
+      res0 = make_float (bignum_to_double (XBIGNUM_DATA (res0)));
     }
   else
     {
-      CHECK_REAL (divisor);
-      switch (promote_args (&number, &divisor))
-	{
-	case FIXNUM_T:
-	  {
-	    EMACS_INT i1 = XREALINT (number);
-	    EMACS_INT i2 = XREALINT (divisor);
+      res0 = Fcanonicalize_number (res0);
+    }
+
+  return values2 (res0, Fcanonicalize_number (res1));
+}
+#endif /* HAVE_BIGNUM */
 
-	    if (i2 == 0)
-	      Fsignal (Qarith_error, Qnil);
+#ifdef HAVE_RATIO
+static Lisp_Object
+round_two_ratio (Lisp_Object number, Lisp_Object divisor,
+		 int return_float)
+{
+  Lisp_Object res0, res1;
 
-	    /* With C's /, the result is implementation-defined if either
-	       operand is negative, so use only nonnegative operands.  */
-	    i1 = (i2 < 0
-		  ? (i1 <= 0  ?  -i1 / -i2  :  -1 - ((i1 - 1) / -i2))
-		  : (i1 < 0  ?  -1 - ((-1 - i1) / i2)  :  i1 / i2));
+  if (ratio_sign (XRATIO_DATA (divisor)) == 0)
+    {
+      Fsignal (Qarith_error, Qnil);
+    }
 
-	    return make_int (i1);
-	  }
-#ifdef HAVE_BIGNUM
-	case BIGNUM_T:
-	  if (bignum_sign (XBIGNUM_DATA (divisor)) == 0)
-	    Fsignal (Qarith_error, Qnil);
-	  bignum_floor (scratch_bignum, XBIGNUM_DATA (number),
-			XBIGNUM_DATA (divisor));
-	  return Fcanonicalize_number (make_bignum_bg (scratch_bignum));
-#endif
-#ifdef HAVE_RATIO
-	case RATIO_T:
-	  if (ratio_sign (XRATIO_DATA (divisor)) == 0)
-	    Fsignal (Qarith_error, Qnil);
-	  ratio_div (scratch_ratio, XRATIO_DATA (number),
-		     XRATIO_DATA (divisor));
-	  bignum_floor (scratch_bignum, ratio_numerator (scratch_ratio),
-			ratio_denominator (scratch_ratio));
-	  return Fcanonicalize_number (make_bignum_bg (scratch_bignum));
-#endif
+  ratio_div (scratch_ratio, XRATIO_DATA (number), XRATIO_DATA (divisor));
+  
+  round_two_bignum_1 (ratio_numerator (scratch_ratio),
+		      ratio_denominator (scratch_ratio), &res0, &res1);
+
+  if (!ZEROP (res1))
+    {
+      /* The numerator and denominator don't round exactly, calculate a
+	 ratio remainder: */
+      ratio_set_bignum (scratch_ratio2, XBIGNUM_DATA (res0));
+      ratio_mul (scratch_ratio, scratch_ratio2, XRATIO_DATA (divisor));
+      ratio_sub (scratch_ratio, XRATIO_DATA (number), scratch_ratio);
+      
+      res1 = Fcanonicalize_number (make_ratio_rt (scratch_ratio));
+    }
+
+  res0 = return_float ?
+    make_float ((double)bignum_to_double(XBIGNUM_DATA (res0))) :
+    Fcanonicalize_number (res0);
+
+  return values2 (res0, res1);
+}
+#endif /* HAVE_RATIO */
+
 #ifdef HAVE_BIGFLOAT
-	case BIGFLOAT_T:
-	  if (bigfloat_sign (XBIGFLOAT_DATA (divisor)) == 0)
-	    Fsignal (Qarith_error, Qnil);
-	  bigfloat_set_prec (scratch_bigfloat,
-			     max (XBIGFLOAT_GET_PREC (number),
-				  XBIGFLOAT_GET_PREC (divisor)));
-	  bigfloat_div (scratch_bigfloat, XBIGFLOAT_DATA (number),
-			XBIGFLOAT_DATA (divisor));
-	  bigfloat_floor (scratch_bigfloat, scratch_bigfloat);
-	  return make_bigfloat_bf (scratch_bigfloat);
-#endif
-	default: /* FLOAT_T */
+/* This is the logic of emacs_rint above, no more and no less. */
+static Lisp_Object
+round_one_bigfloat_1 (bigfloat number)
+{
+  Lisp_Object res0;
+  unsigned long prec = bigfloat_get_prec (number);
+
+  assert ((bigfloat *)(&number) != (bigfloat *)&scratch_bigfloat
+	  && (bigfloat *)(&number) != (bigfloat *)(&scratch_bigfloat2));
+
+  bigfloat_set_prec (scratch_bigfloat, prec);
+  bigfloat_set_prec (scratch_bigfloat2, prec);
+
+  bigfloat_set_double (scratch_bigfloat, 0.5);
+  bigfloat_add (scratch_bigfloat2, scratch_bigfloat, number);
+  bigfloat_floor (scratch_bigfloat, scratch_bigfloat2);
+  res0 = make_bigfloat_bf (scratch_bigfloat);
+
+  bigfloat_sub (scratch_bigfloat2, scratch_bigfloat, number);
+  bigfloat_abs (scratch_bigfloat, scratch_bigfloat2);
+
+  bigfloat_set_double (scratch_bigfloat2, 0.5);
+
+  do {
+    if (!bigfloat_ge (scratch_bigfloat, scratch_bigfloat2))
+      {
+	break;
+      }
+
+    if (!bigfloat_gt (scratch_bigfloat, scratch_bigfloat2))
+      {
+	bigfloat_set_double (scratch_bigfloat2, 2.0);
+	bigfloat_div (scratch_bigfloat, XBIGFLOAT_DATA (res0),
+		      scratch_bigfloat2);
+	bigfloat_floor (scratch_bigfloat2, scratch_bigfloat);
+	bigfloat_set_double (scratch_bigfloat, 2.0);
+	bigfloat_mul (scratch_bigfloat2, scratch_bigfloat2,
+		      scratch_bigfloat);
+	if (bigfloat_eql (scratch_bigfloat2, XBIGFLOAT_DATA (res0)))
 	  {
-	    double f1 = extract_float (number);
-	    double f2 = extract_float (divisor);
-	    
-	    if (f2 == 0.0)
-	      Fsignal (Qarith_error, Qnil);
-	    
-	    IN_FLOAT2 (f1 = floor (f1 / f2), "floor", number, divisor);
-	    return float_to_int (f1, "floor", number, divisor);
+	    break;
 	  }
-	}
-    }
-#else /* !WITH_NUMBER_TYPES */
-  CHECK_INT_OR_FLOAT (number);
+      }
+
+    if (bigfloat_lt (XBIGFLOAT_DATA (res0), number))
+      {
+	bigfloat_set_double (scratch_bigfloat2, 1.0);
+      }
+    else
+      {
+	bigfloat_set_double (scratch_bigfloat2, -1.0);
+      }
+
+    bigfloat_set (scratch_bigfloat, XBIGFLOAT_DATA (res0));
+
+    bigfloat_add (XBIGFLOAT_DATA (res0), scratch_bigfloat2,
+		  scratch_bigfloat);
 
-  if (! NILP (divisor))
+  } while (0);
+
+  return res0;
+}
+
+static Lisp_Object
+round_two_bigfloat (Lisp_Object number, Lisp_Object divisor,
+		    int return_float)
+{
+  Lisp_Object res0, res1;
+  bigfloat divided;
+
+  unsigned long prec = max (XBIGFLOAT_GET_PREC (number),
+			    XBIGFLOAT_GET_PREC (divisor));
+
+  if (bigfloat_sign (XBIGFLOAT_DATA (divisor)) == 0)
     {
-      EMACS_INT i1, i2;
-
-      CHECK_INT_OR_FLOAT (divisor);
-
-      if (FLOATP (number) || FLOATP (divisor))
-	{
-	  double f1 = extract_float (number);
-	  double f2 = extract_float (divisor);
-
-	  if (f2 == 0)
-	    Fsignal (Qarith_error, Qnil);
-
-	  IN_FLOAT2 (f1 = floor (f1 / f2), "floor", number, divisor);
-	  return float_to_int (f1, "floor", number, divisor);
-	}
-
-      i1 = XINT (number);
-      i2 = XINT (divisor);
-
-      if (i2 == 0)
-	Fsignal (Qarith_error, Qnil);
-
-      /* With C's /, the result is implementation-defined if either operand
-	 is negative, so use only nonnegative operands.  */
-      i1 = (i2 < 0
-	    ? (i1 <= 0  ?  -i1 / -i2  :  -1 - ((i1 - 1) / -i2))
-	    : (i1 < 0  ?  -1 - ((-1 - i1) / i2)  :  i1 / i2));
-
-      return (make_int (i1));
+      Fsignal (Qarith_error, Qnil);
     }
 
-  if (FLOATP (number))
+  bigfloat_init (divided);
+  bigfloat_set_prec (divided, prec);
+
+  bigfloat_div (divided, XBIGFLOAT_DATA (number), XBIGFLOAT_DATA (divisor));
+
+  res0 = round_one_bigfloat_1 (divided);
+
+  bigfloat_set_prec (scratch_bigfloat, prec);
+  bigfloat_set_prec (scratch_bigfloat2, prec);
+            
+  bigfloat_mul (scratch_bigfloat, XBIGFLOAT_DATA (res0),
+		XBIGFLOAT_DATA (divisor));
+  bigfloat_sub (scratch_bigfloat2, XBIGFLOAT_DATA (number),
+		scratch_bigfloat);
+
+  res1 = make_bigfloat_bf (scratch_bigfloat2);
+
+  if (!return_float)
     {
-      double d;
-      IN_FLOAT ((d = floor (XFLOAT_DATA (number))), "floor", number);
-      return (float_to_int (d, "floor", number, Qunbound));
+#ifdef HAVE_BIGNUM
+      bignum_set_bigfloat (scratch_bignum, XBIGFLOAT_DATA (res0));
+      res0 = Fcanonicalize_number (make_bignum_bg (scratch_bignum));
+#else
+      res0 = make_int ((EMACS_INT) bigfloat_to_long (XBIGFLOAT_DATA (res0)));
+#endif /* HAVE_BIGNUM */
+    }
+
+  return values2 (res0, res1);
+}
+#endif /* HAVE_BIGFLOAT */
+
+#ifdef HAVE_RATIO
+static Lisp_Object
+round_one_ratio (Lisp_Object number, Lisp_Object UNUSED (divisor),
+		 int return_float)
+{
+  Lisp_Object res0, res1;
+
+  round_two_bignum_1 (XRATIO_NUMERATOR (number), XRATIO_DENOMINATOR (number),
+		      &res0, &res1);
+
+  if (!ZEROP (res1))
+    {
+      ratio_set_bignum (scratch_ratio2, XBIGNUM_DATA (res0));
+      ratio_sub (scratch_ratio, XRATIO_DATA (number), scratch_ratio2);
+      res1 = Fcanonicalize_number (make_ratio_rt (scratch_ratio));
     }
 
-  return number;
-#endif /* WITH_NUMBER_TYPES */
+  res0 = return_float ?
+    make_float ((double)bignum_to_double(XBIGNUM_DATA (res0))) :
+    Fcanonicalize_number (res0);
+
+  return values2 (res0, res1);
+}
+#endif /* HAVE_RATIO */
+
+#ifdef HAVE_BIGFLOAT
+static Lisp_Object
+round_one_bigfloat (Lisp_Object number, Lisp_Object UNUSED (divisor),
+		    int return_float)
+{
+  Lisp_Object res0 = round_one_bigfloat_1 (XBIGFLOAT_DATA (number));
+  Lisp_Object res1;
+
+  bigfloat_sub (scratch_bigfloat, XBIGFLOAT_DATA (number), 
+		XBIGFLOAT_DATA (res0));
+
+  res1 = make_bigfloat_bf (scratch_bigfloat);
+
+  if (!return_float)
+    {
+#ifdef HAVE_BIGNUM
+      bignum_set_bigfloat (scratch_bignum, XBIGFLOAT_DATA (res0));
+      res0 = Fcanonicalize_number (make_bignum_bg (scratch_bignum));
+#else
+      res0 = make_int ((EMACS_INT) bigfloat_to_long
+		       (XBIGFLOAT_DATA (res0)));
+#endif /* HAVE_BIGNUM */
+    }
+
+  return values2 (res0, res1);
+}
+#endif /* HAVE_BIGFLOAT */
+
+static Lisp_Object
+round_two_float (Lisp_Object number, Lisp_Object divisor,
+		 int return_float)
+{
+  double f1 = extract_float (number);
+  double f2 = extract_float (divisor);
+  double f0, remain;
+	    
+  if (f2 == 0.0)
+    Fsignal (Qarith_error, Qnil);
+
+  IN_FLOAT2 ((f0 = emacs_rint (f1 / f2)), MAYBE_EFF ("round"), number,
+	     divisor); 
+  IN_FLOAT2 (remain = f1 - (f0 * f2), MAYBE_EFF ("round"), number, divisor);
+
+  if (return_float)
+    {
+      return values2 (make_float (f0), make_float (remain));
+    }
+  else
+    {
+      return values2 (float_to_int (f0, MAYBE_EFF("round"), number, divisor),
+		      make_float (remain));
+    }
 }
 
-DEFUN ("round", Fround, 1, 1, 0, /*
-Return the nearest integer to NUMBER.
-*/
-       (number))
+static Lisp_Object
+round_one_float (Lisp_Object number, int return_float)
 {
-  if (FLOATP (number))
+  double d;
+  /* Screw the prevailing rounding mode.  */
+  IN_FLOAT ((d = emacs_rint (XFLOAT_DATA (number))), MAYBE_EFF ("round"),
+    	number);
+
+  if (return_float)
+    {
+      return values2 (make_float (d), make_float (XFLOAT_DATA (number) - d));
+    }
+  else
     {
-      double d;
-      /* Screw the prevailing rounding mode.  */
-      IN_FLOAT ((d = emacs_rint (XFLOAT_DATA (number))), "round", number);
-      return (float_to_int (d, "round", number, Qunbound));
+      return values2 ((float_to_int (d, MAYBE_EFF ("round"), number,
+				     Qunbound)), 
+		      make_float (XFLOAT_DATA (number) - d));
     }
+}
 
+EXFUN (Fround, 2);
+EXFUN (Ffround, 2);
+
+static Lisp_Object
+round_one_mundane_arg (Lisp_Object number, Lisp_Object divisor,
+		       int return_float)
+{
 #ifdef HAVE_BIGNUM
   if (INTEGERP (number))
 #else
   if (INTP (number))
 #endif
-    return number;
-
-#ifdef HAVE_RATIO
-  if (RATIOP (number))
     {
-      if (bignum_divisible_p (XRATIO_NUMERATOR (number),
-			      XRATIO_DENOMINATOR (number)))
+      if (return_float)
 	{
-	  bignum_div (scratch_bignum, XRATIO_NUMERATOR (number),
-		      XRATIO_DENOMINATOR (number));
+	  return values2 (make_float (extract_float (number)), Qzero);
 	}
       else
 	{
-	  bignum_add (scratch_bignum2, XRATIO_NUMERATOR (number),
-		      XRATIO_DENOMINATOR (number));
-	  bignum_div (scratch_bignum, scratch_bignum2,
-		      XRATIO_DENOMINATOR (number));
+	  return values2 (number, Qzero);
 	}
-      return Fcanonicalize_number (make_bignum_bg (scratch_bignum));
+    }
+
+  MAYBE_CHAR_OR_MARKER (round);
+
+  if (return_float)
+    {
+      return Ffround (wrong_type_argument (Qnumberp, number), divisor);  
+    }
+  else
+    {
+      return Fround (wrong_type_argument (Qnumberp, number), divisor);  
+    }
+}
+
+static Lisp_Object
+truncate_two_fixnum (Lisp_Object number, Lisp_Object divisor,
+		     int return_float)
+{
+  EMACS_INT i1 = XREALINT (number);
+  EMACS_INT i2 = XREALINT (divisor);
+  EMACS_INT i0;
+
+  if (i2 == 0)
+    Fsignal (Qarith_error, Qnil);
+
+  /* We're truncating towards zero, so apart from avoiding the C89
+     implementation-defined behaviour with truncation and negative numbers,
+     we don't need to do anything further: */
+  i0 = (i2 < 0
+	? (i1 <= 0  ?  -i1 / -i2  :  -(i1 / -i2))
+	: (i1 < 0  ?  -(-i1 / i2)  :  i1 / i2));
+
+  if (return_float)
+    {
+      return values2 (make_float ((double)i0), make_int (i1 - (i0 * i2)));
+    }
+  else
+    {
+      return values2 (make_int (i0), make_int (i1 - (i0 * i2)));
+    }
+}
+
+#ifdef HAVE_BIGNUM
+static Lisp_Object
+truncate_two_bignum (Lisp_Object number, Lisp_Object divisor,
+		     int return_float)
+{
+  Lisp_Object res0;
+
+  if (bignum_sign (XBIGNUM_DATA (divisor)) == 0)
+    {
+      Fsignal (Qarith_error, Qnil);
     }
+
+  bignum_div (scratch_bignum, XBIGNUM_DATA (number),
+	      XBIGNUM_DATA (divisor));
+
+  if (return_float)
+    {
+      res0 = make_float (bignum_to_double (scratch_bignum));
+    }
+  else
+    {
+      res0 = make_bignum_bg (scratch_bignum);
+    }
+
+  if (bignum_divisible_p (XBIGNUM_DATA (number),
+			  XBIGNUM_DATA (divisor)))
+    {
+      return values2 (Fcanonicalize_number (res0), Qzero);
+    }
+
+  bignum_mul (scratch_bignum2, scratch_bignum, XBIGNUM_DATA (divisor));
+  bignum_sub (scratch_bignum, XBIGNUM_DATA (number), scratch_bignum2);
+
+  return values2 (Fcanonicalize_number (res0),
+		  Fcanonicalize_number (make_bignum_bg (scratch_bignum)));
+}
+#endif /* HAVE_BIGNUM */
+
+#ifdef HAVE_RATIO
+static Lisp_Object
+truncate_two_ratio (Lisp_Object number, Lisp_Object divisor,
+		    int return_float)
+{
+  Lisp_Object res0;
+
+  if (ratio_sign (XRATIO_DATA (divisor)) == 0)
+    {
+      Fsignal (Qarith_error, Qnil);
+    }
+
+  ratio_div (scratch_ratio, XRATIO_DATA (number), XRATIO_DATA (divisor));
+
+  bignum_div (scratch_bignum, ratio_numerator (scratch_ratio),
+	      ratio_denominator (scratch_ratio));
+
+  if (return_float)
+    {
+      res0 = make_float (bignum_to_double (scratch_bignum));
+    }
+  else
+    {
+      res0 = Fcanonicalize_number (make_bignum_bg (scratch_bignum));
+    }
+
+  if (bignum_divisible_p (ratio_numerator (scratch_ratio),
+			  ratio_denominator (scratch_ratio)))
+    {
+      return values2 (res0, Qzero);
+    }
+
+  ratio_set_bignum (scratch_ratio2, scratch_bignum);
+  ratio_mul (scratch_ratio, scratch_ratio2, XRATIO_DATA (divisor));
+  ratio_sub (scratch_ratio2, XRATIO_DATA (number), scratch_ratio);
+
+  return values2 (res0, Fcanonicalize_number (make_ratio_rt(scratch_ratio2)));
+}
 #endif
 
 #ifdef HAVE_BIGFLOAT
-  if (BIGFLOATP (number))
+static Lisp_Object
+truncate_two_bigfloat (Lisp_Object number, Lisp_Object divisor,
+		       int return_float)
+{
+  Lisp_Object res0;
+  unsigned long prec = max (XBIGFLOAT_GET_PREC (number),
+			    XBIGFLOAT_GET_PREC (divisor));
+
+  if (bigfloat_sign (XBIGFLOAT_DATA (divisor)) == 0)
     {
-      unsigned long prec = XBIGFLOAT_GET_PREC (number);
-      bigfloat_set_prec (scratch_bigfloat, prec);
-      bigfloat_set_prec (scratch_bigfloat2, prec);
-      bigfloat_set_double (scratch_bigfloat2,
-			   bigfloat_sign (XBIGFLOAT_DATA (number)) * 0.5);
-      bigfloat_floor (scratch_bigfloat, scratch_bigfloat2);
+      Fsignal (Qarith_error, Qnil);
+    }
+
+  bigfloat_set_prec (scratch_bigfloat, prec);
+  bigfloat_set_prec (scratch_bigfloat2, prec);
+
+  bigfloat_div (scratch_bigfloat, XBIGFLOAT_DATA (number),
+		XBIGFLOAT_DATA (divisor));
+  bigfloat_trunc (scratch_bigfloat, scratch_bigfloat);
+
+  if (return_float)
+    {
+      res0 = make_bigfloat_bf (scratch_bigfloat);
+    }
+  else
+    {
 #ifdef HAVE_BIGNUM
       bignum_set_bigfloat (scratch_bignum, scratch_bigfloat);
-      return Fcanonicalize_number (make_bignum_bg (scratch_bignum));
+      res0 = Fcanonicalize_number (make_bignum_bg (scratch_bignum));
 #else
-      return make_int ((EMACS_INT) bigfloat_to_long (scratch_bigfloat));
+      res0 = make_int ((EMACS_INT) bigfloat_to_long (scratch_bigfloat));
 #endif /* HAVE_BIGNUM */
     }
+            
+  bigfloat_mul (scratch_bigfloat2, scratch_bigfloat, XBIGFLOAT_DATA (divisor));
+  bigfloat_sub (scratch_bigfloat, XBIGFLOAT_DATA (number), scratch_bigfloat2);
+
+  return values2 (res0, make_bigfloat_bf (scratch_bigfloat));
+}
 #endif /* HAVE_BIGFLOAT */
 
-  return Fround (wrong_type_argument (Qnumberp, number));
+#ifdef HAVE_RATIO
+static Lisp_Object
+truncate_one_ratio (Lisp_Object number, Lisp_Object UNUSED (divisor),
+		   int return_float)
+{
+  Lisp_Object res0;
+
+  if (ratio_sign (XRATIO_DATA (number)) == 0)
+    {
+      Fsignal (Qarith_error, Qnil);
+    }
+
+  bignum_div (scratch_bignum, XRATIO_NUMERATOR (number),
+	      XRATIO_DENOMINATOR (number));
+  if (return_float)
+    {
+      res0 = make_float (bignum_to_double (scratch_bignum));
+    }
+  else
+    {
+      res0 = Fcanonicalize_number (make_bignum_bg (scratch_bignum));
+    }
+
+  if (bignum_divisible_p (XRATIO_NUMERATOR (number),
+			  XRATIO_DENOMINATOR (number)))
+    {
+      return values2 (res0, Qzero);
+    }
+
+  ratio_set_bignum (scratch_ratio2, scratch_bignum);
+  ratio_sub (scratch_ratio, XRATIO_DATA (number), scratch_ratio2);
+
+  return values2 (res0, Fcanonicalize_number (make_ratio_rt (scratch_ratio)));
+}
+#endif /* HAVE_RATIO */
+
+#ifdef HAVE_BIGFLOAT
+static Lisp_Object
+truncate_one_bigfloat (Lisp_Object number, Lisp_Object UNUSED (divisor),
+		       int return_float)
+{
+  Lisp_Object res0;
+
+  bigfloat_set_prec (scratch_bigfloat, XBIGFLOAT_GET_PREC (number));
+  bigfloat_set_prec (scratch_bigfloat2, XBIGFLOAT_GET_PREC (number));
+  bigfloat_trunc (scratch_bigfloat, XBIGFLOAT_DATA (number));
+
+  if (return_float)
+    {
+      res0 = make_bigfloat_bf (scratch_bigfloat);
+    }
+  else
+    {
+#ifdef HAVE_BIGNUM
+      bignum_set_bigfloat (scratch_bignum, scratch_bigfloat);
+      res0 = Fcanonicalize_number (make_bignum_bg (scratch_bignum));
+#else
+      res0 = make_int ((EMACS_INT) bigfloat_to_long (scratch_bigfloat));
+#endif /* HAVE_BIGNUM */
+    }
+
+  bigfloat_sub (scratch_bigfloat2, XBIGFLOAT_DATA (number), scratch_bigfloat);
+
+  return
+    values2 (res0, 
+	     Fcanonicalize_number (make_bigfloat_bf (scratch_bigfloat2)));
+}
+#endif /* HAVE_BIGFLOAT */
+
+static Lisp_Object
+truncate_two_float (Lisp_Object number, Lisp_Object divisor,
+		    int return_float)
+{
+  double f1 = extract_float (number);
+  double f2 = extract_float (divisor);
+  double f0, remain;
+  Lisp_Object res0;
+	    
+  if (f2 == 0.0)
+    {
+      Fsignal (Qarith_error, Qnil);
+    }
+
+  res0 = float_to_int (f1 / f2, MAYBE_EFF ("truncate"), number, Qunbound);
+  f0 = extract_float (res0);
+
+  IN_FLOAT2 (remain = f1 - (f0 * f2), MAYBE_EFF ("truncate"), number, divisor);
+
+  if (return_float)
+    {
+      res0 = make_float (f0);
+    }
+
+  return values2 (res0, make_float (remain));
 }
 
-DEFUN ("truncate", Ftruncate, 1, 1, 0, /*
-Truncate a floating point number to an integer.
-Rounds the value toward zero.
-*/
-       (number))
+static Lisp_Object
+truncate_one_float (Lisp_Object number, int return_float)
 {
-  if (FLOATP (number))
-    return float_to_int (XFLOAT_DATA (number), "truncate", number, Qunbound);
+  Lisp_Object res0
+    = float_to_int (XFLOAT_DATA (number), MAYBE_EFF ("truncate"),
+		    number, Qunbound);
+  if (return_float)
+    {
+      res0 = make_float ((double)XINT(res0));
+      return values2 (res0, make_float ((XFLOAT_DATA (number)
+					 - XFLOAT_DATA (res0))));
+    }
+  else
+    {
+      return values2 (res0, make_float (XFLOAT_DATA (number)
+					- XREALINT (res0)));
+    }
+}
 
+EXFUN (Fftruncate, 2);
+
+static Lisp_Object
+truncate_one_mundane_arg (Lisp_Object number, Lisp_Object divisor,
+			  int return_float)
+{
 #ifdef HAVE_BIGNUM
   if (INTEGERP (number))
 #else
   if (INTP (number))
 #endif
-    return number;
+    {
+      if (return_float)
+	{
+	  return values2 (make_float (extract_float (number)), Qzero);
+	}
+      else
+	{
+	  return values2 (number, Qzero);
+	}
+    }
 
-#ifdef HAVE_RATIO
-  if (RATIOP (number))
+  MAYBE_CHAR_OR_MARKER (truncate);
+
+  if (return_float)
+    {
+      return Fftruncate (wrong_type_argument (Qnumberp, number), divisor);
+    }
+  else
     {
-      bignum_div (scratch_bignum, XRATIO_NUMERATOR (number),
-		  XRATIO_DENOMINATOR (number));
-      return Fcanonicalize_number (make_bignum_bg (scratch_bignum));
+      return Ftruncate (wrong_type_argument (Qnumberp, number), divisor);
     }
-#endif
+}
+
+/* Rounding functions that will not necessarily return floats: */
+
+DEFUN ("ceiling", Fceiling, 1, 2, 0, /*
+Return the smallest integer no less than NUMBER.  (Round toward +inf.)
+
+With optional argument DIVISOR, return the smallest integer no less than
+the quotient of NUMBER and DIVISOR. 
+
+This function returns multiple values; see `multiple-value-bind' and
+`multiple-value-call'.  The second returned value is the remainder in the
+calculation, which will be one minus the fractional part of NUMBER if DIVISOR
+is omitted or one.
+*/
+       (number, divisor))
+{
+  ROUNDING_CONVERT(ceiling, 0);
+}
 
-#ifdef HAVE_BIGFLOAT
-  if (BIGFLOATP (number))
-    {
-      bigfloat_set_prec (scratch_bigfloat, XBIGFLOAT_GET_PREC (number));
-      bigfloat_trunc (scratch_bigfloat, XBIGFLOAT_DATA (number));
-#ifdef HAVE_BIGNUM
-      bignum_set_bigfloat (scratch_bignum, scratch_bigfloat);
-      return Fcanonicalize_number (make_bignum_bg (scratch_bignum));
-#else
-      return make_int ((EMACS_INT) bigfloat_to_long (scratch_bigfloat));
-#endif /* HAVE_BIGNUM */
-    }
-#endif /* HAVE_BIGFLOAT */
+DEFUN ("floor", Ffloor, 1, 2, 0, /*
+Return the largest integer no greater than NUMBER.  (Round towards -inf.)
+With optional second argument DIVISOR, return the largest integer no
+greater than the quotient of NUMBER and DIVISOR.
+
+This function returns multiple values; see `multiple-value-call' and
+`multiple-value-bind'.  The second returned value is the remainder in the
+calculation, which will just be the fractional part if DIVISOR is omitted or
+one.
+*/
+       (number, divisor))
+{
+  ROUNDING_CONVERT(floor, 0);
+}
+
+DEFUN ("round", Fround, 1, 2, 0, /*
+Return the nearest integer to NUMBER.
+If NUMBER is exactly halfway between two integers, return the one that
+is even.
 
-  return Ftruncate (wrong_type_argument (Qnumberp, number));
+Optional argument DIVISOR means return the nearest integer to NUMBER
+divided by DIVISOR.
+
+This function returns multiple values; see `multiple-value-call' and
+`multiple-value-bind'.  The second returned value is the remainder
+in the calculation.
+*/
+       (number, divisor))
+{
+  ROUNDING_CONVERT(round, 0);
+}
+
+DEFUN ("truncate", Ftruncate, 1, 2, 0, /*
+Truncate a floating point number to an integer.
+Rounds the value toward zero.
+
+Optional argument DIVISOR means truncate NUMBER divided by DIVISOR.
+
+This function returns multiple values; see `multiple-value-call' and
+`multiple-value-bind'.  The second returned value is the remainder.
+*/
+       (number, divisor))
+{
+  ROUNDING_CONVERT(truncate, 0);
 }
 
 /* Float-rounding functions. */
 
-DEFUN ("fceiling", Ffceiling, 1, 1, 0, /*
+DEFUN ("fceiling", Ffceiling, 1, 2, 0, /*
 Return the smallest integer no less than NUMBER, as a float.
 \(Round toward +inf.\)
+
+With optional argument DIVISOR, return the smallest integer no less than the
+quotient of NUMBER and DIVISOR, as a float.
+
+This function returns multiple values; the second value is the remainder in
+the calculation.
 */
-       (number))
+       (number, divisor))
 {
-  double d = extract_float (number);
-  IN_FLOAT (d = ceil (d), "fceiling", number);
-  return make_float (d);
+  ROUNDING_CONVERT(ceiling, 1);
 }
 
-DEFUN ("ffloor", Fffloor, 1, 1, 0, /*
+DEFUN ("ffloor", Fffloor, 1, 2, 0, /*
 Return the largest integer no greater than NUMBER, as a float.
 \(Round towards -inf.\)
+
+With optional argument DIVISOR, return the largest integer no greater than
+the quotient of NUMBER and DIVISOR, as a float.
+
+This function returns multiple values; the second value is the remainder in
+the calculation.
 */
-       (number))
+       (number, divisor))
 {
-  double d = extract_float (number);
-  IN_FLOAT (d = floor (d), "ffloor", number);
-  return make_float (d);
+  ROUNDING_CONVERT(floor, 1);
 }
 
-DEFUN ("fround", Ffround, 1, 1, 0, /*
+DEFUN ("fround", Ffround, 1, 2, 0, /*
 Return the nearest integer to NUMBER, as a float.
+If NUMBER is exactly halfway between two integers, return the one that is
+even.
+
+With optional argument DIVISOR, return the nearest integer to the quotient
+of NUMBER and DIVISOR, as a float.
+
+This function returns multiple values; the second value is the remainder in
+the calculation.
 */
-       (number))
+       (number, divisor))
 {
-  double d = extract_float (number);
-  IN_FLOAT (d = emacs_rint (d), "fround", number);
-  return make_float (d);
+  ROUNDING_CONVERT(round, 1);
 }
 
-DEFUN ("ftruncate", Fftruncate, 1, 1, 0, /*
+DEFUN ("ftruncate", Fftruncate, 1, 2, 0, /*
 Truncate a floating point number to an integral float value.
 Rounds the value toward zero.
+
+With optional argument DIVISOR, truncate the quotient of NUMBER and DIVISOR,
+to an integral float value.
+
+This function returns multiple values; the second value is the remainder in
+the calculation.
 */
-       (number))
+       (number, divisor))
 {
-  double d = extract_float (number);
-  if (d >= 0.0)
-    IN_FLOAT (d = floor (d), "ftruncate", number);
-  else
-    IN_FLOAT (d = ceil (d), "ftruncate", number);
-  return make_float (d);
+  ROUNDING_CONVERT(truncate, 1);
 }
 
 #ifdef FLOAT_CATCH_SIGILL
--- a/src/lisp.h	Sun Aug 16 20:55:49 2009 +0100
+++ b/src/lisp.h	Tue Aug 11 17:59:23 2009 +0100
@@ -4705,7 +4705,7 @@
 void unlock_buffer (struct buffer *);
 
 /* Defined in floatfns.c */
-EXFUN (Ftruncate, 1);
+EXFUN (Ftruncate, 2);
 
 double extract_float (Lisp_Object);
 
--- a/src/number.c	Sun Aug 16 20:55:49 2009 +0100
+++ b/src/number.c	Tue Aug 11 17:59:23 2009 +0100
@@ -41,7 +41,7 @@
 bignum scratch_bignum, scratch_bignum2;
 #endif
 #ifdef HAVE_RATIO
-ratio scratch_ratio;
+ratio scratch_ratio, scratch_ratio2;
 #endif
 #ifdef HAVE_BIGFLOAT
 bigfloat scratch_bigfloat, scratch_bigfloat2;
@@ -561,7 +561,7 @@
       switch (type)
 	{
 	case FIXNUM_T:
-	  return Ftruncate (number);
+	  return Ftruncate (number, Qnil);
 	case BIGNUM_T:
 #ifdef HAVE_BIGNUM
 	  bignum_set_double (scratch_bignum, XFLOAT_DATA (number));
@@ -853,6 +853,7 @@
 
 #ifdef HAVE_RATIO
       ratio_init (scratch_ratio);
+      ratio_init (scratch_ratio2);
 #endif
 
 #ifdef HAVE_BIGFLOAT
--- a/src/number.h	Sun Aug 16 20:55:49 2009 +0100
+++ b/src/number.h	Tue Aug 11 17:59:23 2009 +0100
@@ -195,7 +195,7 @@
 extern Lisp_Object make_ratio (long, unsigned long);
 extern Lisp_Object make_ratio_bg (bignum, bignum);
 extern Lisp_Object make_ratio_rt (ratio);
-extern ratio scratch_ratio;
+extern ratio scratch_ratio, scratch_ratio2;
 
 #else /* !HAVE_RATIO */
 
@@ -251,16 +251,16 @@
 #define XBIGFLOAT_GET_PREC(x) bigfloat_get_prec (XBIGFLOAT_DATA (x))
 #define XBIGFLOAT_SET_PREC(x,p) bigfloat_set_prec (XBIGFLOAT_DATA (x), p)
 
-#define BIGFLOAT_ARITH_RETURN(f,op) do				\
-{								\
-  Lisp_Object retval = make_bigfloat_bf (f);			\
+#define BIGFLOAT_ARITH_RETURN(f,op) do					\
+{									\
+  Lisp_Object retval = make_bigfloat (0.0, bigfloat_get_default_prec()); \
   bigfloat_##op (XBIGFLOAT_DATA (retval), XBIGFLOAT_DATA (f));	\
   return retval;						\
 } while (0)
 
 #define BIGFLOAT_ARITH_RETURN1(f,op,arg) do				\
 {									\
-  Lisp_Object retval = make_bigfloat_bf (f);				\
+  Lisp_Object retval = make_bigfloat (0.0, bigfloat_get_default_prec()); \
   bigfloat_##op (XBIGFLOAT_DATA (retval), XBIGFLOAT_DATA (f), arg);	\
   return retval;							\
 } while (0)
--- a/tests/ChangeLog	Sun Aug 16 20:55:49 2009 +0100
+++ b/tests/ChangeLog	Tue Aug 11 17:59:23 2009 +0100
@@ -1,3 +1,11 @@
+2009-08-11  Aidan Kehoe  <kehoea@parhasard.net>
+
+	* automated/lisp-tests.el: 
+	Test the new Common Lisp-compatible rounding functions available in
+	C. 
+	(generate-rounding-output): Provide a function useful for
+	generating the data for the rounding functions tests. 
+
 2009-08-10  Aidan Kehoe  <kehoea@parhasard.net>
 
 	* automated/mule-tests.el: 
--- a/tests/automated/lisp-tests.el	Sun Aug 16 20:55:49 2009 +0100
+++ b/tests/automated/lisp-tests.el	Tue Aug 11 17:59:23 2009 +0100
@@ -1368,5 +1368,574 @@
    (load test-file-name nil t nil)
    (delete-file test-file-name))
 
+(flet ((cl-floor (x &optional y)
+	 (let ((q (floor x y)))
+	   (list q (- x (if y (* y q) q)))))
+       (cl-ceiling (x &optional y)
+	 (let ((res (cl-floor x y)))
+	   (if (= (car (cdr res)) 0) res
+	     (list (1+ (car res)) (- (car (cdr res)) (or y 1))))))
+       (cl-truncate (x &optional y)
+	 (if (eq (>= x 0) (or (null y) (>= y 0)))
+	     (cl-floor x y) (cl-ceiling x y)))
+       (cl-round (x &optional y)
+	 (if y
+	     (if (and (integerp x) (integerp y))
+		 (let* ((hy (/ y 2))
+			(res (cl-floor (+ x hy) y)))
+		   (if (and (= (car (cdr res)) 0)
+			    (= (+ hy hy) y)
+			    (/= (% (car res) 2) 0))
+		       (list (1- (car res)) hy)
+		     (list (car res) (- (car (cdr res)) hy))))
+	       (let ((q (round (/ x y))))
+		 (list q (- x (* q y)))))
+	   (if (integerp x) (list x 0)
+	     (let ((q (round x)))
+	       (list q (- x q))))))
+       (Assert-rounding (first second &key
+			 one-floor-result two-floor-result 
+			 one-ffloor-result two-ffloor-result 
+			 one-ceiling-result two-ceiling-result
+			 one-fceiling-result two-fceiling-result
+			 one-round-result two-round-result
+			 one-fround-result two-fround-result
+			 one-truncate-result two-truncate-result
+			 one-ftruncate-result two-ftruncate-result)
+	 (Assert (equal one-floor-result (multiple-value-list
+					  (floor first)))
+		 (format "checking (floor %S) gives %S"
+			 first one-floor-result))
+	 (Assert (equal one-floor-result (multiple-value-list
+					  (floor first 1)))
+		 (format "checking (floor %S 1) gives %S"
+			 first one-floor-result))
+	 (Check-Error arith-error (floor first 0))
+	 (Check-Error arith-error (floor first 0.0))
+	 (Assert (equal two-floor-result (multiple-value-list
+					  (floor first second)))
+		 (format
+		  "checking (floor %S %S) gives %S"
+		  first second two-floor-result))
+	 (Assert (equal (cl-floor first second)
+			(multiple-value-list (floor first second)))
+		 (format
+		  "checking (floor %S %S) gives the same as the old code"
+		  first second))
+	 (Assert (equal one-ffloor-result (multiple-value-list
+					   (ffloor first)))
+		 (format "checking (ffloor %S) gives %S"
+			 first one-ffloor-result))
+	 (Assert (equal one-ffloor-result (multiple-value-list
+					   (ffloor first 1)))
+		 (format "checking (ffloor %S 1) gives %S"
+			 first one-ffloor-result))
+	 (Check-Error arith-error (ffloor first 0))
+	 (Check-Error arith-error (ffloor first 0.0))
+	 (Assert (equal two-ffloor-result (multiple-value-list
+					   (ffloor first second)))
+		 (format "checking (ffloor %S %S) gives %S"
+			 first second two-ffloor-result))
+	 (Assert (equal one-ceiling-result (multiple-value-list
+					    (ceiling first)))
+		 (format "checking (ceiling %S) gives %S"
+			 first one-ceiling-result))
+	 (Assert (equal one-ceiling-result (multiple-value-list
+					    (ceiling first 1)))
+		 (format "checking (ceiling %S 1) gives %S"
+			 first one-ceiling-result))
+	 (Check-Error arith-error (ceiling first 0))
+	 (Check-Error arith-error (ceiling first 0.0))
+	 (Assert (equal two-ceiling-result (multiple-value-list
+					    (ceiling first second)))
+		 (format "checking (ceiling %S %S) gives %S"
+			 first second two-ceiling-result))
+	 (Assert (equal (cl-ceiling first second)
+			(multiple-value-list (ceiling first second)))
+		 (format
+		  "checking (ceiling %S %S) gives the same as the old code"
+		  first second))
+	 (Assert (equal one-fceiling-result (multiple-value-list
+					     (fceiling first)))
+		 (format "checking (fceiling %S) gives %S"
+			 first one-fceiling-result))
+	 (Assert (equal one-fceiling-result (multiple-value-list
+					     (fceiling first 1)))
+		 (format "checking (fceiling %S 1) gives %S"
+			 first one-fceiling-result))
+	 (Check-Error arith-error (fceiling first 0))
+	 (Check-Error arith-error (fceiling first 0.0))
+	 (Assert (equal two-fceiling-result (multiple-value-list
+					  (fceiling first second)))
+		 (format "checking (fceiling %S %S) gives %S"
+			 first second two-fceiling-result))
+	 (Assert (equal one-round-result (multiple-value-list
+					  (round first)))
+		 (format "checking (round %S) gives %S"
+			 first one-round-result))
+	 (Assert (equal one-round-result (multiple-value-list
+					  (round first 1)))
+		 (format "checking (round %S 1) gives %S, types %S, actual %S, types %S"
+			 first one-round-result (mapcar #'type-of one-round-result)
+			 (multiple-value-list (round first 1))
+			 (mapcar #'type-of (multiple-value-list (round first 1)))))
 
+	 (Check-Error arith-error (round first 0))
+	 (Check-Error arith-error (round first 0.0))
+	 (Assert (equal two-round-result (multiple-value-list
+					  (round first second)))
+		 (format "checking (round %S %S) gives %S"
+			 first second two-round-result))
+	 (Assert (equal one-fround-result (multiple-value-list
+					   (fround first)))
+		 (format "checking (fround %S) gives %S"
+			 first one-fround-result))
+	 (Assert (equal one-fround-result (multiple-value-list
+					   (fround first 1)))
+		 (format "checking (fround %S 1) gives %S"
+			 first one-fround-result))
+	 (Check-Error arith-error (fround first 0))
+	 (Check-Error arith-error (fround first 0.0))
+	 (Assert (equal two-fround-result (multiple-value-list
+					   (fround first second)))
+		 (format "checking (fround %S %S) gives %S"
+			 first second two-fround-result))
+	 (Assert (equal (cl-round first second)
+			(multiple-value-list (round first second)))
+		 (format
+		  "checking (round %S %S) gives the same as the old code"
+		  first second))
+	 (Assert (equal one-truncate-result (multiple-value-list
+					     (truncate first)))
+		 (format "checking (truncate %S) gives %S"
+			 first one-truncate-result))
+	 (Assert (equal one-truncate-result (multiple-value-list
+					     (truncate first 1)))
+		 (format "checking (truncate %S 1) gives %S"
+			 first one-truncate-result))
+	 (Check-Error arith-error (truncate first 0))
+	 (Check-Error arith-error (truncate first 0.0))
+	 (Assert (equal two-truncate-result (multiple-value-list
+					     (truncate first second)))
+		 (format "checking (truncate %S %S) gives %S"
+			 first second two-truncate-result))
+	 (Assert (equal (cl-truncate first second)
+			(multiple-value-list (truncate first second)))
+		 (format
+		  "checking (truncate %S %S) gives the same as the old code"
+		  first second))
+	 (Assert (equal one-ftruncate-result (multiple-value-list
+					      (ftruncate first)))
+		 (format "checking (ftruncate %S) gives %S"
+			 first one-ftruncate-result))
+	 (Assert (equal one-ftruncate-result (multiple-value-list
+					      (ftruncate first 1)))
+		 (format "checking (ftruncate %S 1) gives %S"
+			 first one-ftruncate-result))
+	 (Check-Error arith-error (ftruncate first 0))
+	 (Check-Error arith-error (ftruncate first 0.0))
+	 (Assert (equal two-ftruncate-result (multiple-value-list
+					      (ftruncate first second)))
+		 (format "checking (ftruncate %S %S) gives %S"
+			 first second two-ftruncate-result)))
+       (Assert-rounding-floating (pie ee)
+	 (let ((pie-type (type-of pie)))
+	   (assert (eq pie-type (type-of ee)) t
+		   "This code assumes the two arguments have the same type.")
+	   (Assert-rounding pie ee
+  	    :one-floor-result (list 3 (- pie 3))
+            :two-floor-result (list 1 (- pie (* 1 ee)))
+            :one-ffloor-result (list (coerce 3 pie-type) (- pie 3.0))
+            :two-ffloor-result (list (coerce 1 pie-type) (- pie (* 1.0 ee)))
+            :one-ceiling-result (list 4 (- pie 4))
+            :two-ceiling-result (list 2 (- pie (* 2 ee)))
+            :one-fceiling-result (list (coerce 4 pie-type) (- pie 4.0))
+            :two-fceiling-result (list (coerce 2 pie-type) (- pie (* 2.0 ee)))
+            :one-round-result (list 3 (- pie 3))
+            :two-round-result (list 1 (- pie (* 1 ee)))
+            :one-fround-result (list (coerce 3 pie-type) (- pie 3.0))
+            :two-fround-result (list (coerce 1 pie-type) (- pie (* 1.0 ee)))
+            :one-truncate-result (list 3 (- pie 3))
+            :two-truncate-result (list 1 (- pie (* 1 ee)))
+            :one-ftruncate-result (list (coerce 3 pie-type) (- pie 3.0))
+            :two-ftruncate-result (list (coerce 1 pie-type)
+					(- pie (* 1.0 ee))))
+  	 (Assert-rounding pie (- ee)
+            :one-floor-result (list 3 (- pie 3))
+            :two-floor-result (list -2 (- pie (* -2 (- ee))))
+            :one-ffloor-result (list (coerce 3 pie-type) (- pie 3.0))
+            :two-ffloor-result (list (coerce -2 pie-type)
+				     (- pie (* -2.0 (- ee))))
+            :one-ceiling-result (list 4 (- pie 4))
+            :two-ceiling-result (list -1 (- pie (* -1 (- ee))))
+            :one-fceiling-result (list (coerce 4 pie-type) (- pie 4.0))
+            :two-fceiling-result (list (coerce -1 pie-type)
+				       (- pie (* -1.0 (- ee))))
+            :one-round-result (list 3 (- pie 3))
+            :two-round-result (list -1 (- pie (* -1 (- ee))))
+            :one-fround-result (list (coerce 3 pie-type) (- pie 3.0))
+            :two-fround-result (list (coerce -1 pie-type)
+				     (- pie (* -1.0 (- ee))))
+            :one-truncate-result (list 3 (- pie 3))
+            :two-truncate-result (list -1 (- pie (* -1 (- ee))))
+            :one-ftruncate-result (list (coerce 3 pie-type) (- pie 3.0))
+            :two-ftruncate-result (list (coerce -1 pie-type)
+					(- pie (* -1.0 (- ee)))))
+  	 (Assert-rounding (- pie) ee
+            :one-floor-result (list -4 (- (- pie) -4))
+            :two-floor-result (list -2 (- (- pie) (* -2 ee)))
+            :one-ffloor-result (list (coerce -4 pie-type) (- (- pie) -4.0))
+            :two-ffloor-result (list (coerce -2 pie-type)
+				     (- (- pie) (* -2.0 ee)))
+            :one-ceiling-result (list -3 (- (- pie) -3))
+            :two-ceiling-result (list -1 (- (- pie) (* -1 ee)))
+            :one-fceiling-result (list (coerce -3 pie-type) (- (- pie) -3.0))
+            :two-fceiling-result (list (coerce -1 pie-type)
+				       (- (- pie) (* -1.0 ee)))
+            :one-round-result (list -3 (- (- pie) -3))
+            :two-round-result (list -1 (- (- pie) (* -1 ee)))
+            :one-fround-result (list (coerce -3 pie-type) (- (- pie) -3.0))
+            :two-fround-result (list (coerce -1 pie-type)
+				     (- (- pie) (* -1.0 ee)))
+            :one-truncate-result (list -3 (- (- pie) -3))
+            :two-truncate-result (list -1 (- (- pie) (* -1 ee)))
+            :one-ftruncate-result (list (coerce -3 pie-type) (- (- pie) -3.0))
+            :two-ftruncate-result (list (coerce -1 pie-type)
+					(- (- pie) (* -1.0 ee))))
+  	 (Assert-rounding (- pie) (- ee)
+            :one-floor-result (list -4 (- (- pie) -4))
+            :two-floor-result (list 1 (- (- pie) (* 1 (- ee))))
+            :one-ffloor-result (list (coerce -4 pie-type) (- (- pie) -4.0))
+            :two-ffloor-result (list (coerce 1 pie-type)
+				     (- (- pie) (* 1.0 (- ee))))
+            :one-ceiling-result (list -3 (- (- pie) -3))
+            :two-ceiling-result (list 2 (- (- pie) (* 2 (- ee))))
+            :one-fceiling-result (list (coerce -3 pie-type) (- (- pie) -3.0))
+            :two-fceiling-result (list (coerce 2 pie-type)
+				       (- (- pie) (* 2.0 (- ee))))
+            :one-round-result (list -3 (- (- pie) -3))
+            :two-round-result (list 1 (- (- pie) (* 1 (- ee))))
+            :one-fround-result (list (coerce -3 pie-type) (- (- pie) -3.0))
+            :two-fround-result (list (coerce 1 pie-type)
+				     (- (- pie) (* 1.0 (- ee))))
+            :one-truncate-result (list -3 (- (- pie) -3))
+            :two-truncate-result (list 1 (- (- pie) (* 1 (- ee))))
+            :one-ftruncate-result (list (coerce -3 pie-type) (- (- pie) -3.0))
+            :two-ftruncate-result (list (coerce 1 pie-type)
+					(- (- pie) (* 1.0 (- ee)))))
+  	 (Assert-rounding ee pie
+            :one-floor-result (list 2 (- ee 2))
+            :two-floor-result (list 0 ee)
+            :one-ffloor-result (list (coerce 2 pie-type) (- ee 2.0))
+            :two-ffloor-result (list (coerce 0 pie-type) ee)
+            :one-ceiling-result (list 3 (- ee 3))
+            :two-ceiling-result (list 1 (- ee pie))
+            :one-fceiling-result (list (coerce 3 pie-type) (- ee 3.0))
+            :two-fceiling-result (list (coerce 1 pie-type) (- ee pie))
+            :one-round-result (list 3 (- ee 3))
+            :two-round-result (list 1 (- ee (* 1 pie)))
+            :one-fround-result (list (coerce 3 pie-type) (- ee 3.0))
+            :two-fround-result (list (coerce 1 pie-type) (- ee (* 1.0 pie)))
+            :one-truncate-result (list 2 (- ee 2))
+            :two-truncate-result (list 0 ee)
+            :one-ftruncate-result (list (coerce 2 pie-type) (- ee 2.0))
+            :two-ftruncate-result (list (coerce 0 pie-type) ee))
+  	 (Assert-rounding ee (- pie)
+            :one-floor-result (list 2 (- ee 2))
+            :two-floor-result (list -1 (- ee (* -1 (- pie))))
+            :one-ffloor-result (list (coerce 2 pie-type) (- ee 2.0))
+            :two-ffloor-result (list (coerce -1 pie-type)
+				     (- ee (* -1.0 (- pie))))
+            :one-ceiling-result (list 3 (- ee 3))
+            :two-ceiling-result (list 0 ee)
+            :one-fceiling-result (list (coerce 3 pie-type) (- ee 3.0))
+            :two-fceiling-result (list (coerce 0 pie-type) ee)
+            :one-round-result (list 3 (- ee 3))
+            :two-round-result (list -1 (- ee (* -1 (- pie))))
+            :one-fround-result (list (coerce 3 pie-type) (- ee 3.0))
+            :two-fround-result (list (coerce -1 pie-type)
+				     (- ee (* -1.0 (- pie))))
+            :one-truncate-result (list 2 (- ee 2))
+            :two-truncate-result (list 0 ee)
+            :one-ftruncate-result (list (coerce 2 pie-type) (- ee 2.0))
+            :two-ftruncate-result (list (coerce 0 pie-type) ee)))))
+    ;; First, two integers: 
+  (Assert-rounding 27 8 :one-floor-result '(27 0) :two-floor-result '(3 3)
+    :one-ffloor-result '(27.0 0) :two-ffloor-result '(3.0 3)
+    :one-ceiling-result '(27 0) :two-ceiling-result '(4 -5)
+    :one-fceiling-result '(27.0 0) :two-fceiling-result '(4.0 -5)
+    :one-round-result '(27 0) :two-round-result '(3 3)
+    :one-fround-result '(27.0 0) :two-fround-result '(3.0 3)
+    :one-truncate-result '(27 0) :two-truncate-result '(3 3)
+    :one-ftruncate-result '(27.0 0) :two-ftruncate-result '(3.0 3))
+  (Assert-rounding 27 -8 :one-floor-result '(27 0)  :two-floor-result '(-4 -5)
+    :one-ffloor-result '(27.0 0) :two-ffloor-result '(-4.0 -5) 
+    :one-ceiling-result '(27 0) :two-ceiling-result '(-3 3)
+    :one-fceiling-result '(27.0 0)  :two-fceiling-result '(-3.0 3)
+    :one-round-result '(27 0) :two-round-result '(-3 3)
+    :one-fround-result '(27.0 0) :two-fround-result '(-3.0 3)
+    :one-truncate-result '(27 0) :two-truncate-result '(-3 3)
+    :one-ftruncate-result '(27.0 0)  :two-ftruncate-result '(-3.0 3))
+  (Assert-rounding -27 8
+    :one-floor-result '(-27 0) :two-floor-result '(-4 5)
+    :one-ffloor-result '(-27.0 0) :two-ffloor-result '(-4.0 5)
+    :one-ceiling-result '(-27 0) :two-ceiling-result '(-3 -3)
+    :one-fceiling-result '(-27.0 0) :two-fceiling-result '(-3.0 -3)
+    :one-round-result '(-27 0) :two-round-result '(-3 -3)
+    :one-fround-result '(-27.0 0) :two-fround-result '(-3.0 -3)
+    :one-truncate-result '(-27 0) :two-truncate-result '(-3 -3)
+    :one-ftruncate-result '(-27.0 0) :two-ftruncate-result '(-3.0 -3))
+  (Assert-rounding -27 -8
+    :one-floor-result '(-27 0) :two-floor-result '(3 -3)
+    :one-ffloor-result '(-27.0 0) :two-ffloor-result '(3.0 -3)
+    :one-ceiling-result '(-27 0) :two-ceiling-result '(4 5)
+    :one-fceiling-result '(-27.0 0) :two-fceiling-result '(4.0 5)
+    :one-round-result '(-27 0) :two-round-result '(3 -3)
+    :one-fround-result '(-27.0 0) :two-fround-result '(3.0 -3)
+    :one-truncate-result '(-27 0) :two-truncate-result '(3 -3)
+    :one-ftruncate-result '(-27.0 0) :two-ftruncate-result '(3.0 -3))
+  (Assert-rounding 8 27
+    :one-floor-result '(8 0) :two-floor-result '(0 8)
+    :one-ffloor-result '(8.0 0) :two-ffloor-result '(0.0 8)
+    :one-ceiling-result '(8 0) :two-ceiling-result '(1 -19)
+    :one-fceiling-result '(8.0 0) :two-fceiling-result '(1.0 -19)
+    :one-round-result '(8 0) :two-round-result '(0 8)
+    :one-fround-result '(8.0 0) :two-fround-result '(0.0 8)
+    :one-truncate-result '(8 0) :two-truncate-result '(0 8)
+    :one-ftruncate-result '(8.0 0) :two-ftruncate-result '(0.0 8))
+  (Assert-rounding 8 -27
+    :one-floor-result '(8 0) :two-floor-result '(-1 -19)
+    :one-ffloor-result '(8.0 0) :two-ffloor-result '(-1.0 -19)
+    :one-ceiling-result '(8 0) :two-ceiling-result '(0 8)
+    :one-fceiling-result '(8.0 0) :two-fceiling-result '(0.0 8)
+    :one-round-result '(8 0) :two-round-result '(0 8)
+    :one-fround-result '(8.0 0) :two-fround-result '(0.0 8)
+    :one-truncate-result '(8 0) :two-truncate-result '(0 8)
+    :one-ftruncate-result '(8.0 0) :two-ftruncate-result '(0.0 8))
+  (Assert-rounding -8 27
+    :one-floor-result '(-8 0) :two-floor-result '(-1 19)
+    :one-ffloor-result '(-8.0 0) :two-ffloor-result '(-1.0 19)
+    :one-ceiling-result '(-8 0) :two-ceiling-result '(0 -8)
+    :one-fceiling-result '(-8.0 0) :two-fceiling-result '(0.0 -8)
+    :one-round-result '(-8 0) :two-round-result '(0 -8)
+    :one-fround-result '(-8.0 0) :two-fround-result '(0.0 -8)
+    :one-truncate-result '(-8 0) :two-truncate-result '(0 -8)
+    :one-ftruncate-result '(-8.0 0) :two-ftruncate-result '(0.0 -8))
+  (Assert-rounding -8 -27
+    :one-floor-result '(-8 0) :two-floor-result '(0 -8)
+    :one-ffloor-result '(-8.0 0) :two-ffloor-result '(0.0 -8)
+    :one-ceiling-result '(-8 0) :two-ceiling-result '(1 19)
+    :one-fceiling-result '(-8.0 0) :two-fceiling-result '(1.0 19)
+    :one-round-result '(-8 0) :two-round-result '(0 -8)
+    :one-fround-result '(-8.0 0) :two-fround-result '(0.0 -8)
+    :one-truncate-result '(-8 0) :two-truncate-result '(0 -8)
+    :one-ftruncate-result '(-8.0 0) :two-ftruncate-result '(0.0 -8))
+  (Assert-rounding 32 4
+    :one-floor-result '(32 0) :two-floor-result '(8 0)
+    :one-ffloor-result '(32.0 0) :two-ffloor-result '(8.0 0)
+    :one-ceiling-result '(32 0) :two-ceiling-result '(8 0)
+    :one-fceiling-result '(32.0 0) :two-fceiling-result '(8.0 0)
+    :one-round-result '(32 0) :two-round-result '(8 0)
+    :one-fround-result '(32.0 0) :two-fround-result '(8.0 0)
+    :one-truncate-result '(32 0) :two-truncate-result '(8 0)
+    :one-ftruncate-result '(32.0 0) :two-ftruncate-result '(8.0 0))
+  (Assert-rounding 32 -4
+    :one-floor-result '(32 0) :two-floor-result '(-8 0)
+    :one-ffloor-result '(32.0 0) :two-ffloor-result '(-8.0 0)
+    :one-ceiling-result '(32 0) :two-ceiling-result '(-8 0)
+    :one-fceiling-result '(32.0 0) :two-fceiling-result '(-8.0 0)
+    :one-round-result '(32 0) :two-round-result '(-8 0)
+    :one-fround-result '(32.0 0) :two-fround-result '(-8.0 0)
+    :one-truncate-result '(32 0) :two-truncate-result '(-8 0)
+    :one-ftruncate-result '(32.0 0) :two-ftruncate-result '(-8.0 0))
+  (Assert-rounding 12 9
+    :one-floor-result '(12 0) :two-floor-result '(1 3)
+    :one-ffloor-result '(12.0 0) :two-ffloor-result '(1.0 3)
+    :one-ceiling-result '(12 0) :two-ceiling-result '(2 -6)
+    :one-fceiling-result '(12.0 0) :two-fceiling-result '(2.0 -6)
+    :one-round-result '(12 0) :two-round-result '(1 3)
+    :one-fround-result '(12.0 0) :two-fround-result '(1.0 3)
+    :one-truncate-result '(12 0) :two-truncate-result '(1 3)
+    :one-ftruncate-result '(12.0 0) :two-ftruncate-result '(1.0 3))
+  (Assert-rounding 10 4
+    :one-floor-result '(10 0) :two-floor-result '(2 2)
+    :one-ffloor-result '(10.0 0) :two-ffloor-result '(2.0 2)
+    :one-ceiling-result '(10 0) :two-ceiling-result '(3 -2)
+    :one-fceiling-result '(10.0 0) :two-fceiling-result '(3.0 -2)
+    :one-round-result '(10 0) :two-round-result '(2 2)
+    :one-fround-result '(10.0 0) :two-fround-result '(2.0 2)
+    :one-truncate-result '(10 0) :two-truncate-result '(2 2)
+    :one-ftruncate-result '(10.0 0) :two-ftruncate-result '(2.0 2))
+  (Assert-rounding 14 4
+    :one-floor-result '(14 0) :two-floor-result '(3 2)
+    :one-ffloor-result '(14.0 0) :two-ffloor-result '(3.0 2)
+    :one-ceiling-result '(14 0) :two-ceiling-result '(4 -2)
+    :one-fceiling-result '(14.0 0) :two-fceiling-result '(4.0 -2)
+    :one-round-result '(14 0) :two-round-result '(4 -2)
+    :one-fround-result '(14.0 0) :two-fround-result '(4.0 -2)
+    :one-truncate-result '(14 0) :two-truncate-result '(3 2)
+    :one-ftruncate-result '(14.0 0) :two-ftruncate-result '(3.0 2))
+  ;; Now, two floats:
+  (Assert-rounding-floating pi e)
+  (when (featurep 'bigfloat)
+    (Assert-rounding-floating (coerce pi 'bigfloat) (coerce e 'bigfloat)))
+  (when (featurep 'bignum)
+    (assert (not (evenp most-positive-fixnum)) t
+      "In the unlikely event that most-positive-fixnum is even, rewrite this.")
+    (Assert-rounding (1+ most-positive-fixnum) (* 2 most-positive-fixnum)
+      :one-floor-result `(,(1+ most-positive-fixnum) 0)
+      :two-floor-result `(0 ,(1+ most-positive-fixnum))
+      :one-ffloor-result `(,(float (1+ most-positive-fixnum)) 0)
+      :two-ffloor-result `(0.0 ,(1+ most-positive-fixnum))
+      :one-ceiling-result `(,(1+ most-positive-fixnum) 0)
+      :two-ceiling-result `(1 ,(1+ (- most-positive-fixnum)))
+      :one-fceiling-result `(,(float (1+ most-positive-fixnum)) 0)
+      :two-fceiling-result `(1.0 ,(1+ (- most-positive-fixnum)))
+      :one-round-result `(,(1+ most-positive-fixnum) 0)
+      :two-round-result `(1 ,(1+ (- most-positive-fixnum)))
+      :one-fround-result `(,(float (1+ most-positive-fixnum)) 0)
+      :two-fround-result `(1.0 ,(1+ (- most-positive-fixnum)))
+      :one-truncate-result `(,(1+ most-positive-fixnum) 0)
+      :two-truncate-result `(0 ,(1+ most-positive-fixnum))
+      :one-ftruncate-result `(,(float (1+ most-positive-fixnum)) 0)
+      :two-ftruncate-result `(0.0 ,(1+ most-positive-fixnum)))
+    (Assert-rounding (1+ most-positive-fixnum) (- (* 2 most-positive-fixnum))
+      :one-floor-result `(,(1+ most-positive-fixnum) 0)
+      :two-floor-result `(-1 ,(1+ (- most-positive-fixnum)))
+      :one-ffloor-result `(,(float (1+ most-positive-fixnum)) 0)
+      :two-ffloor-result `(-1.0 ,(1+ (- most-positive-fixnum)))
+      :one-ceiling-result `(,(1+ most-positive-fixnum) 0)
+      :two-ceiling-result `(0 ,(1+ most-positive-fixnum))
+      :one-fceiling-result `(,(float (1+ most-positive-fixnum)) 0)
+      :two-fceiling-result `(0.0 ,(1+ most-positive-fixnum))
+      :one-round-result `(,(1+ most-positive-fixnum) 0)
+      :two-round-result `(-1 ,(1+ (- most-positive-fixnum)))
+      :one-fround-result `(,(float (1+ most-positive-fixnum)) 0)
+      :two-fround-result `(-1.0 ,(1+ (- most-positive-fixnum)))
+      :one-truncate-result `(,(1+ most-positive-fixnum) 0)
+      :two-truncate-result `(0 ,(1+ most-positive-fixnum))
+      :one-ftruncate-result `(,(float (1+ most-positive-fixnum)) 0)
+      :two-ftruncate-result `(0.0 ,(1+ most-positive-fixnum)))
+    (Assert-rounding (- (1+ most-positive-fixnum)) (* 2 most-positive-fixnum)
+      :one-floor-result `(,(- (1+ most-positive-fixnum)) 0)
+      :two-floor-result `(-1 ,(1- most-positive-fixnum))
+      :one-ffloor-result `(,(float (- (1+ most-positive-fixnum))) 0)
+      :two-ffloor-result `(-1.0 ,(1- most-positive-fixnum))
+      :one-ceiling-result `(,(- (1+ most-positive-fixnum)) 0)
+      :two-ceiling-result `(0 ,(- (1+ most-positive-fixnum)))
+      :one-fceiling-result `(,(float (- (1+ most-positive-fixnum))) 0)
+      :two-fceiling-result `(0.0 ,(- (1+ most-positive-fixnum)))
+      :one-round-result `(,(- (1+ most-positive-fixnum)) 0)
+      :two-round-result `(-1 ,(1- most-positive-fixnum))
+      :one-fround-result `(,(float (- (1+ most-positive-fixnum))) 0)
+      :two-fround-result `(-1.0 ,(1- most-positive-fixnum))
+      :one-truncate-result `(,(- (1+ most-positive-fixnum)) 0)
+      :two-truncate-result `(0 ,(- (1+ most-positive-fixnum)))
+      :one-ftruncate-result `(,(float (- (1+ most-positive-fixnum))) 0)
+      :two-ftruncate-result `(0.0 ,(- (1+ most-positive-fixnum))))
+    ;; Test the handling of values with .5: 
+    (Assert-rounding (1+ (* 2 most-positive-fixnum)) 2
+      :one-floor-result `(,(1+ (* 2 most-positive-fixnum)) 0)
+      :two-floor-result `(,most-positive-fixnum 1)
+      :one-ffloor-result `(,(float (1+ (* 2 most-positive-fixnum))) 0)
+      ;; We can't just call #'float here; we must use code that converts a
+      ;; bignum with value most-positive-fixnum (the creation of which is
+      ;; not directly possible in Lisp) to a float, not code that converts
+      ;; the fixnum with value most-positive-fixnum to a float. The eval is
+      ;; to avoid compile-time optimisation that can break this.
+      :two-ffloor-result `(,(eval '(- (1+ most-positive-fixnum) 1 0.0)) 1)
+      :one-ceiling-result `(,(1+ (* 2 most-positive-fixnum)) 0)
+      :two-ceiling-result `(,(1+ most-positive-fixnum) -1)
+      :one-fceiling-result `(,(float (1+ (* 2 most-positive-fixnum))) 0)
+      :two-fceiling-result `(,(float (1+ most-positive-fixnum)) -1)
+      :one-round-result `(,(1+ (* 2 most-positive-fixnum)) 0)
+      :two-round-result `(,(1+ most-positive-fixnum) -1)
+      :one-fround-result `(,(float (1+ (* 2 most-positive-fixnum))) 0)
+      :two-fround-result `(,(float (1+ most-positive-fixnum)) -1)
+      :one-truncate-result `(,(1+ (* 2 most-positive-fixnum)) 0)
+      :two-truncate-result `(,most-positive-fixnum 1)
+      :one-ftruncate-result `(,(float (1+ (* 2 most-positive-fixnum))) 0)
+      ;; See the comment above on :two-ffloor-result:
+      :two-ftruncate-result `(,(eval '(- (1+ most-positive-fixnum) 1 0.0)) 1))
+    (Assert-rounding (1+ (* 2 (1- most-positive-fixnum))) 2
+      :one-floor-result `(,(1+ (* 2 (1- most-positive-fixnum))) 0)
+      :two-floor-result `(,(1- most-positive-fixnum) 1)
+      :one-ffloor-result `(,(float (1+ (* 2 (1- most-positive-fixnum)))) 0)
+      ;; See commentary above on float conversions.
+      :two-ffloor-result `(,(eval '(- (1+ most-positive-fixnum) 2 0.0)) 1)
+      :one-ceiling-result `(,(1+ (* 2 (1- most-positive-fixnum))) 0)
+      :two-ceiling-result `(,most-positive-fixnum -1)
+      :one-fceiling-result `(,(float (1+ (* 2 (1- most-positive-fixnum)))) 0)
+      :two-fceiling-result `(,(eval '(- (1+ most-positive-fixnum) 1 0.0)) -1)
+      :one-round-result `(,(1+ (* 2 (1- most-positive-fixnum))) 0)
+      :two-round-result `(,(1- most-positive-fixnum) 1)
+      :one-fround-result `(,(float (1+ (* 2 (1- most-positive-fixnum)))) 0)
+      :two-fround-result `(,(eval '(- (1+ most-positive-fixnum) 2 0.0)) 1)
+      :one-truncate-result `(,(1+ (* 2 (1- most-positive-fixnum))) 0)
+      :two-truncate-result `(,(1- most-positive-fixnum) 1)
+      :one-ftruncate-result `(,(float (1+ (* 2 (1- most-positive-fixnum)))) 0)
+      ;; See commentary above
+      :two-ftruncate-result `(,(eval '(- (1+ most-positive-fixnum) 2 0.0))
+			      1)))
+  (when (featurep 'ratio)
+    (Assert-rounding (read "4/3") (read "8/7")
+     :one-floor-result '(1 1/3) :two-floor-result '(1 4/21)
+     :one-ffloor-result '(1.0 1/3) :two-ffloor-result '(1.0 4/21)
+     :one-ceiling-result '(2 -2/3) :two-ceiling-result '(2 -20/21)
+     :one-fceiling-result '(2.0 -2/3) :two-fceiling-result '(2.0 -20/21)
+     :one-round-result '(1 1/3) :two-round-result '(1 4/21)
+     :one-fround-result '(1.0 1/3) :two-fround-result '(1.0 4/21)
+     :one-truncate-result '(1 1/3) :two-truncate-result '(1 4/21)
+     :one-ftruncate-result '(1.0 1/3) :two-ftruncate-result '(1.0 4/21))
+    (Assert-rounding (read "-4/3") (read "8/7")
+     :one-floor-result '(-2 2/3) :two-floor-result '(-2 20/21)
+     :one-ffloor-result '(-2.0 2/3) :two-ffloor-result '(-2.0 20/21)
+     :one-ceiling-result '(-1 -1/3) :two-ceiling-result '(-1 -4/21)
+     :one-fceiling-result '(-1.0 -1/3) :two-fceiling-result '(-1.0 -4/21)
+     :one-round-result '(-1 -1/3) :two-round-result '(-1 -4/21)
+     :one-fround-result '(-1.0 -1/3) :two-fround-result '(-1.0 -4/21)
+     :one-truncate-result '(-1 -1/3) :two-truncate-result '(-1 -4/21)
+     :one-ftruncate-result '(-1.0 -1/3) :two-ftruncate-result '(-1.0 -4/21))))
 
+;; Run this function in a Common Lisp with two arguments to get results that
+;; we should compare against, above. Though note the dancing-around with the
+;; bigfloats and bignums above, too; you can't necessarily just use the
+;; output here.
+
+(defun generate-rounding-output (first second)
+  (let ((print-readably t))
+    (princ first)
+    (princ " ")
+    (princ second)
+    (princ " :one-floor-result ")
+    (princ (list 'quote (multiple-value-list (floor first))))
+    (princ " :two-floor-result ")
+    (princ (list 'quote (multiple-value-list (floor first second))))
+    (princ " :one-ffloor-result ")
+    (princ (list 'quote (multiple-value-list (ffloor first))))
+    (princ " :two-ffloor-result ")
+    (princ (list 'quote (multiple-value-list (ffloor first second))))
+    (princ " :one-ceiling-result ")
+    (princ (list 'quote (multiple-value-list (ceiling first))))
+    (princ " :two-ceiling-result ")
+    (princ (list 'quote (multiple-value-list (ceiling first second))))
+    (princ " :one-fceiling-result ")
+    (princ (list 'quote (multiple-value-list (fceiling first))))
+    (princ " :two-fceiling-result ")
+    (princ (list 'quote (multiple-value-list (fceiling first second))))
+    (princ " :one-round-result ")
+    (princ (list 'quote (multiple-value-list (round first))))
+    (princ " :two-round-result ")
+    (princ (list 'quote (multiple-value-list (round first second))))
+    (princ " :one-fround-result ")
+    (princ (list 'quote (multiple-value-list (fround first))))
+    (princ " :two-fround-result ")
+    (princ (list 'quote (multiple-value-list (fround first second))))
+    (princ " :one-truncate-result ")
+    (princ (list 'quote (multiple-value-list (truncate first))))
+    (princ " :two-truncate-result ")
+    (princ (list 'quote (multiple-value-list (truncate first second))))
+    (princ " :one-ftruncate-result ")
+    (princ (list 'quote (multiple-value-list (ftruncate first))))
+    (princ " :two-ftruncate-result ")
+    (princ (list 'quote (multiple-value-list (ftruncate first second))))))