guile-commits
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Guile-commits] GNU Guile branch, master, updated. release_1-9-15-106-g7


From: Andy Wingo
Subject: [Guile-commits] GNU Guile branch, master, updated. release_1-9-15-106-g7017846
Date: Mon, 14 Feb 2011 19:39:03 +0000

This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GNU Guile".

http://git.savannah.gnu.org/cgit/guile.git/commit/?id=7017846b2b34e4ec3fd402c6c02ff40e90d256b5

The branch, master has been updated
       via  7017846b2b34e4ec3fd402c6c02ff40e90d256b5 (commit)
       via  4fb8bc64a0c5d074ab6658b763dfe24449f3d3b4 (commit)
       via  5093e51839bb1246e96547a25ae41b4c4d92e1f7 (commit)
       via  f135fc3eda9a34f9540486da433867b2698fa58c (commit)
       via  a8da6d9338e2236742e95a0f7337796078f9e2a3 (commit)
       via  8b56bcec442b203fcde509b40bd5866e62aaa111 (commit)
       via  8f9da3406b507058475db2e8dde6fbb50d12faf7 (commit)
       via  03ddd15baed49dcfed0d7fc76648778b76df56d3 (commit)
       via  5fbf680be9e98d005bbbd6a598443fca76a6918d (commit)
       via  a85c1f93f03e1de8f6a52a09b186e885c4c2f216 (commit)
      from  1fa6ac0014bd2b26b4eeb985ef123ba6cd4468e6 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 7017846b2b34e4ec3fd402c6c02ff40e90d256b5
Author: Andy Wingo <address@hidden>
Date:   Mon Feb 14 20:39:31 2011 +0100

    tweak to internal scm_i_extract_values_2
    
    * libguile/values.c (scm_i_extract_values_2): Make nvalues check more
      robust.

commit 4fb8bc64a0c5d074ab6658b763dfe24449f3d3b4
Author: Mark H Weaver <address@hidden>
Date:   Sat Feb 12 05:43:17 2011 -0500

    Allow GOOPS getters to add methods to primitive generics
    
    * module/oop/goops.scm (ensure-generic): If the old definition of a
      desired getter is a primitive generic, let the new method be added to
      it instead of creating a fresh new generic.
    
      (ensure-accessor): Modify as necessary to keep the old behavior.
      Maybe something more optimal can be done here, but it's not yet
      obvious to me how to do it.

commit 5093e51839bb1246e96547a25ae41b4c4d92e1f7
Author: Mark H Weaver <address@hidden>
Date:   Thu Feb 10 19:04:05 2011 -0500

    Make SCM_NUMP and SCM_NUMBERP more extensible
    
    * libguile/numbers.h (SCM_NUMP, SCM_NUMBERP): Mask out more bits in the
      cell type field when doing the comparison, in order to accept future
      numeric types that have not yet been implemented.  This should allow
      us to add more core numeric types without breaking ABI compatibility.
      As a bonus, these macros are now more efficient.

commit f135fc3eda9a34f9540486da433867b2698fa58c
Author: Mark H Weaver <address@hidden>
Date:   Thu Feb 10 19:38:49 2011 -0500

    Slight optimization for scm_equal_p
    
    * libguile/eq.c (scm_equal_p): Move SCM_STRUCTP check within the default
      case of the SCM_TYP7 switch statement, for optimization.

commit a8da6d9338e2236742e95a0f7337796078f9e2a3
Author: Mark H Weaver <address@hidden>
Date:   Sun Feb 13 07:25:28 2011 -0500

    Reduce code size of division operators
    
    * libguile/numbers.c (scm_quotient): Reimplement in terms of
      scm_truncate_quotient.
    
      (scm_remainder): Reimplement in terms of scm_truncate_remainder.
    
      (scm_modulo): Reimplement in terms of scm_floor_remainder.
    
      (scm_euclidean_quotient, scm_euclidean_remainder,
      scm_euclidean_divide): Reimplement in terms of floor and ceiling.
      Make them non-extensible, because there is no need; they will work
      with any objects that implement the floor and ceiling division
      operators, and that can be tested using `negative?'.

commit 8b56bcec442b203fcde509b40bd5866e62aaa111
Author: Mark H Weaver <address@hidden>
Date:   Sun Feb 13 07:14:57 2011 -0500

    Optimize truncate, round, floor, and ceiling
    
    * libguile/numbers.c (scm_c_truncate): Use ceil (x) instead of
      -floor (-x).
    
      (scm_truncate_number): Implement directly instead of by checking the
      sign and using scm_floor or scm_ceiling.  Use scm_truncate_quotient
      for fractions.  Make extensible, so that new number types implemented
      in GOOPS will be able to do the job more efficiently, since it is
      often easier to implement truncate than floor or ceiling.
    
      (scm_round_number): Optimize fractions case by using
      scm_round_quotient.  Make extensible, so that new number types
      implemented in GOOPS will be able to do the job efficiently.
    
      (scm_floor, scm_ceiling): Optimize fractions case by using
      scm_floor_quotient and scm_ceiling_quotient, respectively.
    
    * test-suite/tests/numbers.test: Add test cases.

commit 8f9da3406b507058475db2e8dde6fbb50d12faf7
Author: Mark H Weaver <address@hidden>
Date:   Sun Feb 13 09:16:27 2011 -0500

    Add four new sets of fast quotient and remainder operators
    
    * libguile/numbers.c (scm_floor_divide, scm_floor_quotient,
      scm_floor_remainder, scm_ceiling_divide, scm_ceiling_quotient,
      scm_ceiling_remainder, scm_truncate_divide, scm_truncate_quotient,
      scm_truncate_remainder, scm_round_divide, scm_round_quotient,
      scm_round_remainder): New extensible procedures `floor/',
      `floor-quotient', `floor-remainder', `ceiling/', `ceiling-quotient',
      `ceiling-remainder', `truncate/', `truncate-quotient',
      `truncate-remainder', `round/', `round-quotient', and
      `round-remainder'.
    
    * libguile/numbers.h: Add function prototypes.
    
    * test-suite/tests/numbers.test: Add tests.
    
    * doc/ref/api-data.texi (Arithmetic): Add documentation.
    
    * NEWS: Add NEWS entry.

commit 03ddd15baed49dcfed0d7fc76648778b76df56d3
Author: Mark H Weaver <address@hidden>
Date:   Sun Feb 13 06:04:52 2011 -0500

    Optimize division operators handling of fractions
    
    * libguile/numbers.c: (scm_euclidean_quotient, scm_euclidean_remainder,
      scm_euclidean_divide, scm_centered_quotient, scm_centered_remainder,
      scm_centered_divide): Optimize case where both arguments are exact and
      at least one is a fraction, by reducing to a subproblem involving only
      integers, and then adjusting the resulting remainder as needed.

commit 5fbf680be9e98d005bbbd6a598443fca76a6918d
Author: Mark H Weaver <address@hidden>
Date:   Sun Feb 13 05:47:33 2011 -0500

    Make divide functions return values via (SCM *) output arguments
    
    * libguile/numbers.c (scm_euclidean_divide, scm_centered_divide): Change
      API to return two values via output arguments of type (SCM *), instead
      of packing into a values object.
    
      (scm_i_euclidean_divide, scm_i_centered_divide): New internal wrappers
      that call the above functions and pack the result into a values
      object.
    
    * libguile/numbers.h: Change prototypes to reflect new API.
    
    * doc/ref/api-data.h (Arithmetic): Update manual.

commit a85c1f93f03e1de8f6a52a09b186e885c4c2f216
Author: Mark H Weaver <address@hidden>
Date:   Thu Feb 10 18:03:14 2011 -0500

    Added internal C function to extract from values object
    
    * libguile/values.c (scm_i_extract_values_2): New internal function
      that extracts two values from a values object.
    
    * libguile/values.h: Added prototype.

-----------------------------------------------------------------------

Summary of changes:
 NEWS                          |   28 +
 doc/ref/api-data.texi         |  183 ++-
 libguile/eq.c                 |   16 +-
 libguile/numbers.c            | 3497 +++++++++++++++++++++++++++++------------
 libguile/numbers.h            |   27 +-
 libguile/values.c             |   18 +
 libguile/values.h             |    2 +
 module/oop/goops.scm          |    8 +-
 test-suite/tests/numbers.test |  487 ++++++-
 9 files changed, 3219 insertions(+), 1047 deletions(-)

diff --git a/NEWS b/NEWS
index df44517..6bebbf6 100644
--- a/NEWS
+++ b/NEWS
@@ -23,6 +23,34 @@ instead.
 `define-once' is like Lisp's `defvar': it creates a toplevel binding,
 but only if one does not exist already.
 
+** Added four new sets of fast quotient and remainder operators
+
+Added four new sets of fast quotient and remainder operators with
+different semantics than the R5RS operators.  They support not only
+integers, but all reals, including exact rationals and inexact
+floating point numbers.
+
+These procedures accept two real numbers N and D, where the divisor D
+must be non-zero.  Each set of operators computes an integer quotient
+Q and a real remainder R such that N = Q*D + R and |R| < |D|.  They
+differ only in how N/D is rounded to produce Q.
+
+`floor-quotient' and `floor-remainder' compute Q and R, respectively,
+where Q has been rounded toward negative infinity.  `floor/' returns
+both Q and R, and is more efficient than computing each separately.
+Note that when applied to integers, `floor-remainder' is equivalent to
+the R5RS integer-only `modulo' operator.  `ceiling-quotient',
+`ceiling-remainder', and `ceiling/' are similar except that Q is
+rounded toward positive infinity.
+
+For `truncate-quotient', `truncate-remainder', and `truncate/', Q is
+rounded toward zero.  Note that when applied to integers,
+`truncate-quotient' and `truncate-remainder' are equivalent to the
+R5RS integer-only operators `quotient' and `remainder'.
+
+For `round-quotient', `round-remainder', and `round/', Q is rounded to
+the nearest integer, with ties going to the nearest even integer.
+
 ** Improved exactness handling for complex number parsing
 
 When parsing non-real complex numbers, exactness specifiers are now
diff --git a/doc/ref/api-data.texi b/doc/ref/api-data.texi
index 84a76bd..a8cce24 100644
--- a/doc/ref/api-data.texi
+++ b/doc/ref/api-data.texi
@@ -907,7 +907,7 @@ sign as @var{n}.  In all cases quotient and remainder 
satisfy
 (remainder -13 4) @result{} -1
 @end lisp
 
-See also @code{euclidean-quotient}, @code{euclidean-remainder} and
+See also @code{truncate-quotient}, @code{truncate-remainder} and
 related operations in @ref{Arithmetic}.
 @end deffn
 
@@ -924,7 +924,7 @@ sign as @var{d}.
 (modulo -13 -4) @result{} -1
 @end lisp
 
-See also @code{euclidean-quotient}, @code{euclidean-remainder} and
+See also @code{floor-quotient}, @code{floor-remainder} and
 related operations in @ref{Arithmetic}.
 @end deffn
 
@@ -1148,9 +1148,21 @@ Returns the magnitude or angle of @var{z} as a 
@code{double}.
 @rnindex euclidean/
 @rnindex euclidean-quotient
 @rnindex euclidean-remainder
address@hidden floor/
address@hidden floor-quotient
address@hidden floor-remainder
address@hidden ceiling/
address@hidden ceiling-quotient
address@hidden ceiling-remainder
address@hidden truncate/
address@hidden truncate-quotient
address@hidden truncate-remainder
 @rnindex centered/
 @rnindex centered-quotient
 @rnindex centered-remainder
address@hidden round/
address@hidden round-quotient
address@hidden round-remainder
 
 The C arithmetic functions below always takes two arguments, while the
 Scheme functions can take an arbitrary number.  When you need to
@@ -1250,17 +1262,17 @@ respectively, but these functions take and return 
@code{double}
 values.
 @end deftypefn
 
address@hidden {Scheme Procedure} euclidean/ x y
address@hidden {Scheme Procedure} euclidean-quotient x y
address@hidden {Scheme Procedure} euclidean-remainder x y
address@hidden {C Function} scm_euclidean_divide (x y)
address@hidden {C Function} scm_euclidean_quotient (x y)
address@hidden {C Function} scm_euclidean_remainder (x y)
address@hidden {Scheme Procedure} {} euclidean/ @var{x} @var{y}
address@hidden {Scheme Procedure} {} euclidean-quotient @var{x} @var{y}
address@hidden {Scheme Procedure} {} euclidean-remainder @var{x} @var{y}
address@hidden {C Function} void scm_euclidean_divide (SCM @var{x}, SCM 
@var{y}, SCM address@hidden, SCM address@hidden)
address@hidden {C Function} SCM scm_euclidean_quotient (SCM @var{x}, SCM 
@var{y})
address@hidden {C Function} SCM scm_euclidean_remainder (SCM @var{x}, SCM 
@var{y})
 These procedures accept two real numbers @var{x} and @var{y}, where the
 divisor @var{y} must be non-zero.  @code{euclidean-quotient} returns the
 integer @var{q} and @code{euclidean-remainder} returns the real number
 @var{r} such that @address@hidden = @address@hidden + @var{r}} and
address@hidden <= @var{r} < abs(@var{y})}.  @code{euclidean/} returns both 
@var{q} and
address@hidden <= @var{r} < |@var{y}|}.  @code{euclidean/} returns both @var{q} 
and
 @var{r}, and is more efficient than computing each separately.  Note
 that when @address@hidden > 0}, @code{euclidean-quotient} returns
 @math{floor(@var{x}/@var{y})}, otherwise it returns
@@ -1279,19 +1291,106 @@ Note that these operators are equivalent to the R6RS 
operators
 (euclidean/ -123.2 -63.5) @result{} 2.0 and 3.8
 (euclidean/ 16/3 -10/7) @result{} -3 and 22/21
 @end lisp
address@hidden deffn
address@hidden deftypefn
+
address@hidden {Scheme Procedure} {} floor/ @var{x} @var{y}
address@hidden {Scheme Procedure} {} floor-quotient @var{x} @var{y}
address@hidden {Scheme Procedure} {} floor-remainder @var{x} @var{y}
address@hidden {C Function} void scm_floor_divide (SCM @var{x}, SCM @var{y}, 
SCM address@hidden, SCM address@hidden)
address@hidden {C Function} SCM scm_floor_quotient (@var{x}, @var{y})
address@hidden {C Function} SCM scm_floor_remainder (@var{x}, @var{y})
+These procedures accept two real numbers @var{x} and @var{y}, where the
+divisor @var{y} must be non-zero.  @code{floor-quotient} returns the
+integer @var{q} and @code{floor-remainder} returns the real number
address@hidden such that @address@hidden = floor(@var{x}/@var{y})} and
address@hidden@var{x} = @address@hidden + @var{r}}.  @code{floor/} returns
+both @var{q} and @var{r}, and is more efficient than computing each
+separately.  Note that @var{r}, if non-zero, will have the same sign
+as @var{y}.
+
+When @var{x} and @var{y} are integers, @code{floor-quotient} is
+equivalent to the R5RS integer-only operator @code{modulo}.
+
address@hidden
+(floor-quotient 123 10) @result{} 12
+(floor-remainder 123 10) @result{} 3
+(floor/ 123 10) @result{} 12 and 3
+(floor/ 123 -10) @result{} -13 and -7
+(floor/ -123 10) @result{} -13 and 7
+(floor/ -123 -10) @result{} 12 and -3
+(floor/ -123.2 -63.5) @result{} 1.0 and -59.7
+(floor/ 16/3 -10/7) @result{} -4 and -8/21
address@hidden lisp
address@hidden deftypefn
+
address@hidden {Scheme Procedure} {} ceiling/ @var{x} @var{y}
address@hidden {Scheme Procedure} {} ceiling-quotient @var{x} @var{y}
address@hidden {Scheme Procedure} {} ceiling-remainder @var{x} @var{y}
address@hidden {C Function} void scm_ceiling_divide (SCM @var{x}, SCM @var{y}, 
SCM address@hidden, SCM address@hidden)
address@hidden {C Function} SCM scm_ceiling_quotient (@var{x}, @var{y})
address@hidden {C Function} SCM scm_ceiling_remainder (@var{x}, @var{y})
+These procedures accept two real numbers @var{x} and @var{y}, where the
+divisor @var{y} must be non-zero.  @code{ceiling-quotient} returns the
+integer @var{q} and @code{ceiling-remainder} returns the real number
address@hidden such that @address@hidden = ceiling(@var{x}/@var{y})} and
address@hidden@var{x} = @address@hidden + @var{r}}.  @code{ceiling/} returns
+both @var{q} and @var{r}, and is more efficient than computing each
+separately.  Note that @var{r}, if non-zero, will have the opposite sign
+of @var{y}.
+
address@hidden
+(ceiling-quotient 123 10) @result{} 13
+(ceiling-remainder 123 10) @result{} -7
+(ceiling/ 123 10) @result{} 13 and -7
+(ceiling/ 123 -10) @result{} -12 and 3
+(ceiling/ -123 10) @result{} -12 and -3
+(ceiling/ -123 -10) @result{} 13 and 7
+(ceiling/ -123.2 -63.5) @result{} 2.0 and 3.8
+(ceiling/ 16/3 -10/7) @result{} -3 and 22/21
address@hidden lisp
address@hidden deftypefn
+
address@hidden {Scheme Procedure} {} truncate/ @var{x} @var{y}
address@hidden {Scheme Procedure} {} truncate-quotient @var{x} @var{y}
address@hidden {Scheme Procedure} {} truncate-remainder @var{x} @var{y}
address@hidden {C Function} void scm_truncate_divide (SCM @var{x}, SCM @var{y}, 
SCM address@hidden, SCM address@hidden)
address@hidden {C Function} SCM scm_truncate_quotient (@var{x}, @var{y})
address@hidden {C Function} SCM scm_truncate_remainder (@var{x}, @var{y})
+These procedures accept two real numbers @var{x} and @var{y}, where the
+divisor @var{y} must be non-zero.  @code{truncate-quotient} returns the
+integer @var{q} and @code{truncate-remainder} returns the real number
address@hidden such that @var{q} is @address@hidden/@var{y}} rounded toward 
zero,
+and @address@hidden = @address@hidden + @var{r}}.  @code{truncate/} returns
+both @var{q} and @var{r}, and is more efficient than computing each
+separately.  Note that @var{r}, if non-zero, will have the same sign
+as @var{x}.
 
address@hidden {Scheme Procedure} centered/ x y
address@hidden {Scheme Procedure} centered-quotient x y
address@hidden {Scheme Procedure} centered-remainder x y
address@hidden {C Function} scm_centered_divide (x y)
address@hidden {C Function} scm_centered_quotient (x y)
address@hidden {C Function} scm_centered_remainder (x y)
+When @var{x} and @var{y} are integers, these operators are equivalent to
+the R5RS integer-only operators @code{quotient} and @code{remainder}.
+
address@hidden
+(truncate-quotient 123 10) @result{} 12
+(truncate-remainder 123 10) @result{} 3
+(truncate/ 123 10) @result{} 12 and 3
+(truncate/ 123 -10) @result{} -12 and 3
+(truncate/ -123 10) @result{} -12 and -3
+(truncate/ -123 -10) @result{} 12 and -3
+(truncate/ -123.2 -63.5) @result{} 1.0 and -59.7
+(truncate/ 16/3 -10/7) @result{} -3 and 22/21
address@hidden lisp
address@hidden deftypefn
+
address@hidden {Scheme Procedure} {} centered/ @var{x} @var{y}
address@hidden {Scheme Procedure} {} centered-quotient @var{x} @var{y}
address@hidden {Scheme Procedure} {} centered-remainder @var{x} @var{y}
address@hidden {C Function} void scm_centered_divide (SCM @var{x}, SCM @var{y}, 
SCM address@hidden, SCM address@hidden)
address@hidden {C Function} SCM scm_centered_quotient (SCM @var{x}, SCM @var{y})
address@hidden {C Function} SCM scm_centered_remainder (SCM @var{x}, SCM 
@var{y})
 These procedures accept two real numbers @var{x} and @var{y}, where the
 divisor @var{y} must be non-zero.  @code{centered-quotient} returns the
 integer @var{q} and @code{centered-remainder} returns the real number
 @var{r} such that @address@hidden = @address@hidden + @var{r}} and
address@hidden(@var{y}/2) <= @var{r} < abs(@var{y}/2)}.  @code{centered/}
address@hidden|@var{y}/2| <= @var{r} < |@var{y}/2|}.  @code{centered/}
 returns both @var{q} and @var{r}, and is more efficient than computing
 each separately.
 
@@ -1300,7 +1399,8 @@ rounded to the nearest integer.  When 
@address@hidden/@var{y}} lies
 exactly half-way between two integers, the tie is broken according to
 the sign of @var{y}.  If @address@hidden > 0}, ties are rounded toward
 positive infinity, otherwise they are rounded toward negative infinity.
-This is a consequence of the requirement that @math{-abs(@var{y}/2) <= @var{r} 
< abs(@var{y}/2)}.
+This is a consequence of the requirement that
address@hidden|@var{y}/2| <= @var{r} < |@var{y}/2|}.
 
 Note that these operators are equivalent to the R6RS operators
 @code{div0}, @code{mod0}, and @code{div0-and-mod0}.
@@ -1312,10 +1412,55 @@ Note that these operators are equivalent to the R6RS 
operators
 (centered/ 123 -10) @result{} -12 and 3
 (centered/ -123 10) @result{} -12 and -3
 (centered/ -123 -10) @result{} 12 and -3
+(centered/ 125 10) @result{} 13 and -5
+(centered/ 127 10) @result{} 13 and -3
+(centered/ 135 10) @result{} 14 and -5
 (centered/ -123.2 -63.5) @result{} 2.0 and 3.8
 (centered/ 16/3 -10/7) @result{} -4 and -8/21
 @end lisp
address@hidden deffn
address@hidden deftypefn
+
address@hidden {Scheme Procedure} {} round/ @var{x} @var{y}
address@hidden {Scheme Procedure} {} round-quotient @var{x} @var{y}
address@hidden {Scheme Procedure} {} round-remainder @var{x} @var{y}
address@hidden {C Function} void scm_round_divide (SCM @var{x}, SCM @var{y}, 
SCM address@hidden, SCM address@hidden)
address@hidden {C Function} SCM scm_round_quotient (@var{x}, @var{y})
address@hidden {C Function} SCM scm_round_remainder (@var{x}, @var{y})
+These procedures accept two real numbers @var{x} and @var{y}, where the
+divisor @var{y} must be non-zero.  @code{round-quotient} returns the
+integer @var{q} and @code{round-remainder} returns the real number
address@hidden such that @address@hidden = @address@hidden + @var{r}} and
address@hidden is @address@hidden/@var{y}} rounded to the nearest integer,
+with ties going to the nearest even integer.  @code{round/}
+returns both @var{q} and @var{r}, and is more efficient than computing
+each separately.
+
+Note that @code{round/} and @code{centered/} are almost equivalent, but
+their behavior differs when @address@hidden/@var{y}} lies exactly half-way
+between two integers.  In this case, @code{round/} chooses the nearest
+even integer, whereas @code{centered/} chooses in such a way to satisfy
+the constraint @math{-|@var{y}/2| <= @var{r} < |@var{y}/2|}, which
+is stronger than the corresponding constraint for @code{round/},
address@hidden|@var{y}/2| <= @var{r} <= |@var{y}/2|}.  In particular,
+when @var{x} and @var{y} are integers, the number of possible remainders
+returned by @code{centered/} is @math{|@var{y}|}, whereas the number of
+possible remainders returned by @code{round/} is @math{|@var{y}|+1} when
address@hidden is even.
+
address@hidden
+(round-quotient 123 10) @result{} 12
+(round-remainder 123 10) @result{} 3
+(round/ 123 10) @result{} 12 and 3
+(round/ 123 -10) @result{} -12 and 3
+(round/ -123 10) @result{} -12 and -3
+(round/ -123 -10) @result{} 12 and -3
+(round/ 125 10) @result{} 12 and 5
+(round/ 127 10) @result{} 13 and -3
+(round/ 135 10) @result{} 14 and -5
+(round/ -123.2 -63.5) @result{} 2.0 and 3.8
+(round/ 16/3 -10/7) @result{} -4 and -8/21
address@hidden lisp
address@hidden deftypefn
 
 @node Scientific
 @subsubsection Scientific Functions
diff --git a/libguile/eq.c b/libguile/eq.c
index 99b3488..11dce99 100644
--- a/libguile/eq.c
+++ b/libguile/eq.c
@@ -332,6 +332,14 @@ scm_equal_p (SCM x, SCM y)
   switch (SCM_TYP7 (x))
     {
     default:
+      /* Check equality between structs of equal type (see cell-type test 
above). */
+      if (SCM_STRUCTP (x))
+       {
+         if (SCM_INSTANCEP (x))
+           goto generic_equal;
+         else
+           return scm_i_struct_equalp (x, y);
+       }
       break;
     case scm_tc7_number:
       switch SCM_TYP16 (x)
@@ -349,14 +357,6 @@ scm_equal_p (SCM x, SCM y)
     case scm_tc7_wvect:
       return scm_i_vector_equal_p (x, y);
     }
-  /* Check equality between structs of equal type (see cell-type test above). 
*/
-  if (SCM_STRUCTP (x))
-    {
-      if (SCM_INSTANCEP (x))
-        goto generic_equal;
-      else
-        return scm_i_struct_equalp (x, y);
-    }
 
   /* Otherwise just return false. Dispatching to the generic is the wrong thing
      here, as we can hit this case for any two objects of the same type that we
diff --git a/libguile/numbers.c b/libguile/numbers.c
index 05840ef..81d689d 100644
--- a/libguile/numbers.c
+++ b/libguile/numbers.c
@@ -788,73 +788,10 @@ SCM_PRIMITIVE_GENERIC (scm_quotient, "quotient", 2, 0, 0,
        "Return the quotient of the numbers @var{x} and @var{y}.")
 #define FUNC_NAME s_scm_quotient
 {
-  if (SCM_LIKELY (SCM_I_INUMP (x)))
-    {
-      scm_t_inum xx = SCM_I_INUM (x);
-      if (SCM_LIKELY (SCM_I_INUMP (y)))
-       {
-         scm_t_inum yy = SCM_I_INUM (y);
-         if (SCM_UNLIKELY (yy == 0))
-           scm_num_overflow (s_scm_quotient);
-         else
-           {
-             scm_t_inum z = xx / yy;
-             if (SCM_LIKELY (SCM_FIXABLE (z)))
-               return SCM_I_MAKINUM (z);
-             else
-               return scm_i_inum2big (z);
-           }
-       }
-      else if (SCM_BIGP (y))
-       {
-         if ((SCM_I_INUM (x) == SCM_MOST_NEGATIVE_FIXNUM)
-             && (mpz_cmp_ui (SCM_I_BIG_MPZ (y),
-                              - SCM_MOST_NEGATIVE_FIXNUM) == 0))
-            {
-              /* Special case:  x == fixnum-min && y == abs (fixnum-min) */
-             scm_remember_upto_here_1 (y);
-              return SCM_I_MAKINUM (-1);
-            }
-         else
-           return SCM_INUM0;
-       }
-      else
-       SCM_WTA_DISPATCH_2 (g_scm_quotient, x, y, SCM_ARG2, s_scm_quotient);
-    }
-  else if (SCM_BIGP (x))
+  if (SCM_LIKELY (SCM_I_INUMP (x)) || SCM_LIKELY (SCM_BIGP (x)))
     {
-      if (SCM_LIKELY (SCM_I_INUMP (y)))
-       {
-         scm_t_inum yy = SCM_I_INUM (y);
-         if (SCM_UNLIKELY (yy == 0))
-           scm_num_overflow (s_scm_quotient);
-         else if (SCM_UNLIKELY (yy == 1))
-           return x;
-         else
-           {
-             SCM result = scm_i_mkbig ();
-             if (yy < 0)
-               {
-                 mpz_tdiv_q_ui (SCM_I_BIG_MPZ (result),
-                                SCM_I_BIG_MPZ (x),
-                                - yy);
-                 mpz_neg (SCM_I_BIG_MPZ (result), SCM_I_BIG_MPZ (result));
-               }
-             else
-               mpz_tdiv_q_ui (SCM_I_BIG_MPZ (result), SCM_I_BIG_MPZ (x), yy);
-             scm_remember_upto_here_1 (x);
-             return scm_i_normbig (result);
-           }
-       }
-      else if (SCM_BIGP (y))
-       {
-         SCM result = scm_i_mkbig ();
-         mpz_tdiv_q (SCM_I_BIG_MPZ (result),
-                     SCM_I_BIG_MPZ (x),
-                     SCM_I_BIG_MPZ (y));
-         scm_remember_upto_here_2 (x, y);
-         return scm_i_normbig (result);
-       }
+      if (SCM_LIKELY (SCM_I_INUMP (y)) || SCM_LIKELY (SCM_BIGP (y)))
+       return scm_truncate_quotient (x, y);
       else
        SCM_WTA_DISPATCH_2 (g_scm_quotient, x, y, SCM_ARG2, s_scm_quotient);
     }
@@ -872,64 +809,10 @@ SCM_PRIMITIVE_GENERIC (scm_remainder, "remainder", 2, 0, 
0,
        "@end lisp")
 #define FUNC_NAME s_scm_remainder
 {
-  if (SCM_LIKELY (SCM_I_INUMP (x)))
-    {
-      if (SCM_LIKELY (SCM_I_INUMP (y)))
-       {
-         scm_t_inum yy = SCM_I_INUM (y);
-         if (SCM_UNLIKELY (yy == 0))
-           scm_num_overflow (s_scm_remainder);
-         else
-           {
-             /* C99 specifies that "%" is the remainder corresponding to a
-                 quotient rounded towards zero, and that's also traditional
-                 for machine division, so z here should be well defined.  */
-             scm_t_inum z = SCM_I_INUM (x) % yy;
-             return SCM_I_MAKINUM (z);
-           }
-       }
-      else if (SCM_BIGP (y))
-       {
-         if ((SCM_I_INUM (x) == SCM_MOST_NEGATIVE_FIXNUM)
-             && (mpz_cmp_ui (SCM_I_BIG_MPZ (y),
-                              - SCM_MOST_NEGATIVE_FIXNUM) == 0))
-            {
-              /* Special case:  x == fixnum-min && y == abs (fixnum-min) */
-             scm_remember_upto_here_1 (y);
-              return SCM_INUM0;
-            }
-         else
-           return x;
-       }
-      else
-       SCM_WTA_DISPATCH_2 (g_scm_remainder, x, y, SCM_ARG2, s_scm_remainder);
-    }
-  else if (SCM_BIGP (x))
+  if (SCM_LIKELY (SCM_I_INUMP (x)) || SCM_LIKELY (SCM_BIGP (x)))
     {
-      if (SCM_LIKELY (SCM_I_INUMP (y)))
-       {
-         scm_t_inum yy = SCM_I_INUM (y);
-         if (SCM_UNLIKELY (yy == 0))
-           scm_num_overflow (s_scm_remainder);
-         else
-           {
-             SCM result = scm_i_mkbig ();
-             if (yy < 0)
-               yy = - yy;
-             mpz_tdiv_r_ui (SCM_I_BIG_MPZ (result), SCM_I_BIG_MPZ(x), yy);
-             scm_remember_upto_here_1 (x);
-             return scm_i_normbig (result);
-           }
-       }
-      else if (SCM_BIGP (y))
-       {
-         SCM result = scm_i_mkbig ();
-         mpz_tdiv_r (SCM_I_BIG_MPZ (result),
-                     SCM_I_BIG_MPZ (x),
-                     SCM_I_BIG_MPZ (y));
-         scm_remember_upto_here_2 (x, y);
-         return scm_i_normbig (result);
-       }
+      if (SCM_LIKELY (SCM_I_INUMP (y)) || SCM_LIKELY (SCM_BIGP (y)))
+       return scm_truncate_remainder (x, y);
       else
        SCM_WTA_DISPATCH_2 (g_scm_remainder, x, y, SCM_ARG2, s_scm_remainder);
     }
@@ -948,119 +831,10 @@ SCM_PRIMITIVE_GENERIC (scm_modulo, "modulo", 2, 0, 0,
        "@end lisp")
 #define FUNC_NAME s_scm_modulo
 {
-  if (SCM_LIKELY (SCM_I_INUMP (x)))
-    {
-      scm_t_inum xx = SCM_I_INUM (x);
-      if (SCM_LIKELY (SCM_I_INUMP (y)))
-       {
-         scm_t_inum yy = SCM_I_INUM (y);
-         if (SCM_UNLIKELY (yy == 0))
-           scm_num_overflow (s_scm_modulo);
-         else
-           {
-             /* C99 specifies that "%" is the remainder corresponding to a
-                 quotient rounded towards zero, and that's also traditional
-                 for machine division, so z here should be well defined.  */
-             scm_t_inum z = xx % yy;
-             scm_t_inum result;
-
-             if (yy < 0)
-               {
-                 if (z > 0)
-                   result = z + yy;
-                 else
-                   result = z;
-               }
-             else
-               {
-                 if (z < 0)
-                   result = z + yy;
-                 else
-                   result = z;
-               }
-             return SCM_I_MAKINUM (result);
-           }
-       }
-      else if (SCM_BIGP (y))
-       {
-         int sgn_y = mpz_sgn (SCM_I_BIG_MPZ (y));
-           {
-             mpz_t z_x;
-             SCM result;
-
-             if (sgn_y < 0)
-               {
-                 SCM pos_y = scm_i_clonebig (y, 0);
-                 /* do this after the last scm_op */
-                 mpz_init_set_si (z_x, xx);
-                 result = pos_y; /* re-use this bignum */
-                 mpz_mod (SCM_I_BIG_MPZ (result),
-                          z_x,
-                          SCM_I_BIG_MPZ (pos_y));        
-                 scm_remember_upto_here_1 (pos_y);
-               }
-             else
-               {
-                 result = scm_i_mkbig ();
-                 /* do this after the last scm_op */
-                 mpz_init_set_si (z_x, xx);
-                 mpz_mod (SCM_I_BIG_MPZ (result),
-                          z_x,
-                          SCM_I_BIG_MPZ (y));        
-                 scm_remember_upto_here_1 (y);
-               }
-        
-             if ((sgn_y < 0) && mpz_sgn (SCM_I_BIG_MPZ (result)) != 0)
-               mpz_add (SCM_I_BIG_MPZ (result),
-                        SCM_I_BIG_MPZ (y),
-                        SCM_I_BIG_MPZ (result));
-             scm_remember_upto_here_1 (y);
-             /* and do this before the next one */
-             mpz_clear (z_x);
-             return scm_i_normbig (result);
-           }
-       }
-      else
-       SCM_WTA_DISPATCH_2 (g_scm_modulo, x, y, SCM_ARG2, s_scm_modulo);
-    }
-  else if (SCM_BIGP (x))
+  if (SCM_LIKELY (SCM_I_INUMP (x)) || SCM_LIKELY (SCM_BIGP (x)))
     {
-      if (SCM_LIKELY (SCM_I_INUMP (y)))
-       {
-         scm_t_inum yy = SCM_I_INUM (y);
-         if (SCM_UNLIKELY (yy == 0))
-           scm_num_overflow (s_scm_modulo);
-         else
-           {
-             SCM result = scm_i_mkbig ();
-             mpz_mod_ui (SCM_I_BIG_MPZ (result),
-                         SCM_I_BIG_MPZ (x),
-                         (yy < 0) ? - yy : yy);
-             scm_remember_upto_here_1 (x);
-             if ((yy < 0) && (mpz_sgn (SCM_I_BIG_MPZ (result)) != 0))
-               mpz_sub_ui (SCM_I_BIG_MPZ (result),
-                           SCM_I_BIG_MPZ (result),
-                           - yy);
-             return scm_i_normbig (result);
-           }
-       }
-      else if (SCM_BIGP (y))
-       {
-         SCM result = scm_i_mkbig ();
-         int y_sgn = mpz_sgn (SCM_I_BIG_MPZ (y));
-         SCM pos_y = scm_i_clonebig (y, y_sgn >= 0);
-         mpz_mod (SCM_I_BIG_MPZ (result),
-                  SCM_I_BIG_MPZ (x),
-                  SCM_I_BIG_MPZ (pos_y));
-        
-         scm_remember_upto_here_1 (x);
-         if ((y_sgn < 0) && (mpz_sgn (SCM_I_BIG_MPZ (result)) != 0))
-           mpz_add (SCM_I_BIG_MPZ (result),
-                    SCM_I_BIG_MPZ (y),
-                    SCM_I_BIG_MPZ (result));
-         scm_remember_upto_here_2 (y, pos_y);
-         return scm_i_normbig (result);
-       }
+      if (SCM_LIKELY (SCM_I_INUMP (y)) || SCM_LIKELY (SCM_BIGP (y)))
+       return scm_floor_remainder (x, y);
       else
        SCM_WTA_DISPATCH_2 (g_scm_modulo, x, y, SCM_ARG2, s_scm_modulo);
     }
@@ -1069,23 +843,120 @@ SCM_PRIMITIVE_GENERIC (scm_modulo, "modulo", 2, 0, 0,
 }
 #undef FUNC_NAME
 
-static SCM scm_i_inexact_euclidean_quotient (double x, double y);
-static SCM scm_i_slow_exact_euclidean_quotient (SCM x, SCM y);
+/* two_valued_wta_dispatch_2 is a version of SCM_WTA_DISPATCH_2 for
+   two-valued functions.  It is called from primitive generics that take
+   two arguments and return two values, when the core procedure is
+   unable to handle the given argument types.  If there are GOOPS
+   methods for this primitive generic, it dispatches to GOOPS and, if
+   successful, expects two values to be returned, which are placed in
+   *rp1 and *rp2.  If there are no GOOPS methods, it throws a
+   wrong-type-arg exception.
+
+   FIXME: This obviously belongs somewhere else, but until we decide on
+   the right API, it is here as a static function, because it is needed
+   by the *_divide functions below.
+*/
+static void
+two_valued_wta_dispatch_2 (SCM gf, SCM a1, SCM a2, int pos,
+                          const char *subr, SCM *rp1, SCM *rp2)
+{
+  if (SCM_UNPACK (gf))
+    scm_i_extract_values_2 (scm_call_generic_2 (gf, a1, a2), rp1, rp2);
+  else
+    scm_wrong_type_arg (subr, pos, (pos == SCM_ARG1) ? a1 : a2);
+}
+
+SCM_DEFINE (scm_euclidean_quotient, "euclidean-quotient", 2, 0, 0,
+           (SCM x, SCM y),
+           "Return the integer @var{q} such that\n"
+           "@address@hidden = @address@hidden + @var{r}}\n"
+           "where @math{0 <= @var{r} < abs(@var{y})}.\n"
+           "@lisp\n"
+           "(euclidean-quotient 123 10) @result{} 12\n"
+           "(euclidean-quotient 123 -10) @result{} -12\n"
+           "(euclidean-quotient -123 10) @result{} -13\n"
+           "(euclidean-quotient -123 -10) @result{} 13\n"
+           "(euclidean-quotient -123.2 -63.5) @result{} 2.0\n"
+           "(euclidean-quotient 16/3 -10/7) @result{} -3\n"
+           "@end lisp")
+#define FUNC_NAME s_scm_euclidean_quotient
+{
+  if (scm_is_false (scm_negative_p (y)))
+    return scm_floor_quotient (x, y);
+  else
+    return scm_ceiling_quotient (x, y);
+}
+#undef FUNC_NAME
+
+SCM_DEFINE (scm_euclidean_remainder, "euclidean-remainder", 2, 0, 0,
+           (SCM x, SCM y),
+           "Return the real number @var{r} such that\n"
+           "@math{0 <= @var{r} < abs(@var{y})} and\n"
+           "@address@hidden = @address@hidden + @var{r}}\n"
+           "for some integer @var{q}.\n"
+           "@lisp\n"
+           "(euclidean-remainder 123 10) @result{} 3\n"
+           "(euclidean-remainder 123 -10) @result{} 3\n"
+           "(euclidean-remainder -123 10) @result{} 7\n"
+           "(euclidean-remainder -123 -10) @result{} 7\n"
+           "(euclidean-remainder -123.2 -63.5) @result{} 3.8\n"
+           "(euclidean-remainder 16/3 -10/7) @result{} 22/21\n"
+           "@end lisp")
+#define FUNC_NAME s_scm_euclidean_remainder
+{
+  if (scm_is_false (scm_negative_p (y)))
+    return scm_floor_remainder (x, y);
+  else
+    return scm_ceiling_remainder (x, y);
+}
+#undef FUNC_NAME
+
+SCM_DEFINE (scm_i_euclidean_divide, "euclidean/", 2, 0, 0,
+           (SCM x, SCM y),
+           "Return the integer @var{q} and the real number @var{r}\n"
+           "such that @address@hidden = @address@hidden + @var{r}}\n"
+           "and @math{0 <= @var{r} < abs(@var{y})}.\n"
+           "@lisp\n"
+           "(euclidean/ 123 10) @result{} 12 and 3\n"
+           "(euclidean/ 123 -10) @result{} -12 and 3\n"
+           "(euclidean/ -123 10) @result{} -13 and 7\n"
+           "(euclidean/ -123 -10) @result{} 13 and 7\n"
+           "(euclidean/ -123.2 -63.5) @result{} 2.0 and 3.8\n"
+           "(euclidean/ 16/3 -10/7) @result{} -3 and 22/21\n"
+           "@end lisp")
+#define FUNC_NAME s_scm_i_euclidean_divide
+{
+  if (scm_is_false (scm_negative_p (y)))
+    return scm_i_floor_divide (x, y);
+  else
+    return scm_i_ceiling_divide (x, y);
+}
+#undef FUNC_NAME
+
+void
+scm_euclidean_divide (SCM x, SCM y, SCM *qp, SCM *rp)
+{
+  if (scm_is_false (scm_negative_p (y)))
+    return scm_floor_divide (x, y, qp, rp);
+  else
+    return scm_ceiling_divide (x, y, qp, rp);
+}
+
+static SCM scm_i_inexact_floor_quotient (double x, double y);
+static SCM scm_i_exact_rational_floor_quotient (SCM x, SCM y);
 
-SCM_PRIMITIVE_GENERIC (scm_euclidean_quotient, "euclidean-quotient", 2, 0, 0,
+SCM_PRIMITIVE_GENERIC (scm_floor_quotient, "floor-quotient", 2, 0, 0,
                       (SCM x, SCM y),
-                      "Return the integer @var{q} such that\n"
-                      "@address@hidden = @address@hidden + @var{r}}\n"
-                      "where @math{0 <= @var{r} < abs(@var{y})}.\n"
+                      "Return the floor of @address@hidden / @var{y}}.\n"
                       "@lisp\n"
-                      "(euclidean-quotient 123 10) @result{} 12\n"
-                      "(euclidean-quotient 123 -10) @result{} -12\n"
-                      "(euclidean-quotient -123 10) @result{} -13\n"
-                      "(euclidean-quotient -123 -10) @result{} 13\n"
-                      "(euclidean-quotient -123.2 -63.5) @result{} 2.0\n"
-                      "(euclidean-quotient 16/3 -10/7) @result{} -3\n"
+                      "(floor-quotient 123 10) @result{} 12\n"
+                      "(floor-quotient 123 -10) @result{} -13\n"
+                      "(floor-quotient -123 10) @result{} -13\n"
+                      "(floor-quotient -123 -10) @result{} 12\n"
+                      "(floor-quotient -123.2 -63.5) @result{} 1.0\n"
+                      "(floor-quotient 16/3 -10/7) @result{} -4\n"
                       "@end lisp")
-#define FUNC_NAME s_scm_euclidean_quotient
+#define FUNC_NAME s_scm_floor_quotient
 {
   if (SCM_LIKELY (SCM_I_INUMP (x)))
     {
@@ -1093,42 +964,39 @@ SCM_PRIMITIVE_GENERIC (scm_euclidean_quotient, 
"euclidean-quotient", 2, 0, 0,
       if (SCM_LIKELY (SCM_I_INUMP (y)))
        {
          scm_t_inum yy = SCM_I_INUM (y);
-         if (SCM_UNLIKELY (yy == 0))
-           scm_num_overflow (s_scm_euclidean_quotient);
-         else
+         scm_t_inum xx1 = xx;
+         scm_t_inum qq;
+         if (SCM_LIKELY (yy > 0))
            {
-             scm_t_inum qq = xx / yy;
-             if (xx < qq * yy)
-               {
-                 if (yy > 0)
-                   qq--;
-                 else
-                   qq++;
-               }
-             if (SCM_LIKELY (SCM_FIXABLE (qq)))
-               return SCM_I_MAKINUM (qq);
-             else
-               return scm_i_inum2big (qq);
+             if (SCM_UNLIKELY (xx < 0))
+               xx1 = xx - yy + 1;
            }
+         else if (SCM_UNLIKELY (yy == 0))
+           scm_num_overflow (s_scm_floor_quotient);
+         else if (xx > 0)
+           xx1 = xx - yy - 1;
+         qq = xx1 / yy;
+         if (SCM_LIKELY (SCM_FIXABLE (qq)))
+           return SCM_I_MAKINUM (qq);
+         else
+           return scm_i_inum2big (qq);
        }
       else if (SCM_BIGP (y))
        {
-         if (xx >= 0)
-           return SCM_INUM0;
+         int sign = mpz_sgn (SCM_I_BIG_MPZ (y));
+         scm_remember_upto_here_1 (y);
+         if (sign > 0)
+           return SCM_I_MAKINUM ((xx < 0) ? -1 : 0);
          else
-           {
-             scm_t_inum qq = - mpz_sgn (SCM_I_BIG_MPZ (y));
-             scm_remember_upto_here_1 (y);
-             return SCM_I_MAKINUM (qq);
-           }
+           return SCM_I_MAKINUM ((xx > 0) ? -1 : 0);
        }
       else if (SCM_REALP (y))
-       return scm_i_inexact_euclidean_quotient (xx, SCM_REAL_VALUE (y));
+       return scm_i_inexact_floor_quotient (xx, SCM_REAL_VALUE (y));
       else if (SCM_FRACTIONP (y))
-       return scm_i_slow_exact_euclidean_quotient (x, y);
+       return scm_i_exact_rational_floor_quotient (x, y);
       else
-       SCM_WTA_DISPATCH_2 (g_scm_euclidean_quotient, x, y, SCM_ARG2,
-                           s_scm_euclidean_quotient);
+       SCM_WTA_DISPATCH_2 (g_scm_floor_quotient, x, y, SCM_ARG2,
+                           s_scm_floor_quotient);
     }
   else if (SCM_BIGP (x))
     {
@@ -1136,7 +1004,7 @@ SCM_PRIMITIVE_GENERIC (scm_euclidean_quotient, 
"euclidean-quotient", 2, 0, 0,
        {
          scm_t_inum yy = SCM_I_INUM (y);
          if (SCM_UNLIKELY (yy == 0))
-           scm_num_overflow (s_scm_euclidean_quotient);
+           scm_num_overflow (s_scm_floor_quotient);
          else if (SCM_UNLIKELY (yy == 1))
            return x;
          else
@@ -1146,7 +1014,7 @@ SCM_PRIMITIVE_GENERIC (scm_euclidean_quotient, 
"euclidean-quotient", 2, 0, 0,
                mpz_fdiv_q_ui (SCM_I_BIG_MPZ (q), SCM_I_BIG_MPZ (x), yy);
              else
                {
-                 mpz_fdiv_q_ui (SCM_I_BIG_MPZ (q), SCM_I_BIG_MPZ (x), -yy);
+                 mpz_cdiv_q_ui (SCM_I_BIG_MPZ (q), SCM_I_BIG_MPZ (x), -yy);
                  mpz_neg (SCM_I_BIG_MPZ (q), SCM_I_BIG_MPZ (q));
                }
              scm_remember_upto_here_1 (x);
@@ -1156,101 +1024,82 @@ SCM_PRIMITIVE_GENERIC (scm_euclidean_quotient, 
"euclidean-quotient", 2, 0, 0,
       else if (SCM_BIGP (y))
        {
          SCM q = scm_i_mkbig ();
-         if (mpz_sgn (SCM_I_BIG_MPZ (y)) > 0)
-           mpz_fdiv_q (SCM_I_BIG_MPZ (q),
-                       SCM_I_BIG_MPZ (x),
-                       SCM_I_BIG_MPZ (y));
-         else
-           mpz_cdiv_q (SCM_I_BIG_MPZ (q),
-                       SCM_I_BIG_MPZ (x),
-                       SCM_I_BIG_MPZ (y));
+         mpz_fdiv_q (SCM_I_BIG_MPZ (q),
+                     SCM_I_BIG_MPZ (x),
+                     SCM_I_BIG_MPZ (y));
          scm_remember_upto_here_2 (x, y);
          return scm_i_normbig (q);
        }
       else if (SCM_REALP (y))
-       return scm_i_inexact_euclidean_quotient
+       return scm_i_inexact_floor_quotient
          (scm_i_big2dbl (x), SCM_REAL_VALUE (y));
       else if (SCM_FRACTIONP (y))
-       return scm_i_slow_exact_euclidean_quotient (x, y);
+       return scm_i_exact_rational_floor_quotient (x, y);
       else
-       SCM_WTA_DISPATCH_2 (g_scm_euclidean_quotient, x, y, SCM_ARG2,
-                           s_scm_euclidean_quotient);
+       SCM_WTA_DISPATCH_2 (g_scm_floor_quotient, x, y, SCM_ARG2,
+                           s_scm_floor_quotient);
     }
   else if (SCM_REALP (x))
     {
       if (SCM_REALP (y) || SCM_I_INUMP (y) ||
          SCM_BIGP (y) || SCM_FRACTIONP (y))
-       return scm_i_inexact_euclidean_quotient
+       return scm_i_inexact_floor_quotient
          (SCM_REAL_VALUE (x), scm_to_double (y));
       else
-       SCM_WTA_DISPATCH_2 (g_scm_euclidean_quotient, x, y, SCM_ARG2,
-                           s_scm_euclidean_quotient);
+       SCM_WTA_DISPATCH_2 (g_scm_floor_quotient, x, y, SCM_ARG2,
+                           s_scm_floor_quotient);
     }
   else if (SCM_FRACTIONP (x))
     {
       if (SCM_REALP (y))
-       return scm_i_inexact_euclidean_quotient
+       return scm_i_inexact_floor_quotient
          (scm_i_fraction2double (x), SCM_REAL_VALUE (y));
+      else if (SCM_I_INUMP (y) || SCM_BIGP (y) || SCM_FRACTIONP (y))
+       return scm_i_exact_rational_floor_quotient (x, y);
       else
-       return scm_i_slow_exact_euclidean_quotient (x, y);
+       SCM_WTA_DISPATCH_2 (g_scm_floor_quotient, x, y, SCM_ARG2,
+                           s_scm_floor_quotient);
     }
   else
-    SCM_WTA_DISPATCH_2 (g_scm_euclidean_quotient, x, y, SCM_ARG1,
-                       s_scm_euclidean_quotient);
+    SCM_WTA_DISPATCH_2 (g_scm_floor_quotient, x, y, SCM_ARG1,
+                       s_scm_floor_quotient);
 }
 #undef FUNC_NAME
 
 static SCM
-scm_i_inexact_euclidean_quotient (double x, double y)
+scm_i_inexact_floor_quotient (double x, double y)
 {
-  if (SCM_LIKELY (y > 0))
-    return scm_from_double (floor (x / y));
-  else if (SCM_LIKELY (y < 0))
-    return scm_from_double (ceil (x / y));
-  else if (y == 0)
-    scm_num_overflow (s_scm_euclidean_quotient);  /* or return a NaN? */
+  if (SCM_UNLIKELY (y == 0))
+    scm_num_overflow (s_scm_floor_quotient);  /* or return a NaN? */
   else
-    return scm_nan ();
+    return scm_from_double (floor (x / y));
 }
 
-/* Compute exact euclidean_quotient the slow way.
-   We use this only if both arguments are exact,
-   and at least one of them is a fraction */
 static SCM
-scm_i_slow_exact_euclidean_quotient (SCM x, SCM y)
+scm_i_exact_rational_floor_quotient (SCM x, SCM y)
 {
-  if (!(SCM_I_INUMP (x) || SCM_BIGP (x) || SCM_FRACTIONP (x)))
-    SCM_WTA_DISPATCH_2 (g_scm_euclidean_quotient, x, y, SCM_ARG1,
-                       s_scm_euclidean_quotient);
-  else if (!(SCM_I_INUMP (y) || SCM_BIGP (y) || SCM_FRACTIONP (y)))
-    SCM_WTA_DISPATCH_2 (g_scm_euclidean_quotient, x, y, SCM_ARG2,
-                       s_scm_euclidean_quotient);
-  else if (scm_is_true (scm_positive_p (y)))
-    return scm_floor (scm_divide (x, y));
-  else if (scm_is_true (scm_negative_p (y)))
-    return scm_ceiling (scm_divide (x, y));
-  else
-    scm_num_overflow (s_scm_euclidean_quotient);
+  return scm_floor_quotient
+    (scm_product (scm_numerator (x), scm_denominator (y)),
+     scm_product (scm_numerator (y), scm_denominator (x)));
 }
 
-static SCM scm_i_inexact_euclidean_remainder (double x, double y);
-static SCM scm_i_slow_exact_euclidean_remainder (SCM x, SCM y);
+static SCM scm_i_inexact_floor_remainder (double x, double y);
+static SCM scm_i_exact_rational_floor_remainder (SCM x, SCM y);
 
-SCM_PRIMITIVE_GENERIC (scm_euclidean_remainder, "euclidean-remainder", 2, 0, 0,
+SCM_PRIMITIVE_GENERIC (scm_floor_remainder, "floor-remainder", 2, 0, 0,
                       (SCM x, SCM y),
                       "Return the real number @var{r} such that\n"
-                      "@math{0 <= @var{r} < abs(@var{y})} and\n"
                       "@address@hidden = @address@hidden + @var{r}}\n"
-                      "for some integer @var{q}.\n"
+                      "where @address@hidden = floor(@var{x} / @var{y})}.\n"
                       "@lisp\n"
-                      "(euclidean-remainder 123 10) @result{} 3\n"
-                      "(euclidean-remainder 123 -10) @result{} 3\n"
-                      "(euclidean-remainder -123 10) @result{} 7\n"
-                      "(euclidean-remainder -123 -10) @result{} 7\n"
-                      "(euclidean-remainder -123.2 -63.5) @result{} 3.8\n"
-                      "(euclidean-remainder 16/3 -10/7) @result{} 22/21\n"
+                      "(floor-remainder 123 10) @result{} 3\n"
+                      "(floor-remainder 123 -10) @result{} -7\n"
+                      "(floor-remainder -123 10) @result{} 7\n"
+                      "(floor-remainder -123 -10) @result{} -3\n"
+                      "(floor-remainder -123.2 -63.5) @result{} -59.7\n"
+                      "(floor-remainder 16/3 -10/7) @result{} -8/21\n"
                       "@end lisp")
-#define FUNC_NAME s_scm_euclidean_remainder
+#define FUNC_NAME s_scm_floor_remainder
 {
   if (SCM_LIKELY (SCM_I_INUMP (x)))
     {
@@ -1259,45 +1108,55 @@ SCM_PRIMITIVE_GENERIC (scm_euclidean_remainder, 
"euclidean-remainder", 2, 0, 0,
        {
          scm_t_inum yy = SCM_I_INUM (y);
          if (SCM_UNLIKELY (yy == 0))
-           scm_num_overflow (s_scm_euclidean_remainder);
+           scm_num_overflow (s_scm_floor_remainder);
          else
            {
              scm_t_inum rr = xx % yy;
-             if (rr >= 0)
-               return SCM_I_MAKINUM (rr);
-             else if (yy > 0)
-               return SCM_I_MAKINUM (rr + yy);
+             int needs_adjustment;
+
+             if (SCM_LIKELY (yy > 0))
+               needs_adjustment = (rr < 0);
              else
-               return SCM_I_MAKINUM (rr - yy);
+               needs_adjustment = (rr > 0);
+
+             if (needs_adjustment)
+               rr += yy;
+             return SCM_I_MAKINUM (rr);
            }
        }
       else if (SCM_BIGP (y))
        {
-         if (xx >= 0)
-           return x;
-         else if (mpz_sgn (SCM_I_BIG_MPZ (y)) > 0)
+         int sign = mpz_sgn (SCM_I_BIG_MPZ (y));
+         scm_remember_upto_here_1 (y);
+         if (sign > 0)
            {
-             SCM r = scm_i_mkbig ();
-             mpz_sub_ui (SCM_I_BIG_MPZ (r), SCM_I_BIG_MPZ (y), -xx);
-             scm_remember_upto_here_1 (y);
-             return scm_i_normbig (r);
+             if (xx < 0)
+               {
+                 SCM r = scm_i_mkbig ();
+                 mpz_sub_ui (SCM_I_BIG_MPZ (r), SCM_I_BIG_MPZ (y), -xx);
+                 scm_remember_upto_here_1 (y);
+                 return scm_i_normbig (r);
+               }
+             else
+               return x;
            }
+         else if (xx <= 0)
+           return x;
          else
            {
              SCM r = scm_i_mkbig ();
-             mpz_add_ui (SCM_I_BIG_MPZ (r), SCM_I_BIG_MPZ (y), -xx);
+             mpz_add_ui (SCM_I_BIG_MPZ (r), SCM_I_BIG_MPZ (y), xx);
              scm_remember_upto_here_1 (y);
-             mpz_neg (SCM_I_BIG_MPZ (r), SCM_I_BIG_MPZ (r));
              return scm_i_normbig (r);
            }
        }
       else if (SCM_REALP (y))
-       return scm_i_inexact_euclidean_remainder (xx, SCM_REAL_VALUE (y));
+       return scm_i_inexact_floor_remainder (xx, SCM_REAL_VALUE (y));
       else if (SCM_FRACTIONP (y))
-       return scm_i_slow_exact_euclidean_remainder (x, y);
+       return scm_i_exact_rational_floor_remainder (x, y);
       else
-       SCM_WTA_DISPATCH_2 (g_scm_euclidean_remainder, x, y, SCM_ARG2,
-                           s_scm_euclidean_remainder);
+       SCM_WTA_DISPATCH_2 (g_scm_floor_remainder, x, y, SCM_ARG2,
+                           s_scm_floor_remainder);
     }
   else if (SCM_BIGP (x))
     {
@@ -1305,13 +1164,14 @@ SCM_PRIMITIVE_GENERIC (scm_euclidean_remainder, 
"euclidean-remainder", 2, 0, 0,
        {
          scm_t_inum yy = SCM_I_INUM (y);
          if (SCM_UNLIKELY (yy == 0))
-           scm_num_overflow (s_scm_euclidean_remainder);
+           scm_num_overflow (s_scm_floor_remainder);
          else
            {
              scm_t_inum rr;
-             if (yy < 0)
-               yy = -yy;
-             rr = mpz_fdiv_ui (SCM_I_BIG_MPZ (x), yy);
+             if (yy > 0)
+               rr = mpz_fdiv_ui (SCM_I_BIG_MPZ (x), yy);
+             else
+               rr = -mpz_cdiv_ui (SCM_I_BIG_MPZ (x), -yy);
              scm_remember_upto_here_1 (x);
              return SCM_I_MAKINUM (rr);
            }
@@ -1319,168 +1179,184 @@ SCM_PRIMITIVE_GENERIC (scm_euclidean_remainder, 
"euclidean-remainder", 2, 0, 0,
       else if (SCM_BIGP (y))
        {
          SCM r = scm_i_mkbig ();
-         mpz_mod (SCM_I_BIG_MPZ (r),
-                  SCM_I_BIG_MPZ (x),
-                  SCM_I_BIG_MPZ (y));
+         mpz_fdiv_r (SCM_I_BIG_MPZ (r),
+                     SCM_I_BIG_MPZ (x),
+                     SCM_I_BIG_MPZ (y));
          scm_remember_upto_here_2 (x, y);
          return scm_i_normbig (r);
        }
       else if (SCM_REALP (y))
-       return scm_i_inexact_euclidean_remainder
+       return scm_i_inexact_floor_remainder
          (scm_i_big2dbl (x), SCM_REAL_VALUE (y));
       else if (SCM_FRACTIONP (y))
-       return scm_i_slow_exact_euclidean_remainder (x, y);
+       return scm_i_exact_rational_floor_remainder (x, y);
       else
-       SCM_WTA_DISPATCH_2 (g_scm_euclidean_remainder, x, y, SCM_ARG2,
-                           s_scm_euclidean_remainder);
+       SCM_WTA_DISPATCH_2 (g_scm_floor_remainder, x, y, SCM_ARG2,
+                           s_scm_floor_remainder);
     }
   else if (SCM_REALP (x))
     {
       if (SCM_REALP (y) || SCM_I_INUMP (y) ||
          SCM_BIGP (y) || SCM_FRACTIONP (y))
-       return scm_i_inexact_euclidean_remainder
+       return scm_i_inexact_floor_remainder
          (SCM_REAL_VALUE (x), scm_to_double (y));
       else
-       SCM_WTA_DISPATCH_2 (g_scm_euclidean_remainder, x, y, SCM_ARG2,
-                           s_scm_euclidean_remainder);
+       SCM_WTA_DISPATCH_2 (g_scm_floor_remainder, x, y, SCM_ARG2,
+                           s_scm_floor_remainder);
     }
   else if (SCM_FRACTIONP (x))
     {
       if (SCM_REALP (y))
-       return scm_i_inexact_euclidean_remainder
+       return scm_i_inexact_floor_remainder
          (scm_i_fraction2double (x), SCM_REAL_VALUE (y));
+      else if (SCM_I_INUMP (y) || SCM_BIGP (y) || SCM_FRACTIONP (y))
+       return scm_i_exact_rational_floor_remainder (x, y);
       else
-       return scm_i_slow_exact_euclidean_remainder (x, y);
+       SCM_WTA_DISPATCH_2 (g_scm_floor_remainder, x, y, SCM_ARG2,
+                           s_scm_floor_remainder);
     }
   else
-    SCM_WTA_DISPATCH_2 (g_scm_euclidean_remainder, x, y, SCM_ARG1,
-                       s_scm_euclidean_remainder);
+    SCM_WTA_DISPATCH_2 (g_scm_floor_remainder, x, y, SCM_ARG1,
+                       s_scm_floor_remainder);
 }
 #undef FUNC_NAME
 
 static SCM
-scm_i_inexact_euclidean_remainder (double x, double y)
+scm_i_inexact_floor_remainder (double x, double y)
 {
-  double q;
-
   /* Although it would be more efficient to use fmod here, we can't
      because it would in some cases produce results inconsistent with
-     scm_i_inexact_euclidean_quotient, such that x != q * y + r (not
-     even close).  In particular, when x is very close to a multiple of
-     y, then r might be either 0.0 or abs(y)-epsilon, but those two
-     cases must correspond to different choices of q.  If r = 0.0 then q
-     must be x/y, and if r = abs(y) then q must be (x-r)/y.  If quotient
-     chooses one and remainder chooses the other, it would be bad.  This
-     problem was observed with x = 130.0 and y = 10/7. */
-  if (SCM_LIKELY (y > 0))
-    q = floor (x / y);
-  else if (SCM_LIKELY (y < 0))
-    q = ceil (x / y);
-  else if (y == 0)
-    scm_num_overflow (s_scm_euclidean_remainder);  /* or return a NaN? */
+     scm_i_inexact_floor_quotient, such that x != q * y + r (not even
+     close).  In particular, when x is very close to a multiple of y,
+     then r might be either 0.0 or y, but those two cases must
+     correspond to different choices of q.  If r = 0.0 then q must be
+     x/y, and if r = y then q must be x/y-1.  If quotient chooses one
+     and remainder chooses the other, it would be bad.  */
+  if (SCM_UNLIKELY (y == 0))
+    scm_num_overflow (s_scm_floor_remainder);  /* or return a NaN? */
   else
-    return scm_nan ();
-  return scm_from_double (x - q * y);
+    return scm_from_double (x - y * floor (x / y));
 }
 
-/* Compute exact euclidean_remainder the slow way.
-   We use this only if both arguments are exact,
-   and at least one of them is a fraction */
 static SCM
-scm_i_slow_exact_euclidean_remainder (SCM x, SCM y)
+scm_i_exact_rational_floor_remainder (SCM x, SCM y)
 {
-  SCM q;
-
-  if (!(SCM_I_INUMP (x) || SCM_BIGP (x) || SCM_FRACTIONP (x)))
-    SCM_WTA_DISPATCH_2 (g_scm_euclidean_remainder, x, y, SCM_ARG1,
-                       s_scm_euclidean_remainder);
-  else if (!(SCM_I_INUMP (y) || SCM_BIGP (y) || SCM_FRACTIONP (y)))
-    SCM_WTA_DISPATCH_2 (g_scm_euclidean_remainder, x, y, SCM_ARG2,
-                       s_scm_euclidean_remainder);
-  else if (scm_is_true (scm_positive_p (y)))
-    q = scm_floor (scm_divide (x, y));
-  else if (scm_is_true (scm_negative_p (y)))
-    q = scm_ceiling (scm_divide (x, y));
-  else
-    scm_num_overflow (s_scm_euclidean_remainder);
-  return scm_difference (x, scm_product (y, q));
+  SCM xd = scm_denominator (x);
+  SCM yd = scm_denominator (y);
+  SCM r1 = scm_floor_remainder (scm_product (scm_numerator (x), yd),
+                               scm_product (scm_numerator (y), xd));
+  return scm_divide (r1, scm_product (xd, yd));
 }
 
 
-static SCM scm_i_inexact_euclidean_divide (double x, double y);
-static SCM scm_i_slow_exact_euclidean_divide (SCM x, SCM y);
+static void scm_i_inexact_floor_divide (double x, double y,
+                                       SCM *qp, SCM *rp);
+static void scm_i_exact_rational_floor_divide (SCM x, SCM y,
+                                              SCM *qp, SCM *rp);
 
-SCM_PRIMITIVE_GENERIC (scm_euclidean_divide, "euclidean/", 2, 0, 0,
+SCM_PRIMITIVE_GENERIC (scm_i_floor_divide, "floor/", 2, 0, 0,
                       (SCM x, SCM y),
                       "Return the integer @var{q} and the real number 
@var{r}\n"
                       "such that @address@hidden = @address@hidden + 
@var{r}}\n"
-                      "and @math{0 <= @var{r} < abs(@var{y})}.\n"
+                      "and @address@hidden = floor(@var{x} / @var{y})}.\n"
                       "@lisp\n"
-                      "(euclidean/ 123 10) @result{} 12 and 3\n"
-                      "(euclidean/ 123 -10) @result{} -12 and 3\n"
-                      "(euclidean/ -123 10) @result{} -13 and 7\n"
-                      "(euclidean/ -123 -10) @result{} 13 and 7\n"
-                      "(euclidean/ -123.2 -63.5) @result{} 2.0 and 3.8\n"
-                      "(euclidean/ 16/3 -10/7) @result{} -3 and 22/21\n"
+                      "(floor/ 123 10) @result{} 12 and 3\n"
+                      "(floor/ 123 -10) @result{} -13 and -7\n"
+                      "(floor/ -123 10) @result{} -13 and 7\n"
+                      "(floor/ -123 -10) @result{} 12 and -3\n"
+                      "(floor/ -123.2 -63.5) @result{} 1.0 and -59.7\n"
+                      "(floor/ 16/3 -10/7) @result{} -4 and -8/21\n"
                       "@end lisp")
-#define FUNC_NAME s_scm_euclidean_divide
+#define FUNC_NAME s_scm_i_floor_divide
 {
-  if (SCM_LIKELY (SCM_I_INUMP (x)))
-    {
-      scm_t_inum xx = SCM_I_INUM (x);
-      if (SCM_LIKELY (SCM_I_INUMP (y)))
-       {
-         scm_t_inum yy = SCM_I_INUM (y);
-         if (SCM_UNLIKELY (yy == 0))
-           scm_num_overflow (s_scm_euclidean_divide);
+  SCM q, r;
+
+  scm_floor_divide(x, y, &q, &r);
+  return scm_values (scm_list_2 (q, r));
+}
+#undef FUNC_NAME
+
+#define s_scm_floor_divide s_scm_i_floor_divide
+#define g_scm_floor_divide g_scm_i_floor_divide
+
+void
+scm_floor_divide (SCM x, SCM y, SCM *qp, SCM *rp)
+{
+  if (SCM_LIKELY (SCM_I_INUMP (x)))
+    {
+      scm_t_inum xx = SCM_I_INUM (x);
+      if (SCM_LIKELY (SCM_I_INUMP (y)))
+       {
+         scm_t_inum yy = SCM_I_INUM (y);
+         if (SCM_UNLIKELY (yy == 0))
+           scm_num_overflow (s_scm_floor_divide);
          else
            {
              scm_t_inum qq = xx / yy;
              scm_t_inum rr = xx % yy;
-             SCM q;
+             int needs_adjustment;
 
-             if (rr < 0)
+             if (SCM_LIKELY (yy > 0))
+               needs_adjustment = (rr < 0);
+             else
+               needs_adjustment = (rr > 0);
+
+             if (needs_adjustment)
                {
-                 if (yy > 0)
-                   { rr += yy; qq--; }
-                 else
-                   { rr -= yy; qq++; }
+                 rr += yy;
+                 qq--;
                }
+
              if (SCM_LIKELY (SCM_FIXABLE (qq)))
-               q = SCM_I_MAKINUM (qq);
+               *qp = SCM_I_MAKINUM (qq);
              else
-               q = scm_i_inum2big (qq);
-             return scm_values (scm_list_2 (q, SCM_I_MAKINUM (rr)));
+               *qp = scm_i_inum2big (qq);
+             *rp = SCM_I_MAKINUM (rr);
            }
+         return;
        }
       else if (SCM_BIGP (y))
        {
-         if (xx >= 0)
-           return scm_values (scm_list_2 (SCM_INUM0, x));
-         else if (mpz_sgn (SCM_I_BIG_MPZ (y)) > 0)
+         int sign = mpz_sgn (SCM_I_BIG_MPZ (y));
+         scm_remember_upto_here_1 (y);
+         if (sign > 0)
            {
-             SCM r = scm_i_mkbig ();
-             mpz_sub_ui (SCM_I_BIG_MPZ (r), SCM_I_BIG_MPZ (y), -xx);
-             scm_remember_upto_here_1 (y);
-             return scm_values
-               (scm_list_2 (SCM_I_MAKINUM (-1), scm_i_normbig (r)));
+             if (xx < 0)
+               {
+                 SCM r = scm_i_mkbig ();
+                 mpz_sub_ui (SCM_I_BIG_MPZ (r), SCM_I_BIG_MPZ (y), -xx);
+                 scm_remember_upto_here_1 (y);
+                 *qp = SCM_I_MAKINUM (-1);
+                 *rp = scm_i_normbig (r);
+               }
+             else
+               {
+                 *qp = SCM_INUM0;
+                 *rp = x;
+               }
+           }
+         else if (xx <= 0)
+           {
+             *qp = SCM_INUM0;
+             *rp = x;
            }
          else
            {
              SCM r = scm_i_mkbig ();
-             mpz_add_ui (SCM_I_BIG_MPZ (r), SCM_I_BIG_MPZ (y), -xx);
+             mpz_add_ui (SCM_I_BIG_MPZ (r), SCM_I_BIG_MPZ (y), xx);
              scm_remember_upto_here_1 (y);
-             mpz_neg (SCM_I_BIG_MPZ (r), SCM_I_BIG_MPZ (r));
-             return scm_values (scm_list_2 (SCM_INUM1, scm_i_normbig (r)));
+             *qp = SCM_I_MAKINUM (-1);
+             *rp = scm_i_normbig (r);
            }
+         return;
        }
       else if (SCM_REALP (y))
-       return scm_i_inexact_euclidean_divide (xx, SCM_REAL_VALUE (y));
+       return scm_i_inexact_floor_divide (xx, SCM_REAL_VALUE (y), qp, rp);
       else if (SCM_FRACTIONP (y))
-       return scm_i_slow_exact_euclidean_divide (x, y);
+       return scm_i_exact_rational_floor_divide (x, y, qp, rp);
       else
-       SCM_WTA_DISPATCH_2 (g_scm_euclidean_divide, x, y, SCM_ARG2,
-                           s_scm_euclidean_divide);
+       return two_valued_wta_dispatch_2 (g_scm_floor_divide, x, y, SCM_ARG2,
+                                         s_scm_floor_divide, qp, rp);
     }
   else if (SCM_BIGP (x))
     {
@@ -1488,132 +1364,114 @@ SCM_PRIMITIVE_GENERIC (scm_euclidean_divide, 
"euclidean/", 2, 0, 0,
        {
          scm_t_inum yy = SCM_I_INUM (y);
          if (SCM_UNLIKELY (yy == 0))
-           scm_num_overflow (s_scm_euclidean_divide);
+           scm_num_overflow (s_scm_floor_divide);
          else
            {
              SCM q = scm_i_mkbig ();
-             scm_t_inum rr;
+             SCM r = scm_i_mkbig ();
              if (yy > 0)
-               rr = mpz_fdiv_q_ui (SCM_I_BIG_MPZ (q),
-                                   SCM_I_BIG_MPZ (x), yy);
+               mpz_fdiv_qr_ui (SCM_I_BIG_MPZ (q), SCM_I_BIG_MPZ (r),
+                               SCM_I_BIG_MPZ (x), yy);
              else
                {
-                 rr = mpz_fdiv_q_ui (SCM_I_BIG_MPZ (q),
-                                     SCM_I_BIG_MPZ (x), -yy);
+                 mpz_cdiv_qr_ui (SCM_I_BIG_MPZ (q), SCM_I_BIG_MPZ (r),
+                                 SCM_I_BIG_MPZ (x), -yy);
                  mpz_neg (SCM_I_BIG_MPZ (q), SCM_I_BIG_MPZ (q));
                }
              scm_remember_upto_here_1 (x);
-             return scm_values (scm_list_2 (scm_i_normbig (q),
-                                            SCM_I_MAKINUM (rr)));
+             *qp = scm_i_normbig (q);
+             *rp = scm_i_normbig (r);
            }
+         return;
        }
       else if (SCM_BIGP (y))
        {
          SCM q = scm_i_mkbig ();
          SCM r = scm_i_mkbig ();
-         if (mpz_sgn (SCM_I_BIG_MPZ (y)) > 0)
-           mpz_fdiv_qr (SCM_I_BIG_MPZ (q), SCM_I_BIG_MPZ (r),
-                        SCM_I_BIG_MPZ (x), SCM_I_BIG_MPZ (y));
-         else
-           mpz_cdiv_qr (SCM_I_BIG_MPZ (q), SCM_I_BIG_MPZ (r),
-                        SCM_I_BIG_MPZ (x), SCM_I_BIG_MPZ (y));
+         mpz_fdiv_qr (SCM_I_BIG_MPZ (q), SCM_I_BIG_MPZ (r),
+                      SCM_I_BIG_MPZ (x), SCM_I_BIG_MPZ (y));
          scm_remember_upto_here_2 (x, y);
-         return scm_values (scm_list_2 (scm_i_normbig (q),
-                                        scm_i_normbig (r)));
+         *qp = scm_i_normbig (q);
+         *rp = scm_i_normbig (r);
+         return;
        }
       else if (SCM_REALP (y))
-       return scm_i_inexact_euclidean_divide
-         (scm_i_big2dbl (x), SCM_REAL_VALUE (y));
+       return scm_i_inexact_floor_divide
+         (scm_i_big2dbl (x), SCM_REAL_VALUE (y), qp, rp);
       else if (SCM_FRACTIONP (y))
-       return scm_i_slow_exact_euclidean_divide (x, y);
+       return scm_i_exact_rational_floor_divide (x, y, qp, rp);
       else
-       SCM_WTA_DISPATCH_2 (g_scm_euclidean_divide, x, y, SCM_ARG2,
-                           s_scm_euclidean_divide);
+       return two_valued_wta_dispatch_2 (g_scm_floor_divide, x, y, SCM_ARG2,
+                                         s_scm_floor_divide, qp, rp);
     }
   else if (SCM_REALP (x))
     {
       if (SCM_REALP (y) || SCM_I_INUMP (y) ||
          SCM_BIGP (y) || SCM_FRACTIONP (y))
-       return scm_i_inexact_euclidean_divide
-         (SCM_REAL_VALUE (x), scm_to_double (y));
+       return scm_i_inexact_floor_divide
+         (SCM_REAL_VALUE (x), scm_to_double (y), qp, rp);
       else
-       SCM_WTA_DISPATCH_2 (g_scm_euclidean_divide, x, y, SCM_ARG2,
-                           s_scm_euclidean_divide);
+       return two_valued_wta_dispatch_2 (g_scm_floor_divide, x, y, SCM_ARG2,
+                                         s_scm_floor_divide, qp, rp);
     }
   else if (SCM_FRACTIONP (x))
     {
       if (SCM_REALP (y))
-       return scm_i_inexact_euclidean_divide
-         (scm_i_fraction2double (x), SCM_REAL_VALUE (y));
+       return scm_i_inexact_floor_divide
+         (scm_i_fraction2double (x), SCM_REAL_VALUE (y), qp, rp);
+      else if (SCM_I_INUMP (y) || SCM_BIGP (y) || SCM_FRACTIONP (y))
+       return scm_i_exact_rational_floor_divide (x, y, qp, rp);
       else
-       return scm_i_slow_exact_euclidean_divide (x, y);
+       return two_valued_wta_dispatch_2 (g_scm_floor_divide, x, y, SCM_ARG2,
+                                         s_scm_floor_divide, qp, rp);
     }
   else
-    SCM_WTA_DISPATCH_2 (g_scm_euclidean_divide, x, y, SCM_ARG1,
-                       s_scm_euclidean_divide);
+    return two_valued_wta_dispatch_2 (g_scm_floor_divide, x, y, SCM_ARG1,
+                                     s_scm_floor_divide, qp, rp);
 }
-#undef FUNC_NAME
 
-static SCM
-scm_i_inexact_euclidean_divide (double x, double y)
+static void
+scm_i_inexact_floor_divide (double x, double y, SCM *qp, SCM *rp)
 {
-  double q, r;
-
-  if (SCM_LIKELY (y > 0))
-    q = floor (x / y);
-  else if (SCM_LIKELY (y < 0))
-    q = ceil (x / y);
-  else if (y == 0)
-    scm_num_overflow (s_scm_euclidean_divide);  /* or return a NaN? */
+  if (SCM_UNLIKELY (y == 0))
+    scm_num_overflow (s_scm_floor_divide);  /* or return a NaN? */
   else
-    q = guile_NaN;
-  r = x - q * y;
-  return scm_values (scm_list_2 (scm_from_double (q),
-                                scm_from_double (r)));
+    {
+      double q = floor (x / y);
+      double r = x - q * y;
+      *qp = scm_from_double (q);
+      *rp = scm_from_double (r);
+    }
 }
 
-/* Compute exact euclidean quotient and remainder the slow way.
-   We use this only if both arguments are exact,
-   and at least one of them is a fraction */
-static SCM
-scm_i_slow_exact_euclidean_divide (SCM x, SCM y)
+static void
+scm_i_exact_rational_floor_divide (SCM x, SCM y, SCM *qp, SCM *rp)
 {
-  SCM q, r;
+  SCM r1;
+  SCM xd = scm_denominator (x);
+  SCM yd = scm_denominator (y);
 
-  if (!(SCM_I_INUMP (x) || SCM_BIGP (x) || SCM_FRACTIONP (x)))
-    SCM_WTA_DISPATCH_2 (g_scm_euclidean_divide, x, y, SCM_ARG1,
-                       s_scm_euclidean_divide);
-  else if (!(SCM_I_INUMP (y) || SCM_BIGP (y) || SCM_FRACTIONP (y)))
-    SCM_WTA_DISPATCH_2 (g_scm_euclidean_divide, x, y, SCM_ARG2,
-                       s_scm_euclidean_divide);
-  else if (scm_is_true (scm_positive_p (y)))
-    q = scm_floor (scm_divide (x, y));
-  else if (scm_is_true (scm_negative_p (y)))
-    q = scm_ceiling (scm_divide (x, y));
-  else
-    scm_num_overflow (s_scm_euclidean_divide);
-  r = scm_difference (x, scm_product (q, y));
-  return scm_values (scm_list_2 (q, r));
+  scm_floor_divide (scm_product (scm_numerator (x), yd),
+                   scm_product (scm_numerator (y), xd),
+                   qp, &r1);
+  *rp = scm_divide (r1, scm_product (xd, yd));
 }
 
-static SCM scm_i_inexact_centered_quotient (double x, double y);
-static SCM scm_i_bigint_centered_quotient (SCM x, SCM y);
-static SCM scm_i_slow_exact_centered_quotient (SCM x, SCM y);
+static SCM scm_i_inexact_ceiling_quotient (double x, double y);
+static SCM scm_i_exact_rational_ceiling_quotient (SCM x, SCM y);
 
-SCM_PRIMITIVE_GENERIC (scm_centered_quotient, "centered-quotient", 2, 0, 0,
+SCM_PRIMITIVE_GENERIC (scm_ceiling_quotient, "ceiling-quotient", 2, 0, 0,
                       (SCM x, SCM y),
-                      "Return the integer @var{q} such that\n"
-                      "@address@hidden = @address@hidden + @var{r}} where\n"
-                      "@math{-abs(@var{y}/2) <= @var{r} < abs(@var{y}/2)}.\n"
+                      "Return the ceiling of @address@hidden / @var{y}}.\n"
                       "@lisp\n"
-                      "(centered-quotient 123 10) @result{} 12\n"
-                      "(centered-quotient 123 -10) @result{} -12\n"
-                      "(centered-quotient -123 10) @result{} -12\n"
-                      "(centered-quotient -123 -10) @result{} 12\n"
-                      "(centered-quotient -123.2 -63.5) @result{} 2.0\n"
-                      "(centered-quotient 16/3 -10/7) @result{} -4\n"
+                      "(ceiling-quotient 123 10) @result{} 13\n"
+                      "(ceiling-quotient 123 -10) @result{} -12\n"
+                      "(ceiling-quotient -123 10) @result{} -12\n"
+                      "(ceiling-quotient -123 -10) @result{} 13\n"
+                      "(ceiling-quotient -123.2 -63.5) @result{} 2.0\n"
+                      "(ceiling-quotient 16/3 -10/7) @result{} -3\n"
                       "@end lisp")
-#define FUNC_NAME s_scm_centered_quotient
+#define FUNC_NAME s_scm_ceiling_quotient
 {
   if (SCM_LIKELY (SCM_I_INUMP (x)))
     {
@@ -1622,37 +1480,21 @@ SCM_PRIMITIVE_GENERIC (scm_centered_quotient, 
"centered-quotient", 2, 0, 0,
        {
          scm_t_inum yy = SCM_I_INUM (y);
          if (SCM_UNLIKELY (yy == 0))
-           scm_num_overflow (s_scm_centered_quotient);
+           scm_num_overflow (s_scm_ceiling_quotient);
          else
            {
-             scm_t_inum qq = xx / yy;
-             scm_t_inum rr = xx % yy;
-             if (SCM_LIKELY (xx > 0))
-               {
-                 if (SCM_LIKELY (yy > 0))
-                   {
-                     if (rr >= (yy + 1) / 2)
-                       qq++;
-                   }
-                 else
-                   {
-                     if (rr >= (1 - yy) / 2)
-                       qq--;
-                   }
-               }
-             else
+             scm_t_inum xx1 = xx;
+             scm_t_inum qq;
+             if (SCM_LIKELY (yy > 0))
                {
-                 if (SCM_LIKELY (yy > 0))
-                   {
-                     if (rr < -yy / 2)
-                       qq--;
-                   }
-                 else
-                   {
-                     if (rr < yy / 2)
-                       qq++;
-                   }
+                 if (SCM_LIKELY (xx >= 0))
+                   xx1 = xx + yy - 1;
                }
+             else if (SCM_UNLIKELY (yy == 0))
+               scm_num_overflow (s_scm_ceiling_quotient);
+             else if (xx < 0)
+               xx1 = xx + yy + 1;
+             qq = xx1 / yy;
              if (SCM_LIKELY (SCM_FIXABLE (qq)))
                return SCM_I_MAKINUM (qq);
              else
@@ -1661,17 +1503,35 @@ SCM_PRIMITIVE_GENERIC (scm_centered_quotient, 
"centered-quotient", 2, 0, 0,
        }
       else if (SCM_BIGP (y))
        {
-         /* Pass a denormalized bignum version of x (even though it
-            can fit in a fixnum) to scm_i_bigint_centered_quotient */
-         return scm_i_bigint_centered_quotient (scm_i_long2big (xx), y);
+         int sign = mpz_sgn (SCM_I_BIG_MPZ (y));
+         scm_remember_upto_here_1 (y);
+         if (SCM_LIKELY (sign > 0))
+           {
+             if (SCM_LIKELY (xx > 0))
+               return SCM_INUM1;
+             else if (SCM_UNLIKELY (xx == SCM_MOST_NEGATIVE_FIXNUM)
+                      && SCM_UNLIKELY (mpz_cmp_ui (SCM_I_BIG_MPZ (y),
+                                      - SCM_MOST_NEGATIVE_FIXNUM) == 0))
+               {
+                 /* Special case: x == fixnum-min && y == abs (fixnum-min) */
+                 scm_remember_upto_here_1 (y);
+                 return SCM_I_MAKINUM (-1);
+               }
+             else
+               return SCM_INUM0;
+           }
+         else if (xx >= 0)
+           return SCM_INUM0;
+         else
+           return SCM_INUM1;
        }
       else if (SCM_REALP (y))
-       return scm_i_inexact_centered_quotient (xx, SCM_REAL_VALUE (y));
+       return scm_i_inexact_ceiling_quotient (xx, SCM_REAL_VALUE (y));
       else if (SCM_FRACTIONP (y))
-       return scm_i_slow_exact_centered_quotient (x, y);
+       return scm_i_exact_rational_ceiling_quotient (x, y);
       else
-       SCM_WTA_DISPATCH_2 (g_scm_centered_quotient, x, y, SCM_ARG2,
-                           s_scm_centered_quotient);
+       SCM_WTA_DISPATCH_2 (g_scm_ceiling_quotient, x, y, SCM_ARG2,
+                           s_scm_ceiling_quotient);
     }
   else if (SCM_BIGP (x))
     {
@@ -1679,170 +1539,102 @@ SCM_PRIMITIVE_GENERIC (scm_centered_quotient, 
"centered-quotient", 2, 0, 0,
        {
          scm_t_inum yy = SCM_I_INUM (y);
          if (SCM_UNLIKELY (yy == 0))
-           scm_num_overflow (s_scm_centered_quotient);
+           scm_num_overflow (s_scm_ceiling_quotient);
          else if (SCM_UNLIKELY (yy == 1))
            return x;
          else
            {
              SCM q = scm_i_mkbig ();
-             scm_t_inum rr;
-             /* Arrange for rr to initially be non-positive,
-                because that simplifies the test to see
-                if it is within the needed bounds. */
              if (yy > 0)
-               {
-                 rr = - mpz_cdiv_q_ui (SCM_I_BIG_MPZ (q),
-                                       SCM_I_BIG_MPZ (x), yy);
-                 scm_remember_upto_here_1 (x);
-                 if (rr < -yy / 2)
-                   mpz_sub_ui (SCM_I_BIG_MPZ (q),
-                               SCM_I_BIG_MPZ (q), 1);
-               }
+               mpz_cdiv_q_ui (SCM_I_BIG_MPZ (q), SCM_I_BIG_MPZ (x), yy);
              else
                {
-                 rr = - mpz_cdiv_q_ui (SCM_I_BIG_MPZ (q),
-                                       SCM_I_BIG_MPZ (x), -yy);
-                 scm_remember_upto_here_1 (x);
+                 mpz_fdiv_q_ui (SCM_I_BIG_MPZ (q), SCM_I_BIG_MPZ (x), -yy);
                  mpz_neg (SCM_I_BIG_MPZ (q), SCM_I_BIG_MPZ (q));
-                 if (rr < yy / 2)
-                   mpz_add_ui (SCM_I_BIG_MPZ (q),
-                               SCM_I_BIG_MPZ (q), 1);
                }
+             scm_remember_upto_here_1 (x);
              return scm_i_normbig (q);
            }
        }
       else if (SCM_BIGP (y))
-       return scm_i_bigint_centered_quotient (x, y);
+       {
+         SCM q = scm_i_mkbig ();
+         mpz_cdiv_q (SCM_I_BIG_MPZ (q),
+                     SCM_I_BIG_MPZ (x),
+                     SCM_I_BIG_MPZ (y));
+         scm_remember_upto_here_2 (x, y);
+         return scm_i_normbig (q);
+       }
       else if (SCM_REALP (y))
-       return scm_i_inexact_centered_quotient
+       return scm_i_inexact_ceiling_quotient
          (scm_i_big2dbl (x), SCM_REAL_VALUE (y));
       else if (SCM_FRACTIONP (y))
-       return scm_i_slow_exact_centered_quotient (x, y);
+       return scm_i_exact_rational_ceiling_quotient (x, y);
       else
-       SCM_WTA_DISPATCH_2 (g_scm_centered_quotient, x, y, SCM_ARG2,
-                           s_scm_centered_quotient);
+       SCM_WTA_DISPATCH_2 (g_scm_ceiling_quotient, x, y, SCM_ARG2,
+                           s_scm_ceiling_quotient);
     }
   else if (SCM_REALP (x))
     {
       if (SCM_REALP (y) || SCM_I_INUMP (y) ||
          SCM_BIGP (y) || SCM_FRACTIONP (y))
-       return scm_i_inexact_centered_quotient
+       return scm_i_inexact_ceiling_quotient
          (SCM_REAL_VALUE (x), scm_to_double (y));
       else
-       SCM_WTA_DISPATCH_2 (g_scm_centered_quotient, x, y, SCM_ARG2,
-                           s_scm_centered_quotient);
+       SCM_WTA_DISPATCH_2 (g_scm_ceiling_quotient, x, y, SCM_ARG2,
+                           s_scm_ceiling_quotient);
     }
   else if (SCM_FRACTIONP (x))
     {
       if (SCM_REALP (y))
-       return scm_i_inexact_centered_quotient
+       return scm_i_inexact_ceiling_quotient
          (scm_i_fraction2double (x), SCM_REAL_VALUE (y));
+      else if (SCM_I_INUMP (y) || SCM_BIGP (y) || SCM_FRACTIONP (y))
+       return scm_i_exact_rational_ceiling_quotient (x, y);
       else
-       return scm_i_slow_exact_centered_quotient (x, y);
+       SCM_WTA_DISPATCH_2 (g_scm_ceiling_quotient, x, y, SCM_ARG2,
+                           s_scm_ceiling_quotient);
     }
   else
-    SCM_WTA_DISPATCH_2 (g_scm_centered_quotient, x, y, SCM_ARG1,
-                       s_scm_centered_quotient);
+    SCM_WTA_DISPATCH_2 (g_scm_ceiling_quotient, x, y, SCM_ARG1,
+                       s_scm_ceiling_quotient);
 }
 #undef FUNC_NAME
 
 static SCM
-scm_i_inexact_centered_quotient (double x, double y)
-{
-  if (SCM_LIKELY (y > 0))
-    return scm_from_double (floor (x/y + 0.5));
-  else if (SCM_LIKELY (y < 0))
-    return scm_from_double (ceil (x/y - 0.5));
-  else if (y == 0)
-    scm_num_overflow (s_scm_centered_quotient);  /* or return a NaN? */
-  else
-    return scm_nan ();
-}
-
-/* Assumes that both x and y are bigints, though
-   x might be able to fit into a fixnum. */
-static SCM
-scm_i_bigint_centered_quotient (SCM x, SCM y)
+scm_i_inexact_ceiling_quotient (double x, double y)
 {
-  SCM q, r, min_r;
-
-  /* Note that x might be small enough to fit into a
-     fixnum, so we must not let it escape into the wild */
-  q = scm_i_mkbig ();
-  r = scm_i_mkbig ();
-
-  /* min_r will eventually become -abs(y)/2 */
-  min_r = scm_i_mkbig ();
-  mpz_tdiv_q_2exp (SCM_I_BIG_MPZ (min_r),
-                  SCM_I_BIG_MPZ (y), 1);
-
-  /* Arrange for rr to initially be non-positive,
-     because that simplifies the test to see
-     if it is within the needed bounds. */
-  if (mpz_sgn (SCM_I_BIG_MPZ (y)) > 0)
-    {
-      mpz_cdiv_qr (SCM_I_BIG_MPZ (q), SCM_I_BIG_MPZ (r),
-                  SCM_I_BIG_MPZ (x), SCM_I_BIG_MPZ (y));
-      scm_remember_upto_here_2 (x, y);
-      mpz_neg (SCM_I_BIG_MPZ (min_r), SCM_I_BIG_MPZ (min_r));
-      if (mpz_cmp (SCM_I_BIG_MPZ (r), SCM_I_BIG_MPZ (min_r)) < 0)
-       mpz_sub_ui (SCM_I_BIG_MPZ (q),
-                   SCM_I_BIG_MPZ (q), 1);
-    }
+  if (SCM_UNLIKELY (y == 0))
+    scm_num_overflow (s_scm_ceiling_quotient);  /* or return a NaN? */
   else
-    {
-      mpz_fdiv_qr (SCM_I_BIG_MPZ (q), SCM_I_BIG_MPZ (r),
-                  SCM_I_BIG_MPZ (x), SCM_I_BIG_MPZ (y));
-      scm_remember_upto_here_2 (x, y);
-      if (mpz_cmp (SCM_I_BIG_MPZ (r), SCM_I_BIG_MPZ (min_r)) < 0)
-       mpz_add_ui (SCM_I_BIG_MPZ (q),
-                   SCM_I_BIG_MPZ (q), 1);
-    }
-  scm_remember_upto_here_2 (r, min_r);
-  return scm_i_normbig (q);
+    return scm_from_double (ceil (x / y));
 }
 
-/* Compute exact centered quotient the slow way.
-   We use this only if both arguments are exact,
-   and at least one of them is a fraction */
 static SCM
-scm_i_slow_exact_centered_quotient (SCM x, SCM y)
+scm_i_exact_rational_ceiling_quotient (SCM x, SCM y)
 {
-  if (!(SCM_I_INUMP (x) || SCM_BIGP (x) || SCM_FRACTIONP (x)))
-    SCM_WTA_DISPATCH_2 (g_scm_centered_quotient, x, y, SCM_ARG1,
-                       s_scm_centered_quotient);
-  else if (!(SCM_I_INUMP (y) || SCM_BIGP (y) || SCM_FRACTIONP (y)))
-    SCM_WTA_DISPATCH_2 (g_scm_centered_quotient, x, y, SCM_ARG2,
-                       s_scm_centered_quotient);
-  else if (scm_is_true (scm_positive_p (y)))
-    return scm_floor (scm_sum (scm_divide (x, y),
-                              exactly_one_half));
-  else if (scm_is_true (scm_negative_p (y)))
-    return scm_ceiling (scm_difference (scm_divide (x, y),
-                                       exactly_one_half));
-  else
-    scm_num_overflow (s_scm_centered_quotient);
+  return scm_ceiling_quotient
+    (scm_product (scm_numerator (x), scm_denominator (y)),
+     scm_product (scm_numerator (y), scm_denominator (x)));
 }
 
-static SCM scm_i_inexact_centered_remainder (double x, double y);
-static SCM scm_i_bigint_centered_remainder (SCM x, SCM y);
-static SCM scm_i_slow_exact_centered_remainder (SCM x, SCM y);
+static SCM scm_i_inexact_ceiling_remainder (double x, double y);
+static SCM scm_i_exact_rational_ceiling_remainder (SCM x, SCM y);
 
-SCM_PRIMITIVE_GENERIC (scm_centered_remainder, "centered-remainder", 2, 0, 0,
+SCM_PRIMITIVE_GENERIC (scm_ceiling_remainder, "ceiling-remainder", 2, 0, 0,
                       (SCM x, SCM y),
                       "Return the real number @var{r} such that\n"
-                      "@math{-abs(@var{y}/2) <= @var{r} < abs(@var{y}/2)}\n"
-                      "and @address@hidden = @address@hidden + @var{r}}\n"
-                      "for some integer @var{q}.\n"
+                      "@address@hidden = @address@hidden + @var{r}}\n"
+                      "where @address@hidden = ceiling(@var{x} / @var{y})}.\n"
                       "@lisp\n"
-                      "(centered-remainder 123 10) @result{} 3\n"
-                      "(centered-remainder 123 -10) @result{} 3\n"
-                      "(centered-remainder -123 10) @result{} -3\n"
-                      "(centered-remainder -123 -10) @result{} -3\n"
-                      "(centered-remainder -123.2 -63.5) @result{} 3.8\n"
-                      "(centered-remainder 16/3 -10/7) @result{} -8/21\n"
+                      "(ceiling-remainder 123 10) @result{} -7\n"
+                      "(ceiling-remainder 123 -10) @result{} 3\n"
+                      "(ceiling-remainder -123 10) @result{} -3\n"
+                      "(ceiling-remainder -123 -10) @result{} 7\n"
+                      "(ceiling-remainder -123.2 -63.5) @result{} 3.8\n"
+                      "(ceiling-remainder 16/3 -10/7) @result{} 22/21\n"
                       "@end lisp")
-#define FUNC_NAME s_scm_centered_remainder
+#define FUNC_NAME s_scm_ceiling_remainder
 {
   if (SCM_LIKELY (SCM_I_INUMP (x)))
     {
@@ -1851,35 +1643,1794 @@ SCM_PRIMITIVE_GENERIC (scm_centered_remainder, 
"centered-remainder", 2, 0, 0,
        {
          scm_t_inum yy = SCM_I_INUM (y);
          if (SCM_UNLIKELY (yy == 0))
-           scm_num_overflow (s_scm_centered_remainder);
+           scm_num_overflow (s_scm_ceiling_remainder);
          else
            {
              scm_t_inum rr = xx % yy;
+             int needs_adjustment;
+
+             if (SCM_LIKELY (yy > 0))
+               needs_adjustment = (rr > 0);
+             else
+               needs_adjustment = (rr < 0);
+
+             if (needs_adjustment)
+               rr -= yy;
+             return SCM_I_MAKINUM (rr);
+           }
+       }
+      else if (SCM_BIGP (y))
+       {
+         int sign = mpz_sgn (SCM_I_BIG_MPZ (y));
+         scm_remember_upto_here_1 (y);
+         if (SCM_LIKELY (sign > 0))
+           {
              if (SCM_LIKELY (xx > 0))
                {
-                 if (SCM_LIKELY (yy > 0))
-                   {
-                     if (rr >= (yy + 1) / 2)
-                       rr -= yy;
-                   }
-                 else
-                   {
-                     if (rr >= (1 - yy) / 2)
-                       rr += yy;
-                   }
+                 SCM r = scm_i_mkbig ();
+                 mpz_sub_ui (SCM_I_BIG_MPZ (r), SCM_I_BIG_MPZ (y), xx);
+                 scm_remember_upto_here_1 (y);
+                 mpz_neg (SCM_I_BIG_MPZ (r), SCM_I_BIG_MPZ (r));
+                 return scm_i_normbig (r);
                }
-             else
+             else if (SCM_UNLIKELY (xx == SCM_MOST_NEGATIVE_FIXNUM)
+                      && SCM_UNLIKELY (mpz_cmp_ui (SCM_I_BIG_MPZ (y),
+                                      - SCM_MOST_NEGATIVE_FIXNUM) == 0))
                {
-                 if (SCM_LIKELY (yy > 0))
-                   {
-                     if (rr < -yy / 2)
-                       rr += yy;
-                   }
-                 else
-                   {
-                     if (rr < yy / 2)
-                       rr -= yy;
-                   }
+                 /* Special case: x == fixnum-min && y == abs (fixnum-min) */
+                 scm_remember_upto_here_1 (y);
+                 return SCM_INUM0;
+               }
+             else
+               return x;
+           }
+         else if (xx >= 0)
+           return x;
+         else
+           {
+             SCM r = scm_i_mkbig ();
+             mpz_add_ui (SCM_I_BIG_MPZ (r), SCM_I_BIG_MPZ (y), -xx);
+             scm_remember_upto_here_1 (y);
+             mpz_neg (SCM_I_BIG_MPZ (r), SCM_I_BIG_MPZ (r));
+             return scm_i_normbig (r);
+           }
+       }
+      else if (SCM_REALP (y))
+       return scm_i_inexact_ceiling_remainder (xx, SCM_REAL_VALUE (y));
+      else if (SCM_FRACTIONP (y))
+       return scm_i_exact_rational_ceiling_remainder (x, y);
+      else
+       SCM_WTA_DISPATCH_2 (g_scm_ceiling_remainder, x, y, SCM_ARG2,
+                           s_scm_ceiling_remainder);
+    }
+  else if (SCM_BIGP (x))
+    {
+      if (SCM_LIKELY (SCM_I_INUMP (y)))
+       {
+         scm_t_inum yy = SCM_I_INUM (y);
+         if (SCM_UNLIKELY (yy == 0))
+           scm_num_overflow (s_scm_ceiling_remainder);
+         else
+           {
+             scm_t_inum rr;
+             if (yy > 0)
+               rr = -mpz_cdiv_ui (SCM_I_BIG_MPZ (x), yy);
+             else
+               rr = mpz_fdiv_ui (SCM_I_BIG_MPZ (x), -yy);
+             scm_remember_upto_here_1 (x);
+             return SCM_I_MAKINUM (rr);
+           }
+       }
+      else if (SCM_BIGP (y))
+       {
+         SCM r = scm_i_mkbig ();
+         mpz_cdiv_r (SCM_I_BIG_MPZ (r),
+                     SCM_I_BIG_MPZ (x),
+                     SCM_I_BIG_MPZ (y));
+         scm_remember_upto_here_2 (x, y);
+         return scm_i_normbig (r);
+       }
+      else if (SCM_REALP (y))
+       return scm_i_inexact_ceiling_remainder
+         (scm_i_big2dbl (x), SCM_REAL_VALUE (y));
+      else if (SCM_FRACTIONP (y))
+       return scm_i_exact_rational_ceiling_remainder (x, y);
+      else
+       SCM_WTA_DISPATCH_2 (g_scm_ceiling_remainder, x, y, SCM_ARG2,
+                           s_scm_ceiling_remainder);
+    }
+  else if (SCM_REALP (x))
+    {
+      if (SCM_REALP (y) || SCM_I_INUMP (y) ||
+         SCM_BIGP (y) || SCM_FRACTIONP (y))
+       return scm_i_inexact_ceiling_remainder
+         (SCM_REAL_VALUE (x), scm_to_double (y));
+      else
+       SCM_WTA_DISPATCH_2 (g_scm_ceiling_remainder, x, y, SCM_ARG2,
+                           s_scm_ceiling_remainder);
+    }
+  else if (SCM_FRACTIONP (x))
+    {
+      if (SCM_REALP (y))
+       return scm_i_inexact_ceiling_remainder
+         (scm_i_fraction2double (x), SCM_REAL_VALUE (y));
+      else if (SCM_I_INUMP (y) || SCM_BIGP (y) || SCM_FRACTIONP (y))
+       return scm_i_exact_rational_ceiling_remainder (x, y);
+      else
+       SCM_WTA_DISPATCH_2 (g_scm_ceiling_remainder, x, y, SCM_ARG2,
+                           s_scm_ceiling_remainder);
+    }
+  else
+    SCM_WTA_DISPATCH_2 (g_scm_ceiling_remainder, x, y, SCM_ARG1,
+                       s_scm_ceiling_remainder);
+}
+#undef FUNC_NAME
+
+static SCM
+scm_i_inexact_ceiling_remainder (double x, double y)
+{
+  /* Although it would be more efficient to use fmod here, we can't
+     because it would in some cases produce results inconsistent with
+     scm_i_inexact_ceiling_quotient, such that x != q * y + r (not even
+     close).  In particular, when x is very close to a multiple of y,
+     then r might be either 0.0 or -y, but those two cases must
+     correspond to different choices of q.  If r = 0.0 then q must be
+     x/y, and if r = -y then q must be x/y+1.  If quotient chooses one
+     and remainder chooses the other, it would be bad.  */
+  if (SCM_UNLIKELY (y == 0))
+    scm_num_overflow (s_scm_ceiling_remainder);  /* or return a NaN? */
+  else
+    return scm_from_double (x - y * ceil (x / y));
+}
+
+static SCM
+scm_i_exact_rational_ceiling_remainder (SCM x, SCM y)
+{
+  SCM xd = scm_denominator (x);
+  SCM yd = scm_denominator (y);
+  SCM r1 = scm_ceiling_remainder (scm_product (scm_numerator (x), yd),
+                                 scm_product (scm_numerator (y), xd));
+  return scm_divide (r1, scm_product (xd, yd));
+}
+
+static void scm_i_inexact_ceiling_divide (double x, double y,
+                                         SCM *qp, SCM *rp);
+static void scm_i_exact_rational_ceiling_divide (SCM x, SCM y,
+                                                SCM *qp, SCM *rp);
+
+SCM_PRIMITIVE_GENERIC (scm_i_ceiling_divide, "ceiling/", 2, 0, 0,
+                      (SCM x, SCM y),
+                      "Return the integer @var{q} and the real number 
@var{r}\n"
+                      "such that @address@hidden = @address@hidden + 
@var{r}}\n"
+                      "and @address@hidden = ceiling(@var{x} / @var{y})}.\n"
+                      "@lisp\n"
+                      "(ceiling/ 123 10) @result{} 13 and -7\n"
+                      "(ceiling/ 123 -10) @result{} -12 and 3\n"
+                      "(ceiling/ -123 10) @result{} -12 and -3\n"
+                      "(ceiling/ -123 -10) @result{} 13 and 7\n"
+                      "(ceiling/ -123.2 -63.5) @result{} 2.0 and 3.8\n"
+                      "(ceiling/ 16/3 -10/7) @result{} -3 and 22/21\n"
+                      "@end lisp")
+#define FUNC_NAME s_scm_i_ceiling_divide
+{
+  SCM q, r;
+
+  scm_ceiling_divide(x, y, &q, &r);
+  return scm_values (scm_list_2 (q, r));
+}
+#undef FUNC_NAME
+
+#define s_scm_ceiling_divide s_scm_i_ceiling_divide
+#define g_scm_ceiling_divide g_scm_i_ceiling_divide
+
+void
+scm_ceiling_divide (SCM x, SCM y, SCM *qp, SCM *rp)
+{
+  if (SCM_LIKELY (SCM_I_INUMP (x)))
+    {
+      scm_t_inum xx = SCM_I_INUM (x);
+      if (SCM_LIKELY (SCM_I_INUMP (y)))
+       {
+         scm_t_inum yy = SCM_I_INUM (y);
+         if (SCM_UNLIKELY (yy == 0))
+           scm_num_overflow (s_scm_ceiling_divide);
+         else
+           {
+             scm_t_inum qq = xx / yy;
+             scm_t_inum rr = xx % yy;
+             int needs_adjustment;
+
+             if (SCM_LIKELY (yy > 0))
+               needs_adjustment = (rr > 0);
+             else
+               needs_adjustment = (rr < 0);
+
+             if (needs_adjustment)
+               {
+                 rr -= yy;
+                 qq++;
+               }
+             if (SCM_LIKELY (SCM_FIXABLE (qq)))
+               *qp = SCM_I_MAKINUM (qq);
+             else
+               *qp = scm_i_inum2big (qq);
+             *rp = SCM_I_MAKINUM (rr);
+           }
+         return;
+       }
+      else if (SCM_BIGP (y))
+       {
+         int sign = mpz_sgn (SCM_I_BIG_MPZ (y));
+         scm_remember_upto_here_1 (y);
+         if (SCM_LIKELY (sign > 0))
+           {
+             if (SCM_LIKELY (xx > 0))
+               {
+                 SCM r = scm_i_mkbig ();
+                 mpz_sub_ui (SCM_I_BIG_MPZ (r), SCM_I_BIG_MPZ (y), xx);
+                 scm_remember_upto_here_1 (y);
+                 mpz_neg (SCM_I_BIG_MPZ (r), SCM_I_BIG_MPZ (r));
+                 *qp = SCM_INUM1;
+                 *rp = scm_i_normbig (r);
+               }
+             else if (SCM_UNLIKELY (xx == SCM_MOST_NEGATIVE_FIXNUM)
+                      && SCM_UNLIKELY (mpz_cmp_ui (SCM_I_BIG_MPZ (y),
+                                      - SCM_MOST_NEGATIVE_FIXNUM) == 0))
+               {
+                 /* Special case: x == fixnum-min && y == abs (fixnum-min) */
+                 scm_remember_upto_here_1 (y);
+                 *qp = SCM_I_MAKINUM (-1);
+                 *rp = SCM_INUM0;
+               }
+             else
+               {
+                 *qp = SCM_INUM0;
+                 *rp = x;
+               }
+           }
+         else if (xx >= 0)
+           {
+             *qp = SCM_INUM0;
+             *rp = x;
+           }
+         else
+           {
+             SCM r = scm_i_mkbig ();
+             mpz_add_ui (SCM_I_BIG_MPZ (r), SCM_I_BIG_MPZ (y), -xx);
+             scm_remember_upto_here_1 (y);
+             mpz_neg (SCM_I_BIG_MPZ (r), SCM_I_BIG_MPZ (r));
+             *qp = SCM_INUM1;
+             *rp = scm_i_normbig (r);
+           }
+         return;
+       }
+      else if (SCM_REALP (y))
+       return scm_i_inexact_ceiling_divide (xx, SCM_REAL_VALUE (y), qp, rp);
+      else if (SCM_FRACTIONP (y))
+       return scm_i_exact_rational_ceiling_divide (x, y, qp, rp);
+      else
+       return two_valued_wta_dispatch_2 (g_scm_ceiling_divide, x, y, SCM_ARG2,
+                                         s_scm_ceiling_divide, qp, rp);
+    }
+  else if (SCM_BIGP (x))
+    {
+      if (SCM_LIKELY (SCM_I_INUMP (y)))
+       {
+         scm_t_inum yy = SCM_I_INUM (y);
+         if (SCM_UNLIKELY (yy == 0))
+           scm_num_overflow (s_scm_ceiling_divide);
+         else
+           {
+             SCM q = scm_i_mkbig ();
+             SCM r = scm_i_mkbig ();
+             if (yy > 0)
+               mpz_cdiv_qr_ui (SCM_I_BIG_MPZ (q), SCM_I_BIG_MPZ (r),
+                               SCM_I_BIG_MPZ (x), yy);
+             else
+               {
+                 mpz_fdiv_qr_ui (SCM_I_BIG_MPZ (q), SCM_I_BIG_MPZ (r),
+                                 SCM_I_BIG_MPZ (x), -yy);
+                 mpz_neg (SCM_I_BIG_MPZ (q), SCM_I_BIG_MPZ (q));
+               }
+             scm_remember_upto_here_1 (x);
+             *qp = scm_i_normbig (q);
+             *rp = scm_i_normbig (r);
+           }
+         return;
+       }
+      else if (SCM_BIGP (y))
+       {
+         SCM q = scm_i_mkbig ();
+         SCM r = scm_i_mkbig ();
+         mpz_cdiv_qr (SCM_I_BIG_MPZ (q), SCM_I_BIG_MPZ (r),
+                      SCM_I_BIG_MPZ (x), SCM_I_BIG_MPZ (y));
+         scm_remember_upto_here_2 (x, y);
+         *qp = scm_i_normbig (q);
+         *rp = scm_i_normbig (r);
+         return;
+       }
+      else if (SCM_REALP (y))
+       return scm_i_inexact_ceiling_divide
+         (scm_i_big2dbl (x), SCM_REAL_VALUE (y), qp, rp);
+      else if (SCM_FRACTIONP (y))
+       return scm_i_exact_rational_ceiling_divide (x, y, qp, rp);
+      else
+       return two_valued_wta_dispatch_2 (g_scm_ceiling_divide, x, y, SCM_ARG2,
+                                         s_scm_ceiling_divide, qp, rp);
+    }
+  else if (SCM_REALP (x))
+    {
+      if (SCM_REALP (y) || SCM_I_INUMP (y) ||
+         SCM_BIGP (y) || SCM_FRACTIONP (y))
+       return scm_i_inexact_ceiling_divide
+         (SCM_REAL_VALUE (x), scm_to_double (y), qp, rp);
+      else
+       return two_valued_wta_dispatch_2 (g_scm_ceiling_divide, x, y, SCM_ARG2,
+                                         s_scm_ceiling_divide, qp, rp);
+    }
+  else if (SCM_FRACTIONP (x))
+    {
+      if (SCM_REALP (y))
+       return scm_i_inexact_ceiling_divide
+         (scm_i_fraction2double (x), SCM_REAL_VALUE (y), qp, rp);
+      else if (SCM_I_INUMP (y) || SCM_BIGP (y) || SCM_FRACTIONP (y))
+       return scm_i_exact_rational_ceiling_divide (x, y, qp, rp);
+      else
+       return two_valued_wta_dispatch_2 (g_scm_ceiling_divide, x, y, SCM_ARG2,
+                                         s_scm_ceiling_divide, qp, rp);
+    }
+  else
+    return two_valued_wta_dispatch_2 (g_scm_ceiling_divide, x, y, SCM_ARG1,
+                                     s_scm_ceiling_divide, qp, rp);
+}
+
+static void
+scm_i_inexact_ceiling_divide (double x, double y, SCM *qp, SCM *rp)
+{
+  if (SCM_UNLIKELY (y == 0))
+    scm_num_overflow (s_scm_ceiling_divide);  /* or return a NaN? */
+  else
+    {
+      double q = ceil (x / y);
+      double r = x - q * y;
+      *qp = scm_from_double (q);
+      *rp = scm_from_double (r);
+    }
+}
+
+static void
+scm_i_exact_rational_ceiling_divide (SCM x, SCM y, SCM *qp, SCM *rp)
+{
+  SCM r1;
+  SCM xd = scm_denominator (x);
+  SCM yd = scm_denominator (y);
+
+  scm_ceiling_divide (scm_product (scm_numerator (x), yd),
+                     scm_product (scm_numerator (y), xd),
+                     qp, &r1);
+  *rp = scm_divide (r1, scm_product (xd, yd));
+}
+
+static SCM scm_i_inexact_truncate_quotient (double x, double y);
+static SCM scm_i_exact_rational_truncate_quotient (SCM x, SCM y);
+
+SCM_PRIMITIVE_GENERIC (scm_truncate_quotient, "truncate-quotient", 2, 0, 0,
+                      (SCM x, SCM y),
+                      "Return @address@hidden / @var{y}} rounded toward 
zero.\n"
+                      "@lisp\n"
+                      "(truncate-quotient 123 10) @result{} 12\n"
+                      "(truncate-quotient 123 -10) @result{} -12\n"
+                      "(truncate-quotient -123 10) @result{} -12\n"
+                      "(truncate-quotient -123 -10) @result{} 12\n"
+                      "(truncate-quotient -123.2 -63.5) @result{} 1.0\n"
+                      "(truncate-quotient 16/3 -10/7) @result{} -3\n"
+                      "@end lisp")
+#define FUNC_NAME s_scm_truncate_quotient
+{
+  if (SCM_LIKELY (SCM_I_INUMP (x)))
+    {
+      scm_t_inum xx = SCM_I_INUM (x);
+      if (SCM_LIKELY (SCM_I_INUMP (y)))
+       {
+         scm_t_inum yy = SCM_I_INUM (y);
+         if (SCM_UNLIKELY (yy == 0))
+           scm_num_overflow (s_scm_truncate_quotient);
+         else
+           {
+             scm_t_inum qq = xx / yy;
+             if (SCM_LIKELY (SCM_FIXABLE (qq)))
+               return SCM_I_MAKINUM (qq);
+             else
+               return scm_i_inum2big (qq);
+           }
+       }
+      else if (SCM_BIGP (y))
+       {
+         if (SCM_UNLIKELY (xx == SCM_MOST_NEGATIVE_FIXNUM)
+             && SCM_UNLIKELY (mpz_cmp_ui (SCM_I_BIG_MPZ (y),
+                                          - SCM_MOST_NEGATIVE_FIXNUM) == 0))
+           {
+             /* Special case: x == fixnum-min && y == abs (fixnum-min) */
+             scm_remember_upto_here_1 (y);
+             return SCM_I_MAKINUM (-1);
+           }
+         else
+           return SCM_INUM0;
+       }
+      else if (SCM_REALP (y))
+       return scm_i_inexact_truncate_quotient (xx, SCM_REAL_VALUE (y));
+      else if (SCM_FRACTIONP (y))
+       return scm_i_exact_rational_truncate_quotient (x, y);
+      else
+       SCM_WTA_DISPATCH_2 (g_scm_truncate_quotient, x, y, SCM_ARG2,
+                           s_scm_truncate_quotient);
+    }
+  else if (SCM_BIGP (x))
+    {
+      if (SCM_LIKELY (SCM_I_INUMP (y)))
+       {
+         scm_t_inum yy = SCM_I_INUM (y);
+         if (SCM_UNLIKELY (yy == 0))
+           scm_num_overflow (s_scm_truncate_quotient);
+         else if (SCM_UNLIKELY (yy == 1))
+           return x;
+         else
+           {
+             SCM q = scm_i_mkbig ();
+             if (yy > 0)
+               mpz_tdiv_q_ui (SCM_I_BIG_MPZ (q), SCM_I_BIG_MPZ (x), yy);
+             else
+               {
+                 mpz_tdiv_q_ui (SCM_I_BIG_MPZ (q), SCM_I_BIG_MPZ (x), -yy);
+                 mpz_neg (SCM_I_BIG_MPZ (q), SCM_I_BIG_MPZ (q));
+               }
+             scm_remember_upto_here_1 (x);
+             return scm_i_normbig (q);
+           }
+       }
+      else if (SCM_BIGP (y))
+       {
+         SCM q = scm_i_mkbig ();
+         mpz_tdiv_q (SCM_I_BIG_MPZ (q),
+                     SCM_I_BIG_MPZ (x),
+                     SCM_I_BIG_MPZ (y));
+         scm_remember_upto_here_2 (x, y);
+         return scm_i_normbig (q);
+       }
+      else if (SCM_REALP (y))
+       return scm_i_inexact_truncate_quotient
+         (scm_i_big2dbl (x), SCM_REAL_VALUE (y));
+      else if (SCM_FRACTIONP (y))
+       return scm_i_exact_rational_truncate_quotient (x, y);
+      else
+       SCM_WTA_DISPATCH_2 (g_scm_truncate_quotient, x, y, SCM_ARG2,
+                           s_scm_truncate_quotient);
+    }
+  else if (SCM_REALP (x))
+    {
+      if (SCM_REALP (y) || SCM_I_INUMP (y) ||
+         SCM_BIGP (y) || SCM_FRACTIONP (y))
+       return scm_i_inexact_truncate_quotient
+         (SCM_REAL_VALUE (x), scm_to_double (y));
+      else
+       SCM_WTA_DISPATCH_2 (g_scm_truncate_quotient, x, y, SCM_ARG2,
+                           s_scm_truncate_quotient);
+    }
+  else if (SCM_FRACTIONP (x))
+    {
+      if (SCM_REALP (y))
+       return scm_i_inexact_truncate_quotient
+         (scm_i_fraction2double (x), SCM_REAL_VALUE (y));
+      else if (SCM_I_INUMP (y) || SCM_BIGP (y) || SCM_FRACTIONP (y))
+       return scm_i_exact_rational_truncate_quotient (x, y);
+      else
+       SCM_WTA_DISPATCH_2 (g_scm_truncate_quotient, x, y, SCM_ARG2,
+                           s_scm_truncate_quotient);
+    }
+  else
+    SCM_WTA_DISPATCH_2 (g_scm_truncate_quotient, x, y, SCM_ARG1,
+                       s_scm_truncate_quotient);
+}
+#undef FUNC_NAME
+
+static SCM
+scm_i_inexact_truncate_quotient (double x, double y)
+{
+  if (SCM_UNLIKELY (y == 0))
+    scm_num_overflow (s_scm_truncate_quotient);  /* or return a NaN? */
+  else
+    return scm_from_double (scm_c_truncate (x / y));
+}
+
+static SCM
+scm_i_exact_rational_truncate_quotient (SCM x, SCM y)
+{
+  return scm_truncate_quotient
+    (scm_product (scm_numerator (x), scm_denominator (y)),
+     scm_product (scm_numerator (y), scm_denominator (x)));
+}
+
+static SCM scm_i_inexact_truncate_remainder (double x, double y);
+static SCM scm_i_exact_rational_truncate_remainder (SCM x, SCM y);
+
+SCM_PRIMITIVE_GENERIC (scm_truncate_remainder, "truncate-remainder", 2, 0, 0,
+                      (SCM x, SCM y),
+                      "Return the real number @var{r} such that\n"
+                      "@address@hidden = @address@hidden + @var{r}}\n"
+                      "where @address@hidden = truncate(@var{x} / @var{y})}.\n"
+                      "@lisp\n"
+                      "(truncate-remainder 123 10) @result{} 3\n"
+                      "(truncate-remainder 123 -10) @result{} 3\n"
+                      "(truncate-remainder -123 10) @result{} -3\n"
+                      "(truncate-remainder -123 -10) @result{} -3\n"
+                      "(truncate-remainder -123.2 -63.5) @result{} -59.7\n"
+                      "(truncate-remainder 16/3 -10/7) @result{} 22/21\n"
+                      "@end lisp")
+#define FUNC_NAME s_scm_truncate_remainder
+{
+  if (SCM_LIKELY (SCM_I_INUMP (x)))
+    {
+      scm_t_inum xx = SCM_I_INUM (x);
+      if (SCM_LIKELY (SCM_I_INUMP (y)))
+       {
+         scm_t_inum yy = SCM_I_INUM (y);
+         if (SCM_UNLIKELY (yy == 0))
+           scm_num_overflow (s_scm_truncate_remainder);
+         else
+           return SCM_I_MAKINUM (xx % yy);
+       }
+      else if (SCM_BIGP (y))
+       {
+         if (SCM_UNLIKELY (xx == SCM_MOST_NEGATIVE_FIXNUM)
+             && SCM_UNLIKELY (mpz_cmp_ui (SCM_I_BIG_MPZ (y),
+                                          - SCM_MOST_NEGATIVE_FIXNUM) == 0))
+           {
+             /* Special case: x == fixnum-min && y == abs (fixnum-min) */
+             scm_remember_upto_here_1 (y);
+             return SCM_INUM0;
+           }
+         else
+           return x;
+       }
+      else if (SCM_REALP (y))
+       return scm_i_inexact_truncate_remainder (xx, SCM_REAL_VALUE (y));
+      else if (SCM_FRACTIONP (y))
+       return scm_i_exact_rational_truncate_remainder (x, y);
+      else
+       SCM_WTA_DISPATCH_2 (g_scm_truncate_remainder, x, y, SCM_ARG2,
+                           s_scm_truncate_remainder);
+    }
+  else if (SCM_BIGP (x))
+    {
+      if (SCM_LIKELY (SCM_I_INUMP (y)))
+       {
+         scm_t_inum yy = SCM_I_INUM (y);
+         if (SCM_UNLIKELY (yy == 0))
+           scm_num_overflow (s_scm_truncate_remainder);
+         else
+           {
+             scm_t_inum rr = (mpz_tdiv_ui (SCM_I_BIG_MPZ (x),
+                                           (yy > 0) ? yy : -yy)
+                              * mpz_sgn (SCM_I_BIG_MPZ (x)));
+             scm_remember_upto_here_1 (x);
+             return SCM_I_MAKINUM (rr);
+           }
+       }
+      else if (SCM_BIGP (y))
+       {
+         SCM r = scm_i_mkbig ();
+         mpz_tdiv_r (SCM_I_BIG_MPZ (r),
+                     SCM_I_BIG_MPZ (x),
+                     SCM_I_BIG_MPZ (y));
+         scm_remember_upto_here_2 (x, y);
+         return scm_i_normbig (r);
+       }
+      else if (SCM_REALP (y))
+       return scm_i_inexact_truncate_remainder
+         (scm_i_big2dbl (x), SCM_REAL_VALUE (y));
+      else if (SCM_FRACTIONP (y))
+       return scm_i_exact_rational_truncate_remainder (x, y);
+      else
+       SCM_WTA_DISPATCH_2 (g_scm_truncate_remainder, x, y, SCM_ARG2,
+                           s_scm_truncate_remainder);
+    }
+  else if (SCM_REALP (x))
+    {
+      if (SCM_REALP (y) || SCM_I_INUMP (y) ||
+         SCM_BIGP (y) || SCM_FRACTIONP (y))
+       return scm_i_inexact_truncate_remainder
+         (SCM_REAL_VALUE (x), scm_to_double (y));
+      else
+       SCM_WTA_DISPATCH_2 (g_scm_truncate_remainder, x, y, SCM_ARG2,
+                           s_scm_truncate_remainder);
+    }
+  else if (SCM_FRACTIONP (x))
+    {
+      if (SCM_REALP (y))
+       return scm_i_inexact_truncate_remainder
+         (scm_i_fraction2double (x), SCM_REAL_VALUE (y));
+      else if (SCM_I_INUMP (y) || SCM_BIGP (y) || SCM_FRACTIONP (y))
+       return scm_i_exact_rational_truncate_remainder (x, y);
+      else
+       SCM_WTA_DISPATCH_2 (g_scm_truncate_remainder, x, y, SCM_ARG2,
+                           s_scm_truncate_remainder);
+    }
+  else
+    SCM_WTA_DISPATCH_2 (g_scm_truncate_remainder, x, y, SCM_ARG1,
+                       s_scm_truncate_remainder);
+}
+#undef FUNC_NAME
+
+static SCM
+scm_i_inexact_truncate_remainder (double x, double y)
+{
+  /* Although it would be more efficient to use fmod here, we can't
+     because it would in some cases produce results inconsistent with
+     scm_i_inexact_truncate_quotient, such that x != q * y + r (not even
+     close).  In particular, when x is very close to a multiple of y,
+     then r might be either 0.0 or sgn(x)*|y|, but those two cases must
+     correspond to different choices of q.  If quotient chooses one and
+     remainder chooses the other, it would be bad.  */
+  if (SCM_UNLIKELY (y == 0))
+    scm_num_overflow (s_scm_truncate_remainder);  /* or return a NaN? */
+  else
+    return scm_from_double (x - y * scm_c_truncate (x / y));
+}
+
+static SCM
+scm_i_exact_rational_truncate_remainder (SCM x, SCM y)
+{
+  SCM xd = scm_denominator (x);
+  SCM yd = scm_denominator (y);
+  SCM r1 = scm_truncate_remainder (scm_product (scm_numerator (x), yd),
+                                  scm_product (scm_numerator (y), xd));
+  return scm_divide (r1, scm_product (xd, yd));
+}
+
+
+static void scm_i_inexact_truncate_divide (double x, double y,
+                                          SCM *qp, SCM *rp);
+static void scm_i_exact_rational_truncate_divide (SCM x, SCM y,
+                                                 SCM *qp, SCM *rp);
+
+SCM_PRIMITIVE_GENERIC (scm_i_truncate_divide, "truncate/", 2, 0, 0,
+                      (SCM x, SCM y),
+                      "Return the integer @var{q} and the real number 
@var{r}\n"
+                      "such that @address@hidden = @address@hidden + 
@var{r}}\n"
+                      "and @address@hidden = truncate(@var{x} / @var{y})}.\n"
+                      "@lisp\n"
+                      "(truncate/ 123 10) @result{} 12 and 3\n"
+                      "(truncate/ 123 -10) @result{} -12 and 3\n"
+                      "(truncate/ -123 10) @result{} -12 and -3\n"
+                      "(truncate/ -123 -10) @result{} 12 and -3\n"
+                      "(truncate/ -123.2 -63.5) @result{} 1.0 and -59.7\n"
+                      "(truncate/ 16/3 -10/7) @result{} -3 and 22/21\n"
+                      "@end lisp")
+#define FUNC_NAME s_scm_i_truncate_divide
+{
+  SCM q, r;
+
+  scm_truncate_divide(x, y, &q, &r);
+  return scm_values (scm_list_2 (q, r));
+}
+#undef FUNC_NAME
+
+#define s_scm_truncate_divide s_scm_i_truncate_divide
+#define g_scm_truncate_divide g_scm_i_truncate_divide
+
+void
+scm_truncate_divide (SCM x, SCM y, SCM *qp, SCM *rp)
+{
+  if (SCM_LIKELY (SCM_I_INUMP (x)))
+    {
+      scm_t_inum xx = SCM_I_INUM (x);
+      if (SCM_LIKELY (SCM_I_INUMP (y)))
+       {
+         scm_t_inum yy = SCM_I_INUM (y);
+         if (SCM_UNLIKELY (yy == 0))
+           scm_num_overflow (s_scm_truncate_divide);
+         else
+           {
+             scm_t_inum qq = xx / yy;
+             scm_t_inum rr = xx % yy;
+             if (SCM_LIKELY (SCM_FIXABLE (qq)))
+               *qp = SCM_I_MAKINUM (qq);
+             else
+               *qp = scm_i_inum2big (qq);
+             *rp = SCM_I_MAKINUM (rr);
+           }
+         return;
+       }
+      else if (SCM_BIGP (y))
+       {
+         if (SCM_UNLIKELY (xx == SCM_MOST_NEGATIVE_FIXNUM)
+             && SCM_UNLIKELY (mpz_cmp_ui (SCM_I_BIG_MPZ (y),
+                                          - SCM_MOST_NEGATIVE_FIXNUM) == 0))
+           {
+             /* Special case: x == fixnum-min && y == abs (fixnum-min) */
+             scm_remember_upto_here_1 (y);
+             *qp = SCM_I_MAKINUM (-1);
+             *rp = SCM_INUM0;
+           }
+         else
+           {
+             *qp = SCM_INUM0;
+             *rp = x;
+           }
+         return;
+       }
+      else if (SCM_REALP (y))
+       return scm_i_inexact_truncate_divide (xx, SCM_REAL_VALUE (y), qp, rp);
+      else if (SCM_FRACTIONP (y))
+       return scm_i_exact_rational_truncate_divide (x, y, qp, rp);
+      else
+       return two_valued_wta_dispatch_2
+         (g_scm_truncate_divide, x, y, SCM_ARG2,
+          s_scm_truncate_divide, qp, rp);
+    }
+  else if (SCM_BIGP (x))
+    {
+      if (SCM_LIKELY (SCM_I_INUMP (y)))
+       {
+         scm_t_inum yy = SCM_I_INUM (y);
+         if (SCM_UNLIKELY (yy == 0))
+           scm_num_overflow (s_scm_truncate_divide);
+         else
+           {
+             SCM q = scm_i_mkbig ();
+             scm_t_inum rr;
+             if (yy > 0)
+               rr = mpz_tdiv_q_ui (SCM_I_BIG_MPZ (q),
+                                   SCM_I_BIG_MPZ (x), yy);
+             else
+               {
+                 rr = mpz_tdiv_q_ui (SCM_I_BIG_MPZ (q),
+                                     SCM_I_BIG_MPZ (x), -yy);
+                 mpz_neg (SCM_I_BIG_MPZ (q), SCM_I_BIG_MPZ (q));
+               }
+             rr *= mpz_sgn (SCM_I_BIG_MPZ (x));
+             scm_remember_upto_here_1 (x);
+             *qp = scm_i_normbig (q);
+             *rp = SCM_I_MAKINUM (rr);
+           }
+         return;
+       }
+      else if (SCM_BIGP (y))
+       {
+         SCM q = scm_i_mkbig ();
+         SCM r = scm_i_mkbig ();
+         mpz_tdiv_qr (SCM_I_BIG_MPZ (q), SCM_I_BIG_MPZ (r),
+                      SCM_I_BIG_MPZ (x), SCM_I_BIG_MPZ (y));
+         scm_remember_upto_here_2 (x, y);
+         *qp = scm_i_normbig (q);
+         *rp = scm_i_normbig (r);
+       }
+      else if (SCM_REALP (y))
+       return scm_i_inexact_truncate_divide
+         (scm_i_big2dbl (x), SCM_REAL_VALUE (y), qp, rp);
+      else if (SCM_FRACTIONP (y))
+       return scm_i_exact_rational_truncate_divide (x, y, qp, rp);
+      else
+       return two_valued_wta_dispatch_2
+         (g_scm_truncate_divide, x, y, SCM_ARG2,
+          s_scm_truncate_divide, qp, rp);
+    }
+  else if (SCM_REALP (x))
+    {
+      if (SCM_REALP (y) || SCM_I_INUMP (y) ||
+         SCM_BIGP (y) || SCM_FRACTIONP (y))
+       return scm_i_inexact_truncate_divide
+         (SCM_REAL_VALUE (x), scm_to_double (y), qp, rp);
+      else
+       return two_valued_wta_dispatch_2
+         (g_scm_truncate_divide, x, y, SCM_ARG2,
+          s_scm_truncate_divide, qp, rp);
+    }
+  else if (SCM_FRACTIONP (x))
+    {
+      if (SCM_REALP (y))
+       return scm_i_inexact_truncate_divide
+         (scm_i_fraction2double (x), SCM_REAL_VALUE (y), qp, rp);
+      else if (SCM_I_INUMP (y) || SCM_BIGP (y) || SCM_FRACTIONP (y))
+       return scm_i_exact_rational_truncate_divide (x, y, qp, rp);
+      else
+       return two_valued_wta_dispatch_2
+         (g_scm_truncate_divide, x, y, SCM_ARG2,
+          s_scm_truncate_divide, qp, rp);
+    }
+  else
+    return two_valued_wta_dispatch_2 (g_scm_truncate_divide, x, y, SCM_ARG1,
+                                     s_scm_truncate_divide, qp, rp);
+}
+
+static void
+scm_i_inexact_truncate_divide (double x, double y, SCM *qp, SCM *rp)
+{
+  if (SCM_UNLIKELY (y == 0))
+    scm_num_overflow (s_scm_truncate_divide);  /* or return a NaN? */
+  else
+    {
+      double q, r, q1;
+      /* FIXME: Use trunc, after it has been imported from gnulib */
+      q1 = x / y;
+      q = (q1 >= 0) ? floor (q1) : ceil (q1);
+      r = x - q * y;
+      *qp = scm_from_double (q);
+      *rp = scm_from_double (r);
+    }
+}
+
+static void
+scm_i_exact_rational_truncate_divide (SCM x, SCM y, SCM *qp, SCM *rp)
+{
+  SCM r1;
+  SCM xd = scm_denominator (x);
+  SCM yd = scm_denominator (y);
+
+  scm_truncate_divide (scm_product (scm_numerator (x), yd),
+                      scm_product (scm_numerator (y), xd),
+                      qp, &r1);
+  *rp = scm_divide (r1, scm_product (xd, yd));
+}
+
+static SCM scm_i_inexact_centered_quotient (double x, double y);
+static SCM scm_i_bigint_centered_quotient (SCM x, SCM y);
+static SCM scm_i_exact_rational_centered_quotient (SCM x, SCM y);
+
+SCM_PRIMITIVE_GENERIC (scm_centered_quotient, "centered-quotient", 2, 0, 0,
+                      (SCM x, SCM y),
+                      "Return the integer @var{q} such that\n"
+                      "@address@hidden = @address@hidden + @var{r}} where\n"
+                      "@math{-abs(@var{y}/2) <= @var{r} < abs(@var{y}/2)}.\n"
+                      "@lisp\n"
+                      "(centered-quotient 123 10) @result{} 12\n"
+                      "(centered-quotient 123 -10) @result{} -12\n"
+                      "(centered-quotient -123 10) @result{} -12\n"
+                      "(centered-quotient -123 -10) @result{} 12\n"
+                      "(centered-quotient -123.2 -63.5) @result{} 2.0\n"
+                      "(centered-quotient 16/3 -10/7) @result{} -4\n"
+                      "@end lisp")
+#define FUNC_NAME s_scm_centered_quotient
+{
+  if (SCM_LIKELY (SCM_I_INUMP (x)))
+    {
+      scm_t_inum xx = SCM_I_INUM (x);
+      if (SCM_LIKELY (SCM_I_INUMP (y)))
+       {
+         scm_t_inum yy = SCM_I_INUM (y);
+         if (SCM_UNLIKELY (yy == 0))
+           scm_num_overflow (s_scm_centered_quotient);
+         else
+           {
+             scm_t_inum qq = xx / yy;
+             scm_t_inum rr = xx % yy;
+             if (SCM_LIKELY (xx > 0))
+               {
+                 if (SCM_LIKELY (yy > 0))
+                   {
+                     if (rr >= (yy + 1) / 2)
+                       qq++;
+                   }
+                 else
+                   {
+                     if (rr >= (1 - yy) / 2)
+                       qq--;
+                   }
+               }
+             else
+               {
+                 if (SCM_LIKELY (yy > 0))
+                   {
+                     if (rr < -yy / 2)
+                       qq--;
+                   }
+                 else
+                   {
+                     if (rr < yy / 2)
+                       qq++;
+                   }
+               }
+             if (SCM_LIKELY (SCM_FIXABLE (qq)))
+               return SCM_I_MAKINUM (qq);
+             else
+               return scm_i_inum2big (qq);
+           }
+       }
+      else if (SCM_BIGP (y))
+       {
+         /* Pass a denormalized bignum version of x (even though it
+            can fit in a fixnum) to scm_i_bigint_centered_quotient */
+         return scm_i_bigint_centered_quotient (scm_i_long2big (xx), y);
+       }
+      else if (SCM_REALP (y))
+       return scm_i_inexact_centered_quotient (xx, SCM_REAL_VALUE (y));
+      else if (SCM_FRACTIONP (y))
+       return scm_i_exact_rational_centered_quotient (x, y);
+      else
+       SCM_WTA_DISPATCH_2 (g_scm_centered_quotient, x, y, SCM_ARG2,
+                           s_scm_centered_quotient);
+    }
+  else if (SCM_BIGP (x))
+    {
+      if (SCM_LIKELY (SCM_I_INUMP (y)))
+       {
+         scm_t_inum yy = SCM_I_INUM (y);
+         if (SCM_UNLIKELY (yy == 0))
+           scm_num_overflow (s_scm_centered_quotient);
+         else if (SCM_UNLIKELY (yy == 1))
+           return x;
+         else
+           {
+             SCM q = scm_i_mkbig ();
+             scm_t_inum rr;
+             /* Arrange for rr to initially be non-positive,
+                because that simplifies the test to see
+                if it is within the needed bounds. */
+             if (yy > 0)
+               {
+                 rr = - mpz_cdiv_q_ui (SCM_I_BIG_MPZ (q),
+                                       SCM_I_BIG_MPZ (x), yy);
+                 scm_remember_upto_here_1 (x);
+                 if (rr < -yy / 2)
+                   mpz_sub_ui (SCM_I_BIG_MPZ (q),
+                               SCM_I_BIG_MPZ (q), 1);
+               }
+             else
+               {
+                 rr = - mpz_cdiv_q_ui (SCM_I_BIG_MPZ (q),
+                                       SCM_I_BIG_MPZ (x), -yy);
+                 scm_remember_upto_here_1 (x);
+                 mpz_neg (SCM_I_BIG_MPZ (q), SCM_I_BIG_MPZ (q));
+                 if (rr < yy / 2)
+                   mpz_add_ui (SCM_I_BIG_MPZ (q),
+                               SCM_I_BIG_MPZ (q), 1);
+               }
+             return scm_i_normbig (q);
+           }
+       }
+      else if (SCM_BIGP (y))
+       return scm_i_bigint_centered_quotient (x, y);
+      else if (SCM_REALP (y))
+       return scm_i_inexact_centered_quotient
+         (scm_i_big2dbl (x), SCM_REAL_VALUE (y));
+      else if (SCM_FRACTIONP (y))
+       return scm_i_exact_rational_centered_quotient (x, y);
+      else
+       SCM_WTA_DISPATCH_2 (g_scm_centered_quotient, x, y, SCM_ARG2,
+                           s_scm_centered_quotient);
+    }
+  else if (SCM_REALP (x))
+    {
+      if (SCM_REALP (y) || SCM_I_INUMP (y) ||
+         SCM_BIGP (y) || SCM_FRACTIONP (y))
+       return scm_i_inexact_centered_quotient
+         (SCM_REAL_VALUE (x), scm_to_double (y));
+      else
+       SCM_WTA_DISPATCH_2 (g_scm_centered_quotient, x, y, SCM_ARG2,
+                           s_scm_centered_quotient);
+    }
+  else if (SCM_FRACTIONP (x))
+    {
+      if (SCM_REALP (y))
+       return scm_i_inexact_centered_quotient
+         (scm_i_fraction2double (x), SCM_REAL_VALUE (y));
+      else if (SCM_I_INUMP (y) || SCM_BIGP (y) || SCM_FRACTIONP (y))
+       return scm_i_exact_rational_centered_quotient (x, y);
+      else
+       SCM_WTA_DISPATCH_2 (g_scm_centered_quotient, x, y, SCM_ARG2,
+                           s_scm_centered_quotient);
+    }
+  else
+    SCM_WTA_DISPATCH_2 (g_scm_centered_quotient, x, y, SCM_ARG1,
+                       s_scm_centered_quotient);
+}
+#undef FUNC_NAME
+
+static SCM
+scm_i_inexact_centered_quotient (double x, double y)
+{
+  if (SCM_LIKELY (y > 0))
+    return scm_from_double (floor (x/y + 0.5));
+  else if (SCM_LIKELY (y < 0))
+    return scm_from_double (ceil (x/y - 0.5));
+  else if (y == 0)
+    scm_num_overflow (s_scm_centered_quotient);  /* or return a NaN? */
+  else
+    return scm_nan ();
+}
+
+/* Assumes that both x and y are bigints, though
+   x might be able to fit into a fixnum. */
+static SCM
+scm_i_bigint_centered_quotient (SCM x, SCM y)
+{
+  SCM q, r, min_r;
+
+  /* Note that x might be small enough to fit into a
+     fixnum, so we must not let it escape into the wild */
+  q = scm_i_mkbig ();
+  r = scm_i_mkbig ();
+
+  /* min_r will eventually become -abs(y)/2 */
+  min_r = scm_i_mkbig ();
+  mpz_tdiv_q_2exp (SCM_I_BIG_MPZ (min_r),
+                  SCM_I_BIG_MPZ (y), 1);
+
+  /* Arrange for rr to initially be non-positive,
+     because that simplifies the test to see
+     if it is within the needed bounds. */
+  if (mpz_sgn (SCM_I_BIG_MPZ (y)) > 0)
+    {
+      mpz_cdiv_qr (SCM_I_BIG_MPZ (q), SCM_I_BIG_MPZ (r),
+                  SCM_I_BIG_MPZ (x), SCM_I_BIG_MPZ (y));
+      scm_remember_upto_here_2 (x, y);
+      mpz_neg (SCM_I_BIG_MPZ (min_r), SCM_I_BIG_MPZ (min_r));
+      if (mpz_cmp (SCM_I_BIG_MPZ (r), SCM_I_BIG_MPZ (min_r)) < 0)
+       mpz_sub_ui (SCM_I_BIG_MPZ (q),
+                   SCM_I_BIG_MPZ (q), 1);
+    }
+  else
+    {
+      mpz_fdiv_qr (SCM_I_BIG_MPZ (q), SCM_I_BIG_MPZ (r),
+                  SCM_I_BIG_MPZ (x), SCM_I_BIG_MPZ (y));
+      scm_remember_upto_here_2 (x, y);
+      if (mpz_cmp (SCM_I_BIG_MPZ (r), SCM_I_BIG_MPZ (min_r)) < 0)
+       mpz_add_ui (SCM_I_BIG_MPZ (q),
+                   SCM_I_BIG_MPZ (q), 1);
+    }
+  scm_remember_upto_here_2 (r, min_r);
+  return scm_i_normbig (q);
+}
+
+static SCM
+scm_i_exact_rational_centered_quotient (SCM x, SCM y)
+{
+  return scm_centered_quotient
+    (scm_product (scm_numerator (x), scm_denominator (y)),
+     scm_product (scm_numerator (y), scm_denominator (x)));
+}
+
+static SCM scm_i_inexact_centered_remainder (double x, double y);
+static SCM scm_i_bigint_centered_remainder (SCM x, SCM y);
+static SCM scm_i_exact_rational_centered_remainder (SCM x, SCM y);
+
+SCM_PRIMITIVE_GENERIC (scm_centered_remainder, "centered-remainder", 2, 0, 0,
+                      (SCM x, SCM y),
+                      "Return the real number @var{r} such that\n"
+                      "@math{-abs(@var{y}/2) <= @var{r} < abs(@var{y}/2)}\n"
+                      "and @address@hidden = @address@hidden + @var{r}}\n"
+                      "for some integer @var{q}.\n"
+                      "@lisp\n"
+                      "(centered-remainder 123 10) @result{} 3\n"
+                      "(centered-remainder 123 -10) @result{} 3\n"
+                      "(centered-remainder -123 10) @result{} -3\n"
+                      "(centered-remainder -123 -10) @result{} -3\n"
+                      "(centered-remainder -123.2 -63.5) @result{} 3.8\n"
+                      "(centered-remainder 16/3 -10/7) @result{} -8/21\n"
+                      "@end lisp")
+#define FUNC_NAME s_scm_centered_remainder
+{
+  if (SCM_LIKELY (SCM_I_INUMP (x)))
+    {
+      scm_t_inum xx = SCM_I_INUM (x);
+      if (SCM_LIKELY (SCM_I_INUMP (y)))
+       {
+         scm_t_inum yy = SCM_I_INUM (y);
+         if (SCM_UNLIKELY (yy == 0))
+           scm_num_overflow (s_scm_centered_remainder);
+         else
+           {
+             scm_t_inum rr = xx % yy;
+             if (SCM_LIKELY (xx > 0))
+               {
+                 if (SCM_LIKELY (yy > 0))
+                   {
+                     if (rr >= (yy + 1) / 2)
+                       rr -= yy;
+                   }
+                 else
+                   {
+                     if (rr >= (1 - yy) / 2)
+                       rr += yy;
+                   }
+               }
+             else
+               {
+                 if (SCM_LIKELY (yy > 0))
+                   {
+                     if (rr < -yy / 2)
+                       rr += yy;
+                   }
+                 else
+                   {
+                     if (rr < yy / 2)
+                       rr -= yy;
+                   }
+               }
+             return SCM_I_MAKINUM (rr);
+           }
+       }
+      else if (SCM_BIGP (y))
+       {
+         /* Pass a denormalized bignum version of x (even though it
+            can fit in a fixnum) to scm_i_bigint_centered_remainder */
+         return scm_i_bigint_centered_remainder (scm_i_long2big (xx), y);
+       }
+      else if (SCM_REALP (y))
+       return scm_i_inexact_centered_remainder (xx, SCM_REAL_VALUE (y));
+      else if (SCM_FRACTIONP (y))
+       return scm_i_exact_rational_centered_remainder (x, y);
+      else
+       SCM_WTA_DISPATCH_2 (g_scm_centered_remainder, x, y, SCM_ARG2,
+                           s_scm_centered_remainder);
+    }
+  else if (SCM_BIGP (x))
+    {
+      if (SCM_LIKELY (SCM_I_INUMP (y)))
+       {
+         scm_t_inum yy = SCM_I_INUM (y);
+         if (SCM_UNLIKELY (yy == 0))
+           scm_num_overflow (s_scm_centered_remainder);
+         else
+           {
+             scm_t_inum rr;
+             /* Arrange for rr to initially be non-positive,
+                because that simplifies the test to see
+                if it is within the needed bounds. */
+             if (yy > 0)
+               {
+                 rr = - mpz_cdiv_ui (SCM_I_BIG_MPZ (x), yy);
+                 scm_remember_upto_here_1 (x);
+                 if (rr < -yy / 2)
+                   rr += yy;
+               }
+             else
+               {
+                 rr = - mpz_cdiv_ui (SCM_I_BIG_MPZ (x), -yy);
+                 scm_remember_upto_here_1 (x);
+                 if (rr < yy / 2)
+                   rr -= yy;
+               }
+             return SCM_I_MAKINUM (rr);
+           }
+       }
+      else if (SCM_BIGP (y))
+       return scm_i_bigint_centered_remainder (x, y);
+      else if (SCM_REALP (y))
+       return scm_i_inexact_centered_remainder
+         (scm_i_big2dbl (x), SCM_REAL_VALUE (y));
+      else if (SCM_FRACTIONP (y))
+       return scm_i_exact_rational_centered_remainder (x, y);
+      else
+       SCM_WTA_DISPATCH_2 (g_scm_centered_remainder, x, y, SCM_ARG2,
+                           s_scm_centered_remainder);
+    }
+  else if (SCM_REALP (x))
+    {
+      if (SCM_REALP (y) || SCM_I_INUMP (y) ||
+         SCM_BIGP (y) || SCM_FRACTIONP (y))
+       return scm_i_inexact_centered_remainder
+         (SCM_REAL_VALUE (x), scm_to_double (y));
+      else
+       SCM_WTA_DISPATCH_2 (g_scm_centered_remainder, x, y, SCM_ARG2,
+                           s_scm_centered_remainder);
+    }
+  else if (SCM_FRACTIONP (x))
+    {
+      if (SCM_REALP (y))
+       return scm_i_inexact_centered_remainder
+         (scm_i_fraction2double (x), SCM_REAL_VALUE (y));
+      else if (SCM_I_INUMP (y) || SCM_BIGP (y) || SCM_FRACTIONP (y))
+       return scm_i_exact_rational_centered_remainder (x, y);
+      else
+       SCM_WTA_DISPATCH_2 (g_scm_centered_remainder, x, y, SCM_ARG2,
+                           s_scm_centered_remainder);
+    }
+  else
+    SCM_WTA_DISPATCH_2 (g_scm_centered_remainder, x, y, SCM_ARG1,
+                       s_scm_centered_remainder);
+}
+#undef FUNC_NAME
+
+static SCM
+scm_i_inexact_centered_remainder (double x, double y)
+{
+  double q;
+
+  /* Although it would be more efficient to use fmod here, we can't
+     because it would in some cases produce results inconsistent with
+     scm_i_inexact_centered_quotient, such that x != r + q * y (not even
+     close).  In particular, when x-y/2 is very close to a multiple of
+     y, then r might be either -abs(y/2) or abs(y/2)-epsilon, but those
+     two cases must correspond to different choices of q.  If quotient
+     chooses one and remainder chooses the other, it would be bad. */
+  if (SCM_LIKELY (y > 0))
+    q = floor (x/y + 0.5);
+  else if (SCM_LIKELY (y < 0))
+    q = ceil (x/y - 0.5);
+  else if (y == 0)
+    scm_num_overflow (s_scm_centered_remainder);  /* or return a NaN? */
+  else
+    return scm_nan ();
+  return scm_from_double (x - q * y);
+}
+
+/* Assumes that both x and y are bigints, though
+   x might be able to fit into a fixnum. */
+static SCM
+scm_i_bigint_centered_remainder (SCM x, SCM y)
+{
+  SCM r, min_r;
+
+  /* Note that x might be small enough to fit into a
+     fixnum, so we must not let it escape into the wild */
+  r = scm_i_mkbig ();
+
+  /* min_r will eventually become -abs(y)/2 */
+  min_r = scm_i_mkbig ();
+  mpz_tdiv_q_2exp (SCM_I_BIG_MPZ (min_r),
+                  SCM_I_BIG_MPZ (y), 1);
+
+  /* Arrange for rr to initially be non-positive,
+     because that simplifies the test to see
+     if it is within the needed bounds. */
+  if (mpz_sgn (SCM_I_BIG_MPZ (y)) > 0)
+    {
+      mpz_cdiv_r (SCM_I_BIG_MPZ (r),
+                 SCM_I_BIG_MPZ (x), SCM_I_BIG_MPZ (y));
+      mpz_neg (SCM_I_BIG_MPZ (min_r), SCM_I_BIG_MPZ (min_r));
+      if (mpz_cmp (SCM_I_BIG_MPZ (r), SCM_I_BIG_MPZ (min_r)) < 0)
+       mpz_add (SCM_I_BIG_MPZ (r),
+                SCM_I_BIG_MPZ (r),
+                SCM_I_BIG_MPZ (y));
+    }
+  else
+    {
+      mpz_fdiv_r (SCM_I_BIG_MPZ (r),
+                 SCM_I_BIG_MPZ (x), SCM_I_BIG_MPZ (y));
+      if (mpz_cmp (SCM_I_BIG_MPZ (r), SCM_I_BIG_MPZ (min_r)) < 0)
+       mpz_sub (SCM_I_BIG_MPZ (r),
+                SCM_I_BIG_MPZ (r),
+                SCM_I_BIG_MPZ (y));
+    }
+  scm_remember_upto_here_2 (x, y);
+  return scm_i_normbig (r);
+}
+
+static SCM
+scm_i_exact_rational_centered_remainder (SCM x, SCM y)
+{
+  SCM xd = scm_denominator (x);
+  SCM yd = scm_denominator (y);
+  SCM r1 = scm_centered_remainder (scm_product (scm_numerator (x), yd),
+                                  scm_product (scm_numerator (y), xd));
+  return scm_divide (r1, scm_product (xd, yd));
+}
+
+
+static void scm_i_inexact_centered_divide (double x, double y,
+                                          SCM *qp, SCM *rp);
+static void scm_i_bigint_centered_divide (SCM x, SCM y, SCM *qp, SCM *rp);
+static void scm_i_exact_rational_centered_divide (SCM x, SCM y,
+                                                 SCM *qp, SCM *rp);
+
+SCM_PRIMITIVE_GENERIC (scm_i_centered_divide, "centered/", 2, 0, 0,
+                      (SCM x, SCM y),
+                      "Return the integer @var{q} and the real number 
@var{r}\n"
+                      "such that @address@hidden = @address@hidden + 
@var{r}}\n"
+                      "and @math{-abs(@var{y}/2) <= @var{r} < 
abs(@var{y}/2)}.\n"
+                      "@lisp\n"
+                      "(centered/ 123 10) @result{} 12 and 3\n"
+                      "(centered/ 123 -10) @result{} -12 and 3\n"
+                      "(centered/ -123 10) @result{} -12 and -3\n"
+                      "(centered/ -123 -10) @result{} 12 and -3\n"
+                      "(centered/ -123.2 -63.5) @result{} 2.0 and 3.8\n"
+                      "(centered/ 16/3 -10/7) @result{} -4 and -8/21\n"
+                      "@end lisp")
+#define FUNC_NAME s_scm_i_centered_divide
+{
+  SCM q, r;
+
+  scm_centered_divide(x, y, &q, &r);
+  return scm_values (scm_list_2 (q, r));
+}
+#undef FUNC_NAME
+
+#define s_scm_centered_divide s_scm_i_centered_divide
+#define g_scm_centered_divide g_scm_i_centered_divide
+
+void
+scm_centered_divide (SCM x, SCM y, SCM *qp, SCM *rp)
+{
+  if (SCM_LIKELY (SCM_I_INUMP (x)))
+    {
+      scm_t_inum xx = SCM_I_INUM (x);
+      if (SCM_LIKELY (SCM_I_INUMP (y)))
+       {
+         scm_t_inum yy = SCM_I_INUM (y);
+         if (SCM_UNLIKELY (yy == 0))
+           scm_num_overflow (s_scm_centered_divide);
+         else
+           {
+             scm_t_inum qq = xx / yy;
+             scm_t_inum rr = xx % yy;
+             if (SCM_LIKELY (xx > 0))
+               {
+                 if (SCM_LIKELY (yy > 0))
+                   {
+                     if (rr >= (yy + 1) / 2)
+                       { qq++; rr -= yy; }
+                   }
+                 else
+                   {
+                     if (rr >= (1 - yy) / 2)
+                       { qq--; rr += yy; }
+                   }
+               }
+             else
+               {
+                 if (SCM_LIKELY (yy > 0))
+                   {
+                     if (rr < -yy / 2)
+                       { qq--; rr += yy; }
+                   }
+                 else
+                   {
+                     if (rr < yy / 2)
+                       { qq++; rr -= yy; }
+                   }
+               }
+             if (SCM_LIKELY (SCM_FIXABLE (qq)))
+               *qp = SCM_I_MAKINUM (qq);
+             else
+               *qp = scm_i_inum2big (qq);
+             *rp = SCM_I_MAKINUM (rr);
+           }
+         return;
+       }
+      else if (SCM_BIGP (y))
+       {
+         /* Pass a denormalized bignum version of x (even though it
+            can fit in a fixnum) to scm_i_bigint_centered_divide */
+         return scm_i_bigint_centered_divide (scm_i_long2big (xx), y, qp, rp);
+       }
+      else if (SCM_REALP (y))
+       return scm_i_inexact_centered_divide (xx, SCM_REAL_VALUE (y), qp, rp);
+      else if (SCM_FRACTIONP (y))
+       return scm_i_exact_rational_centered_divide (x, y, qp, rp);
+      else
+       return two_valued_wta_dispatch_2
+         (g_scm_centered_divide, x, y, SCM_ARG2,
+          s_scm_centered_divide, qp, rp);
+    }
+  else if (SCM_BIGP (x))
+    {
+      if (SCM_LIKELY (SCM_I_INUMP (y)))
+       {
+         scm_t_inum yy = SCM_I_INUM (y);
+         if (SCM_UNLIKELY (yy == 0))
+           scm_num_overflow (s_scm_centered_divide);
+         else
+           {
+             SCM q = scm_i_mkbig ();
+             scm_t_inum rr;
+             /* Arrange for rr to initially be non-positive,
+                because that simplifies the test to see
+                if it is within the needed bounds. */
+             if (yy > 0)
+               {
+                 rr = - mpz_cdiv_q_ui (SCM_I_BIG_MPZ (q),
+                                       SCM_I_BIG_MPZ (x), yy);
+                 scm_remember_upto_here_1 (x);
+                 if (rr < -yy / 2)
+                   {
+                     mpz_sub_ui (SCM_I_BIG_MPZ (q),
+                                 SCM_I_BIG_MPZ (q), 1);
+                     rr += yy;
+                   }
+               }
+             else
+               {
+                 rr = - mpz_cdiv_q_ui (SCM_I_BIG_MPZ (q),
+                                       SCM_I_BIG_MPZ (x), -yy);
+                 scm_remember_upto_here_1 (x);
+                 mpz_neg (SCM_I_BIG_MPZ (q), SCM_I_BIG_MPZ (q));
+                 if (rr < yy / 2)
+                   {
+                     mpz_add_ui (SCM_I_BIG_MPZ (q),
+                                 SCM_I_BIG_MPZ (q), 1);
+                     rr -= yy;
+                   }
+               }
+             *qp = scm_i_normbig (q);
+             *rp = SCM_I_MAKINUM (rr);
+           }
+         return;
+       }
+      else if (SCM_BIGP (y))
+       return scm_i_bigint_centered_divide (x, y, qp, rp);
+      else if (SCM_REALP (y))
+       return scm_i_inexact_centered_divide
+         (scm_i_big2dbl (x), SCM_REAL_VALUE (y), qp, rp);
+      else if (SCM_FRACTIONP (y))
+       return scm_i_exact_rational_centered_divide (x, y, qp, rp);
+      else
+       return two_valued_wta_dispatch_2
+         (g_scm_centered_divide, x, y, SCM_ARG2,
+          s_scm_centered_divide, qp, rp);
+    }
+  else if (SCM_REALP (x))
+    {
+      if (SCM_REALP (y) || SCM_I_INUMP (y) ||
+         SCM_BIGP (y) || SCM_FRACTIONP (y))
+       return scm_i_inexact_centered_divide
+         (SCM_REAL_VALUE (x), scm_to_double (y), qp, rp);
+      else
+       return two_valued_wta_dispatch_2
+         (g_scm_centered_divide, x, y, SCM_ARG2,
+          s_scm_centered_divide, qp, rp);
+    }
+  else if (SCM_FRACTIONP (x))
+    {
+      if (SCM_REALP (y))
+       return scm_i_inexact_centered_divide
+         (scm_i_fraction2double (x), SCM_REAL_VALUE (y), qp, rp);
+      else if (SCM_I_INUMP (y) || SCM_BIGP (y) || SCM_FRACTIONP (y))
+       return scm_i_exact_rational_centered_divide (x, y, qp, rp);
+      else
+       return two_valued_wta_dispatch_2
+         (g_scm_centered_divide, x, y, SCM_ARG2,
+          s_scm_centered_divide, qp, rp);
+    }
+  else
+    return two_valued_wta_dispatch_2 (g_scm_centered_divide, x, y, SCM_ARG1,
+                                     s_scm_centered_divide, qp, rp);
+}
+
+static void
+scm_i_inexact_centered_divide (double x, double y, SCM *qp, SCM *rp)
+{
+  double q, r;
+
+  if (SCM_LIKELY (y > 0))
+    q = floor (x/y + 0.5);
+  else if (SCM_LIKELY (y < 0))
+    q = ceil (x/y - 0.5);
+  else if (y == 0)
+    scm_num_overflow (s_scm_centered_divide);  /* or return a NaN? */
+  else
+    q = guile_NaN;
+  r = x - q * y;
+  *qp = scm_from_double (q);
+  *rp = scm_from_double (r);
+}
+
+/* Assumes that both x and y are bigints, though
+   x might be able to fit into a fixnum. */
+static void
+scm_i_bigint_centered_divide (SCM x, SCM y, SCM *qp, SCM *rp)
+{
+  SCM q, r, min_r;
+
+  /* Note that x might be small enough to fit into a
+     fixnum, so we must not let it escape into the wild */
+  q = scm_i_mkbig ();
+  r = scm_i_mkbig ();
+
+  /* min_r will eventually become -abs(y/2) */
+  min_r = scm_i_mkbig ();
+  mpz_tdiv_q_2exp (SCM_I_BIG_MPZ (min_r),
+                  SCM_I_BIG_MPZ (y), 1);
+
+  /* Arrange for rr to initially be non-positive,
+     because that simplifies the test to see
+     if it is within the needed bounds. */
+  if (mpz_sgn (SCM_I_BIG_MPZ (y)) > 0)
+    {
+      mpz_cdiv_qr (SCM_I_BIG_MPZ (q), SCM_I_BIG_MPZ (r),
+                  SCM_I_BIG_MPZ (x), SCM_I_BIG_MPZ (y));
+      mpz_neg (SCM_I_BIG_MPZ (min_r), SCM_I_BIG_MPZ (min_r));
+      if (mpz_cmp (SCM_I_BIG_MPZ (r), SCM_I_BIG_MPZ (min_r)) < 0)
+       {
+         mpz_sub_ui (SCM_I_BIG_MPZ (q),
+                     SCM_I_BIG_MPZ (q), 1);
+         mpz_add (SCM_I_BIG_MPZ (r),
+                  SCM_I_BIG_MPZ (r),
+                  SCM_I_BIG_MPZ (y));
+       }
+    }
+  else
+    {
+      mpz_fdiv_qr (SCM_I_BIG_MPZ (q), SCM_I_BIG_MPZ (r),
+                  SCM_I_BIG_MPZ (x), SCM_I_BIG_MPZ (y));
+      if (mpz_cmp (SCM_I_BIG_MPZ (r), SCM_I_BIG_MPZ (min_r)) < 0)
+       {
+         mpz_add_ui (SCM_I_BIG_MPZ (q),
+                     SCM_I_BIG_MPZ (q), 1);
+         mpz_sub (SCM_I_BIG_MPZ (r),
+                  SCM_I_BIG_MPZ (r),
+                  SCM_I_BIG_MPZ (y));
+       }
+    }
+  scm_remember_upto_here_2 (x, y);
+  *qp = scm_i_normbig (q);
+  *rp = scm_i_normbig (r);
+}
+
+static void
+scm_i_exact_rational_centered_divide (SCM x, SCM y, SCM *qp, SCM *rp)
+{
+  SCM r1;
+  SCM xd = scm_denominator (x);
+  SCM yd = scm_denominator (y);
+
+  scm_centered_divide (scm_product (scm_numerator (x), yd),
+                      scm_product (scm_numerator (y), xd),
+                      qp, &r1);
+  *rp = scm_divide (r1, scm_product (xd, yd));
+}
+
+static SCM scm_i_inexact_round_quotient (double x, double y);
+static SCM scm_i_bigint_round_quotient (SCM x, SCM y);
+static SCM scm_i_exact_rational_round_quotient (SCM x, SCM y);
+
+SCM_PRIMITIVE_GENERIC (scm_round_quotient, "round-quotient", 2, 0, 0,
+                      (SCM x, SCM y),
+                      "Return @address@hidden / @var{y}} to the nearest 
integer,\n"
+                      "with ties going to the nearest even integer.\n"
+                      "@lisp\n"
+                      "(round-quotient 123 10) @result{} 12\n"
+                      "(round-quotient 123 -10) @result{} -12\n"
+                      "(round-quotient -123 10) @result{} -12\n"
+                      "(round-quotient -123 -10) @result{} 12\n"
+                      "(round-quotient 125 10) @result{} 12\n"
+                      "(round-quotient 127 10) @result{} 13\n"
+                      "(round-quotient 135 10) @result{} 14\n"
+                      "(round-quotient -123.2 -63.5) @result{} 2.0\n"
+                      "(round-quotient 16/3 -10/7) @result{} -4\n"
+                      "@end lisp")
+#define FUNC_NAME s_scm_round_quotient
+{
+  if (SCM_LIKELY (SCM_I_INUMP (x)))
+    {
+      scm_t_inum xx = SCM_I_INUM (x);
+      if (SCM_LIKELY (SCM_I_INUMP (y)))
+       {
+         scm_t_inum yy = SCM_I_INUM (y);
+         if (SCM_UNLIKELY (yy == 0))
+           scm_num_overflow (s_scm_round_quotient);
+         else
+           {
+             scm_t_inum qq = xx / yy;
+             scm_t_inum rr = xx % yy;
+             scm_t_inum ay = yy;
+             scm_t_inum r2 = 2 * rr;
+
+             if (SCM_LIKELY (yy < 0))
+               {
+                 ay = -ay;
+                 r2 = -r2;
+               }
+
+             if (qq & 1L)
+               {
+                 if (r2 >= ay)
+                   qq++;
+                 else if (r2 <= -ay)
+                   qq--;
+               }
+             else
+               {
+                 if (r2 > ay)
+                   qq++;
+                 else if (r2 < -ay)
+                   qq--;
+               }
+             if (SCM_LIKELY (SCM_FIXABLE (qq)))
+               return SCM_I_MAKINUM (qq);
+             else
+               return scm_i_inum2big (qq);
+           }
+       }
+      else if (SCM_BIGP (y))
+       {
+         /* Pass a denormalized bignum version of x (even though it
+            can fit in a fixnum) to scm_i_bigint_round_quotient */
+         return scm_i_bigint_round_quotient (scm_i_long2big (xx), y);
+       }
+      else if (SCM_REALP (y))
+       return scm_i_inexact_round_quotient (xx, SCM_REAL_VALUE (y));
+      else if (SCM_FRACTIONP (y))
+       return scm_i_exact_rational_round_quotient (x, y);
+      else
+       SCM_WTA_DISPATCH_2 (g_scm_round_quotient, x, y, SCM_ARG2,
+                           s_scm_round_quotient);
+    }
+  else if (SCM_BIGP (x))
+    {
+      if (SCM_LIKELY (SCM_I_INUMP (y)))
+       {
+         scm_t_inum yy = SCM_I_INUM (y);
+         if (SCM_UNLIKELY (yy == 0))
+           scm_num_overflow (s_scm_round_quotient);
+         else if (SCM_UNLIKELY (yy == 1))
+           return x;
+         else
+           {
+             SCM q = scm_i_mkbig ();
+             scm_t_inum rr;
+             int needs_adjustment;
+
+             if (yy > 0)
+               {
+                 rr = mpz_fdiv_q_ui (SCM_I_BIG_MPZ (q),
+                                     SCM_I_BIG_MPZ (x), yy);
+                 if (mpz_odd_p (SCM_I_BIG_MPZ (q)))
+                   needs_adjustment = (2*rr >= yy);
+                 else
+                   needs_adjustment = (2*rr > yy);
+               }
+             else
+               {
+                 rr = - mpz_cdiv_q_ui (SCM_I_BIG_MPZ (q),
+                                       SCM_I_BIG_MPZ (x), -yy);
+                 mpz_neg (SCM_I_BIG_MPZ (q), SCM_I_BIG_MPZ (q));
+                 if (mpz_odd_p (SCM_I_BIG_MPZ (q)))
+                   needs_adjustment = (2*rr <= yy);
+                 else
+                   needs_adjustment = (2*rr < yy);
+               }
+             scm_remember_upto_here_1 (x);
+             if (needs_adjustment)
+               mpz_add_ui (SCM_I_BIG_MPZ (q), SCM_I_BIG_MPZ (q), 1);
+             return scm_i_normbig (q);
+           }
+       }
+      else if (SCM_BIGP (y))
+       return scm_i_bigint_round_quotient (x, y);
+      else if (SCM_REALP (y))
+       return scm_i_inexact_round_quotient
+         (scm_i_big2dbl (x), SCM_REAL_VALUE (y));
+      else if (SCM_FRACTIONP (y))
+       return scm_i_exact_rational_round_quotient (x, y);
+      else
+       SCM_WTA_DISPATCH_2 (g_scm_round_quotient, x, y, SCM_ARG2,
+                           s_scm_round_quotient);
+    }
+  else if (SCM_REALP (x))
+    {
+      if (SCM_REALP (y) || SCM_I_INUMP (y) ||
+         SCM_BIGP (y) || SCM_FRACTIONP (y))
+       return scm_i_inexact_round_quotient
+         (SCM_REAL_VALUE (x), scm_to_double (y));
+      else
+       SCM_WTA_DISPATCH_2 (g_scm_round_quotient, x, y, SCM_ARG2,
+                           s_scm_round_quotient);
+    }
+  else if (SCM_FRACTIONP (x))
+    {
+      if (SCM_REALP (y))
+       return scm_i_inexact_round_quotient
+         (scm_i_fraction2double (x), SCM_REAL_VALUE (y));
+      else if (SCM_I_INUMP (y) || SCM_BIGP (y) || SCM_FRACTIONP (y))
+       return scm_i_exact_rational_round_quotient (x, y);
+      else
+       SCM_WTA_DISPATCH_2 (g_scm_round_quotient, x, y, SCM_ARG2,
+                           s_scm_round_quotient);
+    }
+  else
+    SCM_WTA_DISPATCH_2 (g_scm_round_quotient, x, y, SCM_ARG1,
+                       s_scm_round_quotient);
+}
+#undef FUNC_NAME
+
+static SCM
+scm_i_inexact_round_quotient (double x, double y)
+{
+  if (SCM_UNLIKELY (y == 0))
+    scm_num_overflow (s_scm_round_quotient);  /* or return a NaN? */
+  else
+    return scm_from_double (scm_c_round (x / y));
+}
+
+/* Assumes that both x and y are bigints, though
+   x might be able to fit into a fixnum. */
+static SCM
+scm_i_bigint_round_quotient (SCM x, SCM y)
+{
+  SCM q, r, r2;
+  int cmp, needs_adjustment;
+
+  /* Note that x might be small enough to fit into a
+     fixnum, so we must not let it escape into the wild */
+  q = scm_i_mkbig ();
+  r = scm_i_mkbig ();
+  r2 = scm_i_mkbig ();
+
+  mpz_fdiv_qr (SCM_I_BIG_MPZ (q), SCM_I_BIG_MPZ (r),
+              SCM_I_BIG_MPZ (x), SCM_I_BIG_MPZ (y));
+  mpz_mul_2exp (SCM_I_BIG_MPZ (r2), SCM_I_BIG_MPZ (r), 1);  /* r2 = 2*r */
+  scm_remember_upto_here_2 (x, r);
+
+  cmp = mpz_cmpabs (SCM_I_BIG_MPZ (r2), SCM_I_BIG_MPZ (y));
+  if (mpz_odd_p (SCM_I_BIG_MPZ (q)))
+    needs_adjustment = (cmp >= 0);
+  else
+    needs_adjustment = (cmp > 0);
+  scm_remember_upto_here_2 (r2, y);
+
+  if (needs_adjustment)
+    mpz_add_ui (SCM_I_BIG_MPZ (q), SCM_I_BIG_MPZ (q), 1);
+
+  return scm_i_normbig (q);
+}
+
+static SCM
+scm_i_exact_rational_round_quotient (SCM x, SCM y)
+{
+  return scm_round_quotient
+    (scm_product (scm_numerator (x), scm_denominator (y)),
+     scm_product (scm_numerator (y), scm_denominator (x)));
+}
+
+static SCM scm_i_inexact_round_remainder (double x, double y);
+static SCM scm_i_bigint_round_remainder (SCM x, SCM y);
+static SCM scm_i_exact_rational_round_remainder (SCM x, SCM y);
+
+SCM_PRIMITIVE_GENERIC (scm_round_remainder, "round-remainder", 2, 0, 0,
+                      (SCM x, SCM y),
+                      "Return the real number @var{r} such that\n"
+                      "@address@hidden = @address@hidden + @var{r}}, where\n"
+                      "@var{q} is @address@hidden / @var{y}} rounded to the\n"
+                      "nearest integer, with ties going to the nearest\n"
+                      "even integer.\n"
+                      "@lisp\n"
+                      "(round-remainder 123 10) @result{} 3\n"
+                      "(round-remainder 123 -10) @result{} 3\n"
+                      "(round-remainder -123 10) @result{} -3\n"
+                      "(round-remainder -123 -10) @result{} -3\n"
+                      "(round-remainder 125 10) @result{} 5\n"
+                      "(round-remainder 127 10) @result{} -3\n"
+                      "(round-remainder 135 10) @result{} -5\n"
+                      "(round-remainder -123.2 -63.5) @result{} 3.8\n"
+                      "(round-remainder 16/3 -10/7) @result{} -8/21\n"
+                      "@end lisp")
+#define FUNC_NAME s_scm_round_remainder
+{
+  if (SCM_LIKELY (SCM_I_INUMP (x)))
+    {
+      scm_t_inum xx = SCM_I_INUM (x);
+      if (SCM_LIKELY (SCM_I_INUMP (y)))
+       {
+         scm_t_inum yy = SCM_I_INUM (y);
+         if (SCM_UNLIKELY (yy == 0))
+           scm_num_overflow (s_scm_round_remainder);
+         else
+           {
+             scm_t_inum qq = xx / yy;
+             scm_t_inum rr = xx % yy;
+             scm_t_inum ay = yy;
+             scm_t_inum r2 = 2 * rr;
+
+             if (SCM_LIKELY (yy < 0))
+               {
+                 ay = -ay;
+                 r2 = -r2;
+               }
+
+             if (qq & 1L)
+               {
+                 if (r2 >= ay)
+                   rr -= yy;
+                 else if (r2 <= -ay)
+                   rr += yy;
+               }
+             else
+               {
+                 if (r2 > ay)
+                   rr -= yy;
+                 else if (r2 < -ay)
+                   rr += yy;
                }
              return SCM_I_MAKINUM (rr);
            }
@@ -1887,16 +3438,17 @@ SCM_PRIMITIVE_GENERIC (scm_centered_remainder, 
"centered-remainder", 2, 0, 0,
       else if (SCM_BIGP (y))
        {
          /* Pass a denormalized bignum version of x (even though it
-            can fit in a fixnum) to scm_i_bigint_centered_remainder */
-         return scm_i_bigint_centered_remainder (scm_i_long2big (xx), y);
+            can fit in a fixnum) to scm_i_bigint_round_remainder */
+         return scm_i_bigint_round_remainder
+           (scm_i_long2big (xx), y);
        }
       else if (SCM_REALP (y))
-       return scm_i_inexact_centered_remainder (xx, SCM_REAL_VALUE (y));
+       return scm_i_inexact_round_remainder (xx, SCM_REAL_VALUE (y));
       else if (SCM_FRACTIONP (y))
-       return scm_i_slow_exact_centered_remainder (x, y);
+       return scm_i_exact_rational_round_remainder (x, y);
       else
-       SCM_WTA_DISPATCH_2 (g_scm_centered_remainder, x, y, SCM_ARG2,
-                           s_scm_centered_remainder);
+       SCM_WTA_DISPATCH_2 (g_scm_round_remainder, x, y, SCM_ARG2,
+                           s_scm_round_remainder);
     }
   else if (SCM_BIGP (x))
     {
@@ -1904,172 +3456,174 @@ SCM_PRIMITIVE_GENERIC (scm_centered_remainder, 
"centered-remainder", 2, 0, 0,
        {
          scm_t_inum yy = SCM_I_INUM (y);
          if (SCM_UNLIKELY (yy == 0))
-           scm_num_overflow (s_scm_centered_remainder);
+           scm_num_overflow (s_scm_round_remainder);
          else
            {
+             SCM q = scm_i_mkbig ();
              scm_t_inum rr;
-             /* Arrange for rr to initially be non-positive,
-                because that simplifies the test to see
-                if it is within the needed bounds. */
+             int needs_adjustment;
+
              if (yy > 0)
                {
-                 rr = - mpz_cdiv_ui (SCM_I_BIG_MPZ (x), yy);
-                 scm_remember_upto_here_1 (x);
-                 if (rr < -yy / 2)
-                   rr += yy;
+                 rr = mpz_fdiv_q_ui (SCM_I_BIG_MPZ (q),
+                                     SCM_I_BIG_MPZ (x), yy);
+                 if (mpz_odd_p (SCM_I_BIG_MPZ (q)))
+                   needs_adjustment = (2*rr >= yy);
+                 else
+                   needs_adjustment = (2*rr > yy);
                }
              else
                {
-                 rr = - mpz_cdiv_ui (SCM_I_BIG_MPZ (x), -yy);
-                 scm_remember_upto_here_1 (x);
-                 if (rr < yy / 2)
-                   rr -= yy;
+                 rr = - mpz_cdiv_q_ui (SCM_I_BIG_MPZ (q),
+                                       SCM_I_BIG_MPZ (x), -yy);
+                 if (mpz_odd_p (SCM_I_BIG_MPZ (q)))
+                   needs_adjustment = (2*rr <= yy);
+                 else
+                   needs_adjustment = (2*rr < yy);
                }
+             scm_remember_upto_here_2 (x, q);
+             if (needs_adjustment)
+               rr -= yy;
              return SCM_I_MAKINUM (rr);
            }
        }
       else if (SCM_BIGP (y))
-       return scm_i_bigint_centered_remainder (x, y);
+       return scm_i_bigint_round_remainder (x, y);
       else if (SCM_REALP (y))
-       return scm_i_inexact_centered_remainder
+       return scm_i_inexact_round_remainder
          (scm_i_big2dbl (x), SCM_REAL_VALUE (y));
       else if (SCM_FRACTIONP (y))
-       return scm_i_slow_exact_centered_remainder (x, y);
+       return scm_i_exact_rational_round_remainder (x, y);
       else
-       SCM_WTA_DISPATCH_2 (g_scm_centered_remainder, x, y, SCM_ARG2,
-                           s_scm_centered_remainder);
+       SCM_WTA_DISPATCH_2 (g_scm_round_remainder, x, y, SCM_ARG2,
+                           s_scm_round_remainder);
     }
   else if (SCM_REALP (x))
     {
       if (SCM_REALP (y) || SCM_I_INUMP (y) ||
          SCM_BIGP (y) || SCM_FRACTIONP (y))
-       return scm_i_inexact_centered_remainder
+       return scm_i_inexact_round_remainder
          (SCM_REAL_VALUE (x), scm_to_double (y));
       else
-       SCM_WTA_DISPATCH_2 (g_scm_centered_remainder, x, y, SCM_ARG2,
-                           s_scm_centered_remainder);
+       SCM_WTA_DISPATCH_2 (g_scm_round_remainder, x, y, SCM_ARG2,
+                           s_scm_round_remainder);
     }
   else if (SCM_FRACTIONP (x))
     {
       if (SCM_REALP (y))
-       return scm_i_inexact_centered_remainder
+       return scm_i_inexact_round_remainder
          (scm_i_fraction2double (x), SCM_REAL_VALUE (y));
+      else if (SCM_I_INUMP (y) || SCM_BIGP (y) || SCM_FRACTIONP (y))
+       return scm_i_exact_rational_round_remainder (x, y);
       else
-       return scm_i_slow_exact_centered_remainder (x, y);
+       SCM_WTA_DISPATCH_2 (g_scm_round_remainder, x, y, SCM_ARG2,
+                           s_scm_round_remainder);
     }
   else
-    SCM_WTA_DISPATCH_2 (g_scm_centered_remainder, x, y, SCM_ARG1,
-                       s_scm_centered_remainder);
+    SCM_WTA_DISPATCH_2 (g_scm_round_remainder, x, y, SCM_ARG1,
+                       s_scm_round_remainder);
 }
 #undef FUNC_NAME
 
 static SCM
-scm_i_inexact_centered_remainder (double x, double y)
+scm_i_inexact_round_remainder (double x, double y)
 {
-  double q;
-
   /* Although it would be more efficient to use fmod here, we can't
      because it would in some cases produce results inconsistent with
-     scm_i_inexact_centered_quotient, such that x != r + q * y (not even
+     scm_i_inexact_round_quotient, such that x != r + q * y (not even
      close).  In particular, when x-y/2 is very close to a multiple of
-     y, then r might be either -abs(y/2) or abs(y/2)-epsilon, but those
-     two cases must correspond to different choices of q.  If quotient
+     y, then r might be either -abs(y/2) or abs(y/2), but those two
+     cases must correspond to different choices of q.  If quotient
      chooses one and remainder chooses the other, it would be bad. */
-  if (SCM_LIKELY (y > 0))
-    q = floor (x/y + 0.5);
-  else if (SCM_LIKELY (y < 0))
-    q = ceil (x/y - 0.5);
-  else if (y == 0)
-    scm_num_overflow (s_scm_centered_remainder);  /* or return a NaN? */
+
+  if (SCM_UNLIKELY (y == 0))
+    scm_num_overflow (s_scm_round_remainder);  /* or return a NaN? */
   else
-    return scm_nan ();
-  return scm_from_double (x - q * y);
+    {
+      double q = scm_c_round (x / y);
+      return scm_from_double (x - q * y);
+    }
 }
 
 /* Assumes that both x and y are bigints, though
    x might be able to fit into a fixnum. */
 static SCM
-scm_i_bigint_centered_remainder (SCM x, SCM y)
+scm_i_bigint_round_remainder (SCM x, SCM y)
 {
-  SCM r, min_r;
+  SCM q, r, r2;
+  int cmp, needs_adjustment;
 
   /* Note that x might be small enough to fit into a
      fixnum, so we must not let it escape into the wild */
+  q = scm_i_mkbig ();
   r = scm_i_mkbig ();
+  r2 = scm_i_mkbig ();
 
-  /* min_r will eventually become -abs(y)/2 */
-  min_r = scm_i_mkbig ();
-  mpz_tdiv_q_2exp (SCM_I_BIG_MPZ (min_r),
-                  SCM_I_BIG_MPZ (y), 1);
+  mpz_fdiv_qr (SCM_I_BIG_MPZ (q), SCM_I_BIG_MPZ (r),
+              SCM_I_BIG_MPZ (x), SCM_I_BIG_MPZ (y));
+  scm_remember_upto_here_1 (x);
+  mpz_mul_2exp (SCM_I_BIG_MPZ (r2), SCM_I_BIG_MPZ (r), 1);  /* r2 = 2*r */
 
-  /* Arrange for rr to initially be non-positive,
-     because that simplifies the test to see
-     if it is within the needed bounds. */
-  if (mpz_sgn (SCM_I_BIG_MPZ (y)) > 0)
-    {
-      mpz_cdiv_r (SCM_I_BIG_MPZ (r),
-                 SCM_I_BIG_MPZ (x), SCM_I_BIG_MPZ (y));
-      mpz_neg (SCM_I_BIG_MPZ (min_r), SCM_I_BIG_MPZ (min_r));
-      if (mpz_cmp (SCM_I_BIG_MPZ (r), SCM_I_BIG_MPZ (min_r)) < 0)
-       mpz_add (SCM_I_BIG_MPZ (r),
-                SCM_I_BIG_MPZ (r),
-                SCM_I_BIG_MPZ (y));
-    }
+  cmp = mpz_cmpabs (SCM_I_BIG_MPZ (r2), SCM_I_BIG_MPZ (y));
+  if (mpz_odd_p (SCM_I_BIG_MPZ (q)))
+    needs_adjustment = (cmp >= 0);
   else
-    {
-      mpz_fdiv_r (SCM_I_BIG_MPZ (r),
-                 SCM_I_BIG_MPZ (x), SCM_I_BIG_MPZ (y));
-      if (mpz_cmp (SCM_I_BIG_MPZ (r), SCM_I_BIG_MPZ (min_r)) < 0)
-       mpz_sub (SCM_I_BIG_MPZ (r),
-                SCM_I_BIG_MPZ (r),
-                SCM_I_BIG_MPZ (y));
-    }
-  scm_remember_upto_here_2 (x, y);
+    needs_adjustment = (cmp > 0);
+  scm_remember_upto_here_2 (q, r2);
+
+  if (needs_adjustment)
+    mpz_sub (SCM_I_BIG_MPZ (r), SCM_I_BIG_MPZ (r), SCM_I_BIG_MPZ (y));
+
+  scm_remember_upto_here_1 (y);
   return scm_i_normbig (r);
 }
 
-/* Compute exact centered_remainder the slow way.
-   We use this only if both arguments are exact,
-   and at least one of them is a fraction */
 static SCM
-scm_i_slow_exact_centered_remainder (SCM x, SCM y)
+scm_i_exact_rational_round_remainder (SCM x, SCM y)
 {
-  SCM q;
-
-  if (!(SCM_I_INUMP (x) || SCM_BIGP (x) || SCM_FRACTIONP (x)))
-    SCM_WTA_DISPATCH_2 (g_scm_centered_remainder, x, y, SCM_ARG1,
-                       s_scm_centered_remainder);
-  else if (!(SCM_I_INUMP (y) || SCM_BIGP (y) || SCM_FRACTIONP (y)))
-    SCM_WTA_DISPATCH_2 (g_scm_centered_remainder, x, y, SCM_ARG2,
-                       s_scm_centered_remainder);
-  else if (scm_is_true (scm_positive_p (y)))
-    q = scm_floor (scm_sum (scm_divide (x, y), exactly_one_half));
-  else if (scm_is_true (scm_negative_p (y)))
-    q = scm_ceiling (scm_difference (scm_divide (x, y), exactly_one_half));
-  else
-    scm_num_overflow (s_scm_centered_remainder);
-  return scm_difference (x, scm_product (y, q));
+  SCM xd = scm_denominator (x);
+  SCM yd = scm_denominator (y);
+  SCM r1 = scm_round_remainder (scm_product (scm_numerator (x), yd),
+                               scm_product (scm_numerator (y), xd));
+  return scm_divide (r1, scm_product (xd, yd));
 }
 
 
-static SCM scm_i_inexact_centered_divide (double x, double y);
-static SCM scm_i_bigint_centered_divide (SCM x, SCM y);
-static SCM scm_i_slow_exact_centered_divide (SCM x, SCM y);
+static void scm_i_inexact_round_divide (double x, double y, SCM *qp, SCM *rp);
+static void scm_i_bigint_round_divide (SCM x, SCM y, SCM *qp, SCM *rp);
+static void scm_i_exact_rational_round_divide (SCM x, SCM y, SCM *qp, SCM *rp);
 
-SCM_PRIMITIVE_GENERIC (scm_centered_divide, "centered/", 2, 0, 0,
+SCM_PRIMITIVE_GENERIC (scm_i_round_divide, "round/", 2, 0, 0,
                       (SCM x, SCM y),
                       "Return the integer @var{q} and the real number 
@var{r}\n"
                       "such that @address@hidden = @address@hidden + 
@var{r}}\n"
-                      "and @math{-abs(@var{y}/2) <= @var{r} < 
abs(@var{y}/2)}.\n"
+                      "and @var{q} is @address@hidden / @var{y}} rounded to 
the\n"
+                      "nearest integer, with ties going to the nearest even 
integer.\n"
                       "@lisp\n"
-                      "(centered/ 123 10) @result{} 12 and 3\n"
-                      "(centered/ 123 -10) @result{} -12 and 3\n"
-                      "(centered/ -123 10) @result{} -12 and -3\n"
-                      "(centered/ -123 -10) @result{} 12 and -3\n"
-                      "(centered/ -123.2 -63.5) @result{} 2.0 and 3.8\n"
-                      "(centered/ 16/3 -10/7) @result{} -4 and -8/21\n"
+                      "(round/ 123 10) @result{} 12 and 3\n"
+                      "(round/ 123 -10) @result{} -12 and 3\n"
+                      "(round/ -123 10) @result{} -12 and -3\n"
+                      "(round/ -123 -10) @result{} 12 and -3\n"
+                      "(round/ 125 10) @result{} 12 and 5\n"
+                      "(round/ 127 10) @result{} 13 and -3\n"
+                      "(round/ 135 10) @result{} 14 and -5\n"
+                      "(round/ -123.2 -63.5) @result{} 2.0 and 3.8\n"
+                      "(round/ 16/3 -10/7) @result{} -4 and -8/21\n"
                       "@end lisp")
-#define FUNC_NAME s_scm_centered_divide
+#define FUNC_NAME s_scm_i_round_divide
+{
+  SCM q, r;
+
+  scm_round_divide(x, y, &q, &r);
+  return scm_values (scm_list_2 (q, r));
+}
+#undef FUNC_NAME
+
+#define s_scm_round_divide s_scm_i_round_divide
+#define g_scm_round_divide g_scm_i_round_divide
+
+void
+scm_round_divide (SCM x, SCM y, SCM *qp, SCM *rp)
 {
   if (SCM_LIKELY (SCM_I_INUMP (x)))
     {
@@ -2078,59 +3632,56 @@ SCM_PRIMITIVE_GENERIC (scm_centered_divide, 
"centered/", 2, 0, 0,
        {
          scm_t_inum yy = SCM_I_INUM (y);
          if (SCM_UNLIKELY (yy == 0))
-           scm_num_overflow (s_scm_centered_divide);
+           scm_num_overflow (s_scm_round_divide);
          else
            {
              scm_t_inum qq = xx / yy;
              scm_t_inum rr = xx % yy;
-             SCM q;
+             scm_t_inum ay = yy;
+             scm_t_inum r2 = 2 * rr;
 
-             if (SCM_LIKELY (xx > 0))
+             if (SCM_LIKELY (yy < 0))
                {
-                 if (SCM_LIKELY (yy > 0))
-                   {
-                     if (rr >= (yy + 1) / 2)
-                       { qq++; rr -= yy; }
-                   }
-                 else
-                   {
-                     if (rr >= (1 - yy) / 2)
-                       { qq--; rr += yy; }
-                   }
+                 ay = -ay;
+                 r2 = -r2;
+               }
+
+             if (qq & 1L)
+               {
+                 if (r2 >= ay)
+                   { qq++; rr -= yy; }
+                 else if (r2 <= -ay)
+                   { qq--; rr += yy; }
                }
              else
                {
-                 if (SCM_LIKELY (yy > 0))
-                   {
-                     if (rr < -yy / 2)
-                       { qq--; rr += yy; }
-                   }
-                 else
-                   {
-                     if (rr < yy / 2)
-                       { qq++; rr -= yy; }
-                   }
+                 if (r2 > ay)
+                   { qq++; rr -= yy; }
+                 else if (r2 < -ay)
+                   { qq--; rr += yy; }
                }
              if (SCM_LIKELY (SCM_FIXABLE (qq)))
-               q = SCM_I_MAKINUM (qq);
+               *qp = SCM_I_MAKINUM (qq);
              else
-               q = scm_i_inum2big (qq);
-             return scm_values (scm_list_2 (q, SCM_I_MAKINUM (rr)));
+               *qp = scm_i_inum2big (qq);
+             *rp = SCM_I_MAKINUM (rr);
            }
+         return;
        }
       else if (SCM_BIGP (y))
        {
          /* Pass a denormalized bignum version of x (even though it
-            can fit in a fixnum) to scm_i_bigint_centered_divide */
-         return scm_i_bigint_centered_divide (scm_i_long2big (xx), y);
+            can fit in a fixnum) to scm_i_bigint_round_divide */
+         return scm_i_bigint_round_divide
+           (scm_i_long2big (SCM_I_INUM (x)), y, qp, rp);
        }
       else if (SCM_REALP (y))
-       return scm_i_inexact_centered_divide (xx, SCM_REAL_VALUE (y));
+       return scm_i_inexact_round_divide (xx, SCM_REAL_VALUE (y), qp, rp);
       else if (SCM_FRACTIONP (y))
-       return scm_i_slow_exact_centered_divide (x, y);
+       return scm_i_exact_rational_round_divide (x, y, qp, rp);
       else
-       SCM_WTA_DISPATCH_2 (g_scm_centered_divide, x, y, SCM_ARG2,
-                           s_scm_centered_divide);
+       return two_valued_wta_dispatch_2 (g_scm_round_divide, x, y, SCM_ARG2,
+                                         s_scm_round_divide, qp, rp);
     }
   else if (SCM_BIGP (x))
     {
@@ -2138,172 +3689,141 @@ SCM_PRIMITIVE_GENERIC (scm_centered_divide, 
"centered/", 2, 0, 0,
        {
          scm_t_inum yy = SCM_I_INUM (y);
          if (SCM_UNLIKELY (yy == 0))
-           scm_num_overflow (s_scm_centered_divide);
+           scm_num_overflow (s_scm_round_divide);
          else
            {
              SCM q = scm_i_mkbig ();
              scm_t_inum rr;
-             /* Arrange for rr to initially be non-positive,
-                because that simplifies the test to see
-                if it is within the needed bounds. */
+             int needs_adjustment;
+
              if (yy > 0)
                {
-                 rr = - mpz_cdiv_q_ui (SCM_I_BIG_MPZ (q),
-                                       SCM_I_BIG_MPZ (x), yy);
-                 scm_remember_upto_here_1 (x);
-                 if (rr < -yy / 2)
-                   {
-                     mpz_sub_ui (SCM_I_BIG_MPZ (q),
-                                 SCM_I_BIG_MPZ (q), 1);
-                     rr += yy;
-                   }
+                 rr = mpz_fdiv_q_ui (SCM_I_BIG_MPZ (q),
+                                     SCM_I_BIG_MPZ (x), yy);
+                 if (mpz_odd_p (SCM_I_BIG_MPZ (q)))
+                   needs_adjustment = (2*rr >= yy);
+                 else
+                   needs_adjustment = (2*rr > yy);
                }
              else
                {
                  rr = - mpz_cdiv_q_ui (SCM_I_BIG_MPZ (q),
                                        SCM_I_BIG_MPZ (x), -yy);
-                 scm_remember_upto_here_1 (x);
                  mpz_neg (SCM_I_BIG_MPZ (q), SCM_I_BIG_MPZ (q));
-                 if (rr < yy / 2)
-                   {
-                     mpz_add_ui (SCM_I_BIG_MPZ (q),
-                                 SCM_I_BIG_MPZ (q), 1);
-                     rr -= yy;
-                   }
+                 if (mpz_odd_p (SCM_I_BIG_MPZ (q)))
+                   needs_adjustment = (2*rr <= yy);
+                 else
+                   needs_adjustment = (2*rr < yy);
                }
-             return scm_values (scm_list_2 (scm_i_normbig (q),
-                                            SCM_I_MAKINUM (rr)));
+             scm_remember_upto_here_1 (x);
+             if (needs_adjustment)
+               {
+                 mpz_add_ui (SCM_I_BIG_MPZ (q), SCM_I_BIG_MPZ (q), 1);
+                 rr -= yy;
+               }
+             *qp = scm_i_normbig (q);
+             *rp = SCM_I_MAKINUM (rr);
            }
+         return;
        }
       else if (SCM_BIGP (y))
-       return scm_i_bigint_centered_divide (x, y);
+       return scm_i_bigint_round_divide (x, y, qp, rp);
       else if (SCM_REALP (y))
-       return scm_i_inexact_centered_divide
-         (scm_i_big2dbl (x), SCM_REAL_VALUE (y));
+       return scm_i_inexact_round_divide
+         (scm_i_big2dbl (x), SCM_REAL_VALUE (y), qp, rp);
       else if (SCM_FRACTIONP (y))
-       return scm_i_slow_exact_centered_divide (x, y);
+       return scm_i_exact_rational_round_divide (x, y, qp, rp);
       else
-       SCM_WTA_DISPATCH_2 (g_scm_centered_divide, x, y, SCM_ARG2,
-                           s_scm_centered_divide);
+       return two_valued_wta_dispatch_2 (g_scm_round_divide, x, y, SCM_ARG2,
+                                         s_scm_round_divide, qp, rp);
     }
   else if (SCM_REALP (x))
     {
       if (SCM_REALP (y) || SCM_I_INUMP (y) ||
          SCM_BIGP (y) || SCM_FRACTIONP (y))
-       return scm_i_inexact_centered_divide
-         (SCM_REAL_VALUE (x), scm_to_double (y));
-     else
-       SCM_WTA_DISPATCH_2 (g_scm_centered_divide, x, y, SCM_ARG2,
-                           s_scm_centered_divide);
+       return scm_i_inexact_round_divide
+         (SCM_REAL_VALUE (x), scm_to_double (y), qp, rp);
+      else
+       return two_valued_wta_dispatch_2 (g_scm_round_divide, x, y, SCM_ARG2,
+                                         s_scm_round_divide, qp, rp);
     }
   else if (SCM_FRACTIONP (x))
     {
       if (SCM_REALP (y))
-       return scm_i_inexact_centered_divide
-         (scm_i_fraction2double (x), SCM_REAL_VALUE (y));
+       return scm_i_inexact_round_divide
+         (scm_i_fraction2double (x), SCM_REAL_VALUE (y), qp, rp);
+      else if (SCM_I_INUMP (y) || SCM_BIGP (y) || SCM_FRACTIONP (y))
+       return scm_i_exact_rational_round_divide (x, y, qp, rp);
       else
-       return scm_i_slow_exact_centered_divide (x, y);
+       return two_valued_wta_dispatch_2 (g_scm_round_divide, x, y, SCM_ARG2,
+                                         s_scm_round_divide, qp, rp);
     }
   else
-    SCM_WTA_DISPATCH_2 (g_scm_centered_divide, x, y, SCM_ARG1,
-                       s_scm_centered_divide);
+    return two_valued_wta_dispatch_2 (g_scm_round_divide, x, y, SCM_ARG1,
+                                     s_scm_round_divide, qp, rp);
 }
-#undef FUNC_NAME
 
-static SCM
-scm_i_inexact_centered_divide (double x, double y)
+static void
+scm_i_inexact_round_divide (double x, double y, SCM *qp, SCM *rp)
 {
-  double q, r;
-
-  if (SCM_LIKELY (y > 0))
-    q = floor (x/y + 0.5);
-  else if (SCM_LIKELY (y < 0))
-    q = ceil (x/y - 0.5);
-  else if (y == 0)
-    scm_num_overflow (s_scm_centered_divide);  /* or return a NaN? */
+  if (SCM_UNLIKELY (y == 0))
+    scm_num_overflow (s_scm_round_divide);  /* or return a NaN? */
   else
-    q = guile_NaN;
-  r = x - q * y;
-  return scm_values (scm_list_2 (scm_from_double (q),
-                                scm_from_double (r)));
+    {
+      double q = scm_c_round (x / y);
+      double r = x - q * y;
+      *qp = scm_from_double (q);
+      *rp = scm_from_double (r);
+    }
 }
 
 /* Assumes that both x and y are bigints, though
    x might be able to fit into a fixnum. */
-static SCM
-scm_i_bigint_centered_divide (SCM x, SCM y)
+static void
+scm_i_bigint_round_divide (SCM x, SCM y, SCM *qp, SCM *rp)
 {
-  SCM q, r, min_r;
+  SCM q, r, r2;
+  int cmp, needs_adjustment;
 
   /* Note that x might be small enough to fit into a
      fixnum, so we must not let it escape into the wild */
   q = scm_i_mkbig ();
   r = scm_i_mkbig ();
+  r2 = scm_i_mkbig ();
 
-  /* min_r will eventually become -abs(y/2) */
-  min_r = scm_i_mkbig ();
-  mpz_tdiv_q_2exp (SCM_I_BIG_MPZ (min_r),
-                  SCM_I_BIG_MPZ (y), 1);
+  mpz_fdiv_qr (SCM_I_BIG_MPZ (q), SCM_I_BIG_MPZ (r),
+              SCM_I_BIG_MPZ (x), SCM_I_BIG_MPZ (y));
+  scm_remember_upto_here_1 (x);
+  mpz_mul_2exp (SCM_I_BIG_MPZ (r2), SCM_I_BIG_MPZ (r), 1);  /* r2 = 2*r */
 
-  /* Arrange for rr to initially be non-positive,
-     because that simplifies the test to see
-     if it is within the needed bounds. */
-  if (mpz_sgn (SCM_I_BIG_MPZ (y)) > 0)
-    {
-      mpz_cdiv_qr (SCM_I_BIG_MPZ (q), SCM_I_BIG_MPZ (r),
-                  SCM_I_BIG_MPZ (x), SCM_I_BIG_MPZ (y));
-      mpz_neg (SCM_I_BIG_MPZ (min_r), SCM_I_BIG_MPZ (min_r));
-      if (mpz_cmp (SCM_I_BIG_MPZ (r), SCM_I_BIG_MPZ (min_r)) < 0)
-       {
-         mpz_sub_ui (SCM_I_BIG_MPZ (q),
-                     SCM_I_BIG_MPZ (q), 1);
-         mpz_add (SCM_I_BIG_MPZ (r),
-                  SCM_I_BIG_MPZ (r),
-                  SCM_I_BIG_MPZ (y));
-       }
-    }
+  cmp = mpz_cmpabs (SCM_I_BIG_MPZ (r2), SCM_I_BIG_MPZ (y));
+  if (mpz_odd_p (SCM_I_BIG_MPZ (q)))
+    needs_adjustment = (cmp >= 0);
   else
+    needs_adjustment = (cmp > 0);
+
+  if (needs_adjustment)
     {
-      mpz_fdiv_qr (SCM_I_BIG_MPZ (q), SCM_I_BIG_MPZ (r),
-                  SCM_I_BIG_MPZ (x), SCM_I_BIG_MPZ (y));
-      if (mpz_cmp (SCM_I_BIG_MPZ (r), SCM_I_BIG_MPZ (min_r)) < 0)
-       {
-         mpz_add_ui (SCM_I_BIG_MPZ (q),
-                     SCM_I_BIG_MPZ (q), 1);
-         mpz_sub (SCM_I_BIG_MPZ (r),
-                  SCM_I_BIG_MPZ (r),
-                  SCM_I_BIG_MPZ (y));
-       }
+      mpz_add_ui (SCM_I_BIG_MPZ (q), SCM_I_BIG_MPZ (q), 1);
+      mpz_sub (SCM_I_BIG_MPZ (r), SCM_I_BIG_MPZ (r), SCM_I_BIG_MPZ (y));
     }
-  scm_remember_upto_here_2 (x, y);
-  return scm_values (scm_list_2 (scm_i_normbig (q),
-                                scm_i_normbig (r)));
+
+  scm_remember_upto_here_2 (r2, y);
+  *qp = scm_i_normbig (q);
+  *rp = scm_i_normbig (r);
 }
 
-/* Compute exact centered quotient and remainder the slow way.
-   We use this only if both arguments are exact,
-   and at least one of them is a fraction */
-static SCM
-scm_i_slow_exact_centered_divide (SCM x, SCM y)
+static void
+scm_i_exact_rational_round_divide (SCM x, SCM y, SCM *qp, SCM *rp)
 {
-  SCM q, r;
+  SCM r1;
+  SCM xd = scm_denominator (x);
+  SCM yd = scm_denominator (y);
 
-  if (!(SCM_I_INUMP (x) || SCM_BIGP (x) || SCM_FRACTIONP (x)))
-    SCM_WTA_DISPATCH_2 (g_scm_centered_divide, x, y, SCM_ARG1,
-                       s_scm_centered_divide);
-  else if (!(SCM_I_INUMP (y) || SCM_BIGP (y) || SCM_FRACTIONP (y)))
-    SCM_WTA_DISPATCH_2 (g_scm_centered_divide, x, y, SCM_ARG2,
-                       s_scm_centered_divide);
-  else if (scm_is_true (scm_positive_p (y)))
-    q = scm_floor (scm_sum (scm_divide (x, y),
-                           exactly_one_half));
-  else if (scm_is_true (scm_negative_p (y)))
-    q = scm_ceiling (scm_difference (scm_divide (x, y),
-                                    exactly_one_half));
-  else
-    scm_num_overflow (s_scm_centered_divide);
-  r = scm_difference (x, scm_product (q, y));
-  return scm_values (scm_list_2 (q, r));
+  scm_round_divide (scm_product (scm_numerator (x), yd),
+                   scm_product (scm_numerator (y), xd),
+                   qp, &r1);
+  *rp = scm_divide (r1, scm_product (xd, yd));
 }
 
 
@@ -6657,8 +8177,9 @@ scm_c_truncate (double x)
   return trunc (x);
 #else
   if (x < 0.0)
-    return -floor (-x);
-  return floor (x);
+    return ceil (x);
+  else
+    return floor (x);
 #endif
 }
 
@@ -6704,43 +8225,41 @@ scm_c_round (double x)
          : result);
 }
 
-SCM_DEFINE (scm_truncate_number, "truncate", 1, 0, 0,
-           (SCM x),
-           "Round the number @var{x} towards zero.")
+SCM_PRIMITIVE_GENERIC (scm_truncate_number, "truncate", 1, 0, 0,
+                      (SCM x),
+                      "Round the number @var{x} towards zero.")
 #define FUNC_NAME s_scm_truncate_number
 {
-  if (scm_is_false (scm_negative_p (x)))
-    return scm_floor (x);
+  if (SCM_I_INUMP (x) || SCM_BIGP (x))
+    return x;
+  else if (SCM_REALP (x))
+    return scm_from_double (scm_c_truncate (SCM_REAL_VALUE (x)));
+  else if (SCM_FRACTIONP (x))
+    return scm_truncate_quotient (SCM_FRACTION_NUMERATOR (x),
+                                 SCM_FRACTION_DENOMINATOR (x));
   else
-    return scm_ceiling (x);
+    SCM_WTA_DISPATCH_1 (g_scm_truncate_number, x, SCM_ARG1,
+                       s_scm_truncate_number);
 }
 #undef FUNC_NAME
 
-SCM_DEFINE (scm_round_number, "round", 1, 0, 0,
-           (SCM x),
-           "Round the number @var{x} towards the nearest integer. "
-           "When it is exactly halfway between two integers, "
-           "round towards the even one.")
+SCM_PRIMITIVE_GENERIC (scm_round_number, "round", 1, 0, 0,
+                      (SCM x),
+                      "Round the number @var{x} towards the nearest integer. "
+                      "When it is exactly halfway between two integers, "
+                      "round towards the even one.")
 #define FUNC_NAME s_scm_round_number
 {
   if (SCM_I_INUMP (x) || SCM_BIGP (x))
     return x;
   else if (SCM_REALP (x))
     return scm_from_double (scm_c_round (SCM_REAL_VALUE (x)));
+  else if (SCM_FRACTIONP (x))
+    return scm_round_quotient (SCM_FRACTION_NUMERATOR (x),
+                              SCM_FRACTION_DENOMINATOR (x));
   else
-    {
-      /* OPTIMIZE-ME: Fraction case could be done more efficiently by a
-         single quotient+remainder division then examining to see which way
-         the rounding should go.  */
-      SCM plus_half = scm_sum (x, exactly_one_half);
-      SCM result = scm_floor (plus_half);
-      /* Adjust so that the rounding is towards even.  */
-      if (scm_is_true (scm_num_eq_p (plus_half, result))
-          && scm_is_true (scm_odd_p (result)))
-        return scm_difference (result, SCM_INUM1);
-      else
-        return result;
-    }
+    SCM_WTA_DISPATCH_1 (g_scm_round_number, x, SCM_ARG1,
+                       s_scm_round_number);
 }
 #undef FUNC_NAME
 
@@ -6754,22 +8273,8 @@ SCM_PRIMITIVE_GENERIC (scm_floor, "floor", 1, 0, 0,
   else if (SCM_REALP (x))
     return scm_from_double (floor (SCM_REAL_VALUE (x)));
   else if (SCM_FRACTIONP (x))
-    {
-      SCM q = scm_quotient (SCM_FRACTION_NUMERATOR (x),
-                           SCM_FRACTION_DENOMINATOR (x));
-      if (scm_is_false (scm_negative_p (x)))
-       {
-         /* For positive x, rounding towards zero is correct. */
-         return q;
-       }
-      else
-       {
-         /* For negative x, we need to return q-1 unless x is an
-            integer.  But fractions are never integer, per our
-            assumptions. */
-         return scm_difference (q, SCM_INUM1);
-       }
-    }
+    return scm_floor_quotient (SCM_FRACTION_NUMERATOR (x),
+                              SCM_FRACTION_DENOMINATOR (x));
   else
     SCM_WTA_DISPATCH_1 (g_scm_floor, x, 1, s_scm_floor);
 }  
@@ -6785,22 +8290,8 @@ SCM_PRIMITIVE_GENERIC (scm_ceiling, "ceiling", 1, 0, 0,
   else if (SCM_REALP (x))
     return scm_from_double (ceil (SCM_REAL_VALUE (x)));
   else if (SCM_FRACTIONP (x))
-    {
-      SCM q = scm_quotient (SCM_FRACTION_NUMERATOR (x),
-                           SCM_FRACTION_DENOMINATOR (x));
-      if (scm_is_false (scm_positive_p (x)))
-       {
-         /* For negative x, rounding towards zero is correct. */
-         return q;
-       }
-      else
-       {
-         /* For positive x, we need to return q+1 unless x is an
-            integer.  But fractions are never integer, per our
-            assumptions. */
-         return scm_sum (q, SCM_INUM1);
-       }
-    }
+    return scm_ceiling_quotient (SCM_FRACTION_NUMERATOR (x),
+                                SCM_FRACTION_DENOMINATOR (x));
   else
     SCM_WTA_DISPATCH_1 (g_scm_ceiling, x, 1, s_scm_ceiling);
 }
diff --git a/libguile/numbers.h b/libguile/numbers.h
index 10a4f17..ab96981 100644
--- a/libguile/numbers.h
+++ b/libguile/numbers.h
@@ -138,9 +138,7 @@ typedef scm_t_int32 scm_t_wchar;
 
 #define SCM_NUMBERP(x) (SCM_I_INUMP(x) || SCM_NUMP(x))
 #define SCM_NUMP(x) (!SCM_IMP(x) \
-  && (((0xfcff & SCM_CELL_TYPE (x)) == scm_tc7_number) \
-      || ((0xfbff & SCM_CELL_TYPE (x)) == scm_tc7_number)))
-/* 0xfcff (#b1100) for 0 free, 1 big, 2 real, 3 complex, then 0xfbff (#b1011) 
for 4 fraction */
+                    && ((0x00ff & SCM_CELL_TYPE (x)) == scm_tc7_number))
 
 #define SCM_FRACTIONP(x) (!SCM_IMP (x) && SCM_TYP16 (x) == scm_tc16_fraction)
 #define SCM_FRACTION_NUMERATOR(x) (SCM_CELL_OBJECT_1 (x))
@@ -178,12 +176,24 @@ SCM_API SCM scm_abs (SCM x);
 SCM_API SCM scm_quotient (SCM x, SCM y);
 SCM_API SCM scm_remainder (SCM x, SCM y);
 SCM_API SCM scm_modulo (SCM x, SCM y);
-SCM_API SCM scm_euclidean_divide (SCM x, SCM y);
+SCM_API void scm_euclidean_divide (SCM x, SCM y, SCM *q, SCM *r);
 SCM_API SCM scm_euclidean_quotient (SCM x, SCM y);
 SCM_API SCM scm_euclidean_remainder (SCM x, SCM y);
-SCM_API SCM scm_centered_divide (SCM x, SCM y);
+SCM_API void scm_floor_divide (SCM x, SCM y, SCM *q, SCM *r);
+SCM_API SCM scm_floor_quotient (SCM x, SCM y);
+SCM_API SCM scm_floor_remainder (SCM x, SCM y);
+SCM_API void scm_ceiling_divide (SCM x, SCM y, SCM *q, SCM *r);
+SCM_API SCM scm_ceiling_quotient (SCM x, SCM y);
+SCM_API SCM scm_ceiling_remainder (SCM x, SCM y);
+SCM_API void scm_truncate_divide (SCM x, SCM y, SCM *q, SCM *r);
+SCM_API SCM scm_truncate_quotient (SCM x, SCM y);
+SCM_API SCM scm_truncate_remainder (SCM x, SCM y);
+SCM_API void scm_centered_divide (SCM x, SCM y, SCM *q, SCM *r);
 SCM_API SCM scm_centered_quotient (SCM x, SCM y);
 SCM_API SCM scm_centered_remainder (SCM x, SCM y);
+SCM_API void scm_round_divide (SCM x, SCM y, SCM *q, SCM *r);
+SCM_API SCM scm_round_quotient (SCM x, SCM y);
+SCM_API SCM scm_round_remainder (SCM x, SCM y);
 SCM_API SCM scm_gcd (SCM x, SCM y);
 SCM_API SCM scm_lcm (SCM n1, SCM n2);
 SCM_API SCM scm_logand (SCM n1, SCM n2);
@@ -199,6 +209,13 @@ SCM_API SCM scm_bit_extract (SCM n, SCM start, SCM end);
 SCM_API SCM scm_logcount (SCM n);
 SCM_API SCM scm_integer_length (SCM n);
 
+SCM_INTERNAL SCM scm_i_euclidean_divide (SCM x, SCM y);
+SCM_INTERNAL SCM scm_i_floor_divide (SCM x, SCM y);
+SCM_INTERNAL SCM scm_i_ceiling_divide (SCM x, SCM y);
+SCM_INTERNAL SCM scm_i_truncate_divide (SCM x, SCM y);
+SCM_INTERNAL SCM scm_i_centered_divide (SCM x, SCM y);
+SCM_INTERNAL SCM scm_i_round_divide (SCM x, SCM y);
+
 SCM_INTERNAL SCM scm_i_gcd (SCM x, SCM y, SCM rest);
 SCM_INTERNAL SCM scm_i_lcm (SCM x, SCM y, SCM rest);
 SCM_INTERNAL SCM scm_i_logand (SCM x, SCM y, SCM rest);
diff --git a/libguile/values.c b/libguile/values.c
index 8bbfc71..ab77731 100644
--- a/libguile/values.c
+++ b/libguile/values.c
@@ -35,6 +35,24 @@
 
 SCM scm_values_vtable;
 
+/* OBJ must be a values object containing exactly two values.
+   scm_i_extract_values_2 puts those two values into *p1 and *p2.  */
+void
+scm_i_extract_values_2 (SCM obj, SCM *p1, SCM *p2)
+{
+  SCM values;
+
+  SCM_ASSERT_TYPE (SCM_VALUESP (obj), obj, SCM_ARG1,
+                  "scm_i_extract_values_2", "values");
+  values = scm_struct_ref (obj, SCM_INUM0);
+  if (scm_ilength (values) != 2)
+    scm_wrong_type_arg_msg
+      ("scm_i_extract_values_2", SCM_ARG1, obj,
+       "a values object containing exactly two values");
+  *p1 = SCM_CAR (values);
+  *p2 = SCM_CADR (values);
+}
+
 static SCM
 print_values (SCM obj, SCM pwps)
 {
diff --git a/libguile/values.h b/libguile/values.h
index 0750aec..65ad8a1 100644
--- a/libguile/values.h
+++ b/libguile/values.h
@@ -30,6 +30,8 @@ SCM_API SCM scm_values_vtable;
 #define SCM_VALUESP(x) (SCM_STRUCTP (x)\
                         && scm_is_eq (scm_struct_vtable (x), 
scm_values_vtable))
 
+SCM_INTERNAL void scm_i_extract_values_2 (SCM obj, SCM *p1, SCM *p2);
+
 SCM_API SCM scm_values (SCM args);
 SCM_INTERNAL void scm_init_values (void);
 
diff --git a/module/oop/goops.scm b/module/oop/goops.scm
index 70bf375..2801aa2 100644
--- a/module/oop/goops.scm
+++ b/module/oop/goops.scm
@@ -391,7 +391,8 @@
                 #:default (procedure old-definition)
                 #:setter (setter old-definition)))
          ((procedure? old-definition)
-          (make <generic> #:name name #:default old-definition))
+           (if (generic-capability? old-definition) old-definition
+               (make <generic> #:name name #:default old-definition)))
          (else (make <generic> #:name name)))))
 
 ;; same semantics as <generic>
@@ -428,7 +429,10 @@
                 #:default (procedure proc)
                 #:setter (ensure-generic (setter proc) name)))
          ((procedure? proc)
-          (ensure-accessor (ensure-generic proc name) name))
+           (ensure-accessor (if (generic-capability? proc)
+                                (make <generic> #:name name #:default proc)
+                                (ensure-generic proc name))
+                            name))
          (else
           (make-accessor name)))))
 
diff --git a/test-suite/tests/numbers.test b/test-suite/tests/numbers.test
index f738189..1f2ee03 100644
--- a/test-suite/tests/numbers.test
+++ b/test-suite/tests/numbers.test
@@ -3307,6 +3307,284 @@
       (= (/ 25+125i 4+3i) 19.0+17.0i))))
 
 ;;;
+;;; floor
+;;;
+
+(with-test-prefix "floor"
+  (pass-if (=  1 (floor  1.75)))
+  (pass-if (=  1 (floor  1.5)))
+  (pass-if (=  1 (floor  1.25)))
+  (pass-if (=  0 (floor  0.75)))
+  (pass-if (=  0 (floor  0.5)))
+  (pass-if (=  0 (floor  0.0)))
+  (pass-if (= -1 (floor -0.5)))
+  (pass-if (= -2 (floor -1.25)))
+  (pass-if (= -2 (floor -1.5)))
+
+  (with-test-prefix "inum"
+    (pass-if "0"
+      (and (= 0    (floor 0))
+          (exact? (floor 0))))
+
+    (pass-if "1"
+      (and (= 1    (floor 1))
+          (exact? (floor 1))))
+
+    (pass-if "-1"
+      (and (= -1   (floor -1))
+          (exact? (floor -1)))))
+
+  (with-test-prefix "bignum"
+    (let ((x (1+ most-positive-fixnum)))
+      (pass-if "(1+ most-positive-fixnum)"
+       (and (= x    (floor x))
+            (exact? (floor x)))))
+
+    (let ((x (1- most-negative-fixnum)))
+      (pass-if "(1- most-negative-fixnum)"
+       (and (= x    (floor x))
+            (exact? (floor x))))))
+
+  (with-test-prefix "frac"
+    (define (=exact x y)
+      (and (= x y)
+          (exact? y)))
+
+    (pass-if (=exact -3 (floor -7/3)))
+    (pass-if (=exact -2 (floor -5/3)))
+    (pass-if (=exact -2 (floor -4/3)))
+    (pass-if (=exact -1 (floor -2/3)))
+    (pass-if (=exact -1 (floor -1/3)))
+    (pass-if (=exact  0 (floor  1/3)))
+    (pass-if (=exact  0 (floor  2/3)))
+    (pass-if (=exact  1 (floor  4/3)))
+    (pass-if (=exact  1 (floor  5/3)))
+    (pass-if (=exact  2 (floor  7/3)))
+
+    (pass-if (=exact -3 (floor -17/6)))
+    (pass-if (=exact -3 (floor -16/6)))
+    (pass-if (=exact -3 (floor -15/6)))
+    (pass-if (=exact -3 (floor -14/6)))
+    (pass-if (=exact -3 (floor -13/6)))
+    (pass-if (=exact -2 (floor -11/6)))
+    (pass-if (=exact -2 (floor -10/6)))
+    (pass-if (=exact -2 (floor  -9/6)))
+    (pass-if (=exact -2 (floor  -8/6)))
+    (pass-if (=exact -2 (floor  -7/6)))
+    (pass-if (=exact -1 (floor  -5/6)))
+    (pass-if (=exact -1 (floor  -4/6)))
+    (pass-if (=exact -1 (floor  -3/6)))
+    (pass-if (=exact -1 (floor  -2/6)))
+    (pass-if (=exact -1 (floor  -1/6)))
+    (pass-if (=exact  0 (floor   1/6)))
+    (pass-if (=exact  0 (floor   2/6)))
+    (pass-if (=exact  0 (floor   3/6)))
+    (pass-if (=exact  0 (floor   4/6)))
+    (pass-if (=exact  0 (floor   5/6)))
+    (pass-if (=exact  1 (floor   7/6)))
+    (pass-if (=exact  1 (floor   8/6)))
+    (pass-if (=exact  1 (floor   9/6)))
+    (pass-if (=exact  1 (floor  10/6)))
+    (pass-if (=exact  1 (floor  11/6)))
+    (pass-if (=exact  2 (floor  13/6)))
+    (pass-if (=exact  2 (floor  14/6)))
+    (pass-if (=exact  2 (floor  15/6)))
+    (pass-if (=exact  2 (floor  16/6)))
+    (pass-if (=exact  2 (floor  17/6))))
+
+  (with-test-prefix "real"
+    (pass-if "0.0"
+      (and (= 0.0    (floor 0.0))
+          (inexact? (floor 0.0))))
+
+    (pass-if "1.0"
+      (and (= 1.0    (floor 1.0))
+          (inexact? (floor 1.0))))
+
+    (pass-if "-1.0"
+      (and (= -1.0   (floor -1.0))
+          (inexact? (floor -1.0))))
+
+    (pass-if "-3.1"
+      (and (= -4.0   (floor -3.1))
+          (inexact? (floor -3.1))))
+
+    (pass-if "3.1"
+      (and (= 3.0    (floor 3.1))
+          (inexact? (floor 3.1))))
+
+    (pass-if "3.9"
+      (and (= 3.0    (floor 3.9))
+          (inexact? (floor 3.9))))
+
+    (pass-if "-3.9"
+      (and (= -4.0   (floor -3.9))
+          (inexact? (floor -3.9))))
+
+    (pass-if "1.5"
+      (and (= 1.0    (floor 1.5))
+          (inexact? (floor 1.5))))
+
+    (pass-if "2.5"
+      (and (= 2.0    (floor 2.5))
+          (inexact? (floor 2.5))))
+
+    (pass-if "3.5"
+      (and (= 3.0    (floor 3.5))
+          (inexact? (floor 3.5))))
+
+    (pass-if "-1.5"
+      (and (= -2.0   (floor -1.5))
+          (inexact? (floor -1.5))))
+
+    (pass-if "-2.5"
+      (and (= -3.0   (floor -2.5))
+          (inexact? (floor -2.5))))
+
+    (pass-if "-3.5"
+      (and (= -4.0   (floor -3.5))
+          (inexact? (floor -3.5))))))
+
+;;;
+;;; ceiling
+;;;
+
+(with-test-prefix "ceiling"
+  (pass-if (=  2 (ceiling  1.75)))
+  (pass-if (=  2 (ceiling  1.5)))
+  (pass-if (=  2 (ceiling  1.25)))
+  (pass-if (=  1 (ceiling  0.75)))
+  (pass-if (=  1 (ceiling  0.5)))
+  (pass-if (=  0 (ceiling  0.0)))
+  (pass-if (=  0 (ceiling -0.5)))
+  (pass-if (= -1 (ceiling -1.25)))
+  (pass-if (= -1 (ceiling -1.5)))
+
+  (with-test-prefix "inum"
+    (pass-if "0"
+      (and (= 0    (ceiling 0))
+          (exact? (ceiling 0))))
+
+    (pass-if "1"
+      (and (= 1    (ceiling 1))
+          (exact? (ceiling 1))))
+
+    (pass-if "-1"
+      (and (= -1   (ceiling -1))
+          (exact? (ceiling -1)))))
+
+  (with-test-prefix "bignum"
+    (let ((x (1+ most-positive-fixnum)))
+      (pass-if "(1+ most-positive-fixnum)"
+       (and (= x    (ceiling x))
+            (exact? (ceiling x)))))
+
+    (let ((x (1- most-negative-fixnum)))
+      (pass-if "(1- most-negative-fixnum)"
+       (and (= x    (ceiling x))
+            (exact? (ceiling x))))))
+
+  (with-test-prefix "frac"
+    (define (=exact x y)
+      (and (= x y)
+          (exact? y)))
+
+    (pass-if (=exact -2 (ceiling -7/3)))
+    (pass-if (=exact -1 (ceiling -5/3)))
+    (pass-if (=exact -1 (ceiling -4/3)))
+    (pass-if (=exact  0 (ceiling -2/3)))
+    (pass-if (=exact  0 (ceiling -1/3)))
+    (pass-if (=exact  1 (ceiling  1/3)))
+    (pass-if (=exact  1 (ceiling  2/3)))
+    (pass-if (=exact  2 (ceiling  4/3)))
+    (pass-if (=exact  2 (ceiling  5/3)))
+    (pass-if (=exact  3 (ceiling  7/3)))
+
+    (pass-if (=exact -2 (ceiling -17/6)))
+    (pass-if (=exact -2 (ceiling -16/6)))
+    (pass-if (=exact -2 (ceiling -15/6)))
+    (pass-if (=exact -2 (ceiling -14/6)))
+    (pass-if (=exact -2 (ceiling -13/6)))
+    (pass-if (=exact -1 (ceiling -11/6)))
+    (pass-if (=exact -1 (ceiling -10/6)))
+    (pass-if (=exact -1 (ceiling  -9/6)))
+    (pass-if (=exact -1 (ceiling  -8/6)))
+    (pass-if (=exact -1 (ceiling  -7/6)))
+    (pass-if (=exact  0 (ceiling  -5/6)))
+    (pass-if (=exact  0 (ceiling  -4/6)))
+    (pass-if (=exact  0 (ceiling  -3/6)))
+    (pass-if (=exact  0 (ceiling  -2/6)))
+    (pass-if (=exact  0 (ceiling  -1/6)))
+    (pass-if (=exact  1 (ceiling   1/6)))
+    (pass-if (=exact  1 (ceiling   2/6)))
+    (pass-if (=exact  1 (ceiling   3/6)))
+    (pass-if (=exact  1 (ceiling   4/6)))
+    (pass-if (=exact  1 (ceiling   5/6)))
+    (pass-if (=exact  2 (ceiling   7/6)))
+    (pass-if (=exact  2 (ceiling   8/6)))
+    (pass-if (=exact  2 (ceiling   9/6)))
+    (pass-if (=exact  2 (ceiling  10/6)))
+    (pass-if (=exact  2 (ceiling  11/6)))
+    (pass-if (=exact  3 (ceiling  13/6)))
+    (pass-if (=exact  3 (ceiling  14/6)))
+    (pass-if (=exact  3 (ceiling  15/6)))
+    (pass-if (=exact  3 (ceiling  16/6)))
+    (pass-if (=exact  3 (ceiling  17/6))))
+
+  (with-test-prefix "real"
+    (pass-if "0.0"
+      (and (= 0.0    (ceiling 0.0))
+          (inexact? (ceiling 0.0))))
+
+    (pass-if "1.0"
+      (and (= 1.0    (ceiling 1.0))
+          (inexact? (ceiling 1.0))))
+
+    (pass-if "-1.0"
+      (and (= -1.0   (ceiling -1.0))
+          (inexact? (ceiling -1.0))))
+
+    (pass-if "-3.1"
+      (and (= -3.0   (ceiling -3.1))
+          (inexact? (ceiling -3.1))))
+
+    (pass-if "3.1"
+      (and (= 4.0    (ceiling 3.1))
+          (inexact? (ceiling 3.1))))
+
+    (pass-if "3.9"
+      (and (= 4.0    (ceiling 3.9))
+          (inexact? (ceiling 3.9))))
+
+    (pass-if "-3.9"
+      (and (= -3.0   (ceiling -3.9))
+          (inexact? (ceiling -3.9))))
+
+    (pass-if "1.5"
+      (and (= 2.0    (ceiling 1.5))
+          (inexact? (ceiling 1.5))))
+
+    (pass-if "2.5"
+      (and (= 3.0    (ceiling 2.5))
+          (inexact? (ceiling 2.5))))
+
+    (pass-if "3.5"
+      (and (= 4.0    (ceiling 3.5))
+          (inexact? (ceiling 3.5))))
+
+    (pass-if "-1.5"
+      (and (= -1.0   (ceiling -1.5))
+          (inexact? (ceiling -1.5))))
+
+    (pass-if "-2.5"
+      (and (= -2.0   (ceiling -2.5))
+          (inexact? (ceiling -2.5))))
+
+    (pass-if "-3.5"
+      (and (= -3.0   (ceiling -3.5))
+          (inexact? (ceiling -3.5))))))
+
+;;;
 ;;; truncate
 ;;;
 
@@ -3319,7 +3597,131 @@
   (pass-if (=  0 (truncate  0.0)))
   (pass-if (=  0 (truncate -0.5)))
   (pass-if (= -1 (truncate -1.25)))
-  (pass-if (= -1 (truncate -1.5))))
+  (pass-if (= -1 (truncate -1.5)))
+
+  (with-test-prefix "inum"
+    (pass-if "0"
+      (and (= 0    (truncate 0))
+          (exact? (truncate 0))))
+
+    (pass-if "1"
+      (and (= 1    (truncate 1))
+          (exact? (truncate 1))))
+
+    (pass-if "-1"
+      (and (= -1   (truncate -1))
+          (exact? (truncate -1)))))
+
+  (with-test-prefix "bignum"
+    (let ((x (1+ most-positive-fixnum)))
+      (pass-if "(1+ most-positive-fixnum)"
+       (and (= x    (truncate x))
+            (exact? (truncate x)))))
+
+    (let ((x (1- most-negative-fixnum)))
+      (pass-if "(1- most-negative-fixnum)"
+       (and (= x    (truncate x))
+            (exact? (truncate x))))))
+
+  (with-test-prefix "frac"
+    (define (=exact x y)
+      (and (= x y)
+          (exact? y)))
+
+    (pass-if (=exact -2 (truncate -7/3)))
+    (pass-if (=exact -1 (truncate -5/3)))
+    (pass-if (=exact -1 (truncate -4/3)))
+    (pass-if (=exact  0 (truncate -2/3)))
+    (pass-if (=exact  0 (truncate -1/3)))
+    (pass-if (=exact  0 (truncate  1/3)))
+    (pass-if (=exact  0 (truncate  2/3)))
+    (pass-if (=exact  1 (truncate  4/3)))
+    (pass-if (=exact  1 (truncate  5/3)))
+    (pass-if (=exact  2 (truncate  7/3)))
+
+    (pass-if (=exact -2 (truncate -17/6)))
+    (pass-if (=exact -2 (truncate -16/6)))
+    (pass-if (=exact -2 (truncate -15/6)))
+    (pass-if (=exact -2 (truncate -14/6)))
+    (pass-if (=exact -2 (truncate -13/6)))
+    (pass-if (=exact -1 (truncate -11/6)))
+    (pass-if (=exact -1 (truncate -10/6)))
+    (pass-if (=exact -1 (truncate  -9/6)))
+    (pass-if (=exact -1 (truncate  -8/6)))
+    (pass-if (=exact -1 (truncate  -7/6)))
+    (pass-if (=exact  0 (truncate  -5/6)))
+    (pass-if (=exact  0 (truncate  -4/6)))
+    (pass-if (=exact  0 (truncate  -3/6)))
+    (pass-if (=exact  0 (truncate  -2/6)))
+    (pass-if (=exact  0 (truncate  -1/6)))
+    (pass-if (=exact  0 (truncate   1/6)))
+    (pass-if (=exact  0 (truncate   2/6)))
+    (pass-if (=exact  0 (truncate   3/6)))
+    (pass-if (=exact  0 (truncate   4/6)))
+    (pass-if (=exact  0 (truncate   5/6)))
+    (pass-if (=exact  1 (truncate   7/6)))
+    (pass-if (=exact  1 (truncate   8/6)))
+    (pass-if (=exact  1 (truncate   9/6)))
+    (pass-if (=exact  1 (truncate  10/6)))
+    (pass-if (=exact  1 (truncate  11/6)))
+    (pass-if (=exact  2 (truncate  13/6)))
+    (pass-if (=exact  2 (truncate  14/6)))
+    (pass-if (=exact  2 (truncate  15/6)))
+    (pass-if (=exact  2 (truncate  16/6)))
+    (pass-if (=exact  2 (truncate  17/6))))
+
+  (with-test-prefix "real"
+    (pass-if "0.0"
+      (and (= 0.0    (truncate 0.0))
+          (inexact? (truncate 0.0))))
+
+    (pass-if "1.0"
+      (and (= 1.0    (truncate 1.0))
+          (inexact? (truncate 1.0))))
+
+    (pass-if "-1.0"
+      (and (= -1.0   (truncate -1.0))
+          (inexact? (truncate -1.0))))
+
+    (pass-if "-3.1"
+      (and (= -3.0   (truncate -3.1))
+          (inexact? (truncate -3.1))))
+
+    (pass-if "3.1"
+      (and (= 3.0    (truncate 3.1))
+          (inexact? (truncate 3.1))))
+
+    (pass-if "3.9"
+      (and (= 3.0    (truncate 3.9))
+          (inexact? (truncate 3.9))))
+
+    (pass-if "-3.9"
+      (and (= -3.0   (truncate -3.9))
+          (inexact? (truncate -3.9))))
+
+    (pass-if "1.5"
+      (and (= 1.0    (truncate 1.5))
+          (inexact? (truncate 1.5))))
+
+    (pass-if "2.5"
+      (and (= 2.0    (truncate 2.5))
+          (inexact? (truncate 2.5))))
+
+    (pass-if "3.5"
+      (and (= 3.0    (truncate 3.5))
+          (inexact? (truncate 3.5))))
+
+    (pass-if "-1.5"
+      (and (= -1.0   (truncate -1.5))
+          (inexact? (truncate -1.5))))
+
+    (pass-if "-2.5"
+      (and (= -2.0   (truncate -2.5))
+          (inexact? (truncate -2.5))))
+
+    (pass-if "-3.5"
+      (and (= -3.0   (truncate -3.5))
+          (inexact? (truncate -3.5))))))
 
 ;;;
 ;;; round
@@ -3568,14 +3970,6 @@
       (= 1.0 (exact->inexact (/ (1+ big) big))))))
 
 ;;;
-;;; floor
-;;;
-
-;;;
-;;; ceiling
-;;;
-
-;;;
 ;;; expt
 ;;;
 
@@ -4148,6 +4542,42 @@
                   (test-within-range? 0 <= r < (abs y)))
              (test-eqv? q (/ x y)))))
 
+  (define (valid-floor-answer? x y q r)
+    (and (eq? (exact? q)
+              (exact? r)
+              (and (exact? x) (exact? y)))
+         (test-eqv? r (- x (* q y)))
+         (if (and (finite? x) (finite? y))
+             (and (integer? q)
+                  (if (> y 0)
+                      (test-within-range? 0 <= r < y)
+                      (test-within-range? y < r <= 0)))
+             (test-eqv? q (/ x y)))))
+
+  (define (valid-ceiling-answer? x y q r)
+    (and (eq? (exact? q)
+              (exact? r)
+              (and (exact? x) (exact? y)))
+         (test-eqv? r (- x (* q y)))
+         (if (and (finite? x) (finite? y))
+             (and (integer? q)
+                  (if (> y 0)
+                      (test-within-range? (- y) < r <= 0)
+                      (test-within-range?    0 <= r < (- y))))
+             (test-eqv? q (/ x y)))))
+
+  (define (valid-truncate-answer? x y q r)
+    (and (eq? (exact? q)
+              (exact? r)
+              (and (exact? x) (exact? y)))
+         (test-eqv? r (- x (* q y)))
+         (if (and (finite? x) (finite? y))
+             (and (integer? q)
+                  (if (> x 0)
+                      (test-within-range?          0 <= r < (abs y))
+                      (test-within-range? (- (abs y)) < r <= 0)))
+             (test-eqv? q (/ x y)))))
+
   (define (valid-centered-answer? x y q r)
     (and (eq? (exact? q)
               (exact? r)
@@ -4159,6 +4589,19 @@
                    (* -1/2 (abs y)) <= r < (* +1/2 (abs y))))
              (test-eqv? q (/ x y)))))
 
+  (define (valid-round-answer? x y q r)
+    (and (eq? (exact? q)
+              (exact? r)
+              (and (exact? x) (exact? y)))
+         (test-eqv? r (- x (* q y)))
+         (if (and (finite? x) (finite? y))
+             (and (integer? q)
+                  (let ((ay/2 (/ (abs y) 2)))
+                    (if (even? q)
+                        (test-within-range? (- ay/2) <= r <= ay/2)
+                        (test-within-range? (- ay/2) <  r <  ay/2))))
+             (test-eqv? q (/ x y)))))
+
   (define (for lsts f) (apply for-each f lsts))
 
   (define big (expt 10 (1+ (inexact->exact (ceiling (log10 fixnum-max))))))
@@ -4284,8 +4727,32 @@
                         euclidean-remainder
                         valid-euclidean-answer?))
 
+  (with-test-prefix "floor/"
+    (run-division-tests floor/
+                        floor-quotient
+                        floor-remainder
+                        valid-floor-answer?))
+
+  (with-test-prefix "ceiling/"
+    (run-division-tests ceiling/
+                        ceiling-quotient
+                        ceiling-remainder
+                        valid-ceiling-answer?))
+
+  (with-test-prefix "truncate/"
+    (run-division-tests truncate/
+                        truncate-quotient
+                        truncate-remainder
+                        valid-truncate-answer?))
+
   (with-test-prefix "centered/"
     (run-division-tests centered/
                         centered-quotient
                         centered-remainder
-                        valid-centered-answer?)))
+                        valid-centered-answer?))
+
+  (with-test-prefix "round/"
+    (run-division-tests round/
+                        round-quotient
+                        round-remainder
+                        valid-round-answer?)))


hooks/post-receive
-- 
GNU Guile



reply via email to

[Prev in Thread] Current Thread [Next in Thread]