isnormal, isunordered, lgamma, log1p, *log2 [via (log X 2)], *logb
(approximately), lrint/llrint, lround/llround, nan, nearbyint,
nextafter, nexttoward, remainder, remquo, *rint, round, scalbln,
- scalbn, signbit, tgamma, trunc.
+ scalbn, signbit, tgamma, *trunc.
*/
#include <config.h>
{
CHECK_NUMBER_OR_FLOAT (arg);
- if (! NILP (divisor))
+ double d;
+ if (NILP (divisor))
+ {
+ if (! FLOATP (arg))
+ return arg;
+ d = XFLOAT_DATA (arg);
+ }
+ else
{
- EMACS_INT i1, i2;
-
CHECK_NUMBER_OR_FLOAT (divisor);
-
- if (FLOATP (arg) || FLOATP (divisor))
+ if (!FLOATP (arg) && !FLOATP (divisor))
{
- double f1, f2;
-
- f1 = FLOATP (arg) ? XFLOAT_DATA (arg) : XINT (arg);
- f2 = (FLOATP (divisor) ? XFLOAT_DATA (divisor) : XINT (divisor));
- if (! IEEE_FLOATING_POINT && f2 == 0)
+ if (XINT (divisor) == 0)
xsignal0 (Qarith_error);
-
- f1 = (*double_round) (f1 / f2);
- if (FIXNUM_OVERFLOW_P (f1))
- xsignal3 (Qrange_error, build_string (name), arg, divisor);
- arg = make_number (f1);
- return arg;
+ return make_number (int_round2 (XINT (arg), XINT (divisor)));
}
- i1 = XINT (arg);
- i2 = XINT (divisor);
-
- if (i2 == 0)
+ double f1 = FLOATP (arg) ? XFLOAT_DATA (arg) : XINT (arg);
+ double f2 = FLOATP (divisor) ? XFLOAT_DATA (divisor) : XINT (divisor);
+ if (! IEEE_FLOATING_POINT && f2 == 0)
xsignal0 (Qarith_error);
-
- XSETINT (arg, (*int_round2) (i1, i2));
- return arg;
+ d = f1 / f2;
}
- if (FLOATP (arg))
+ /* Round, coarsely test for fixnum overflow before converting to
+ EMACS_INT (to avoid undefined C behavior), and then exactly test
+ for overflow after converting (as FIXNUM_OVERFLOW_P is inaccurate
+ on floats). */
+ double dr = double_round (d);
+ if (fabs (dr) < 2 * (MOST_POSITIVE_FIXNUM + 1))
{
- double d = (*double_round) (XFLOAT_DATA (arg));
- if (FIXNUM_OVERFLOW_P (d))
- xsignal2 (Qrange_error, build_string (name), arg);
- arg = make_number (d);
+ EMACS_INT ir = dr;
+ if (! FIXNUM_OVERFLOW_P (ir))
+ return make_number (ir);
}
-
- return arg;
+ xsignal2 (Qrange_error, build_string (name), arg);
}
static EMACS_INT
}
#endif
+#ifdef HAVE_TRUNC
+#define emacs_trunc trunc
+#else
static double
-double_identity (double d)
+emacs_trunc (double d)
{
- return d;
+ return (d < 0 ? ceil : floor) (d);
}
+#endif
DEFUN ("ceiling", Fceiling, Sceiling, 1, 2, 0,
doc: /* Return the smallest integer no less than ARG.
With optional DIVISOR, truncate ARG/DIVISOR. */)
(Lisp_Object arg, Lisp_Object divisor)
{
- return rounding_driver (arg, divisor, double_identity, truncate2,
+ return rounding_driver (arg, divisor, emacs_trunc, truncate2,
"truncate");
}
return lisp_h_EQ (x, y);
}
-/* Value is true if I doesn't fit into a Lisp fixnum. It is
- written this way so that it also works if I is of unsigned
- type or if I is a NaN. */
+/* True if the possibly-unsigned integer I doesn't fit in a Lisp fixnum. */
#define FIXNUM_OVERFLOW_P(i) \
(! ((0 <= (i) || MOST_NEGATIVE_FIXNUM <= (i)) && (i) <= MOST_POSITIVE_FIXNUM))
because 'abs' is reserved by the C standard. */
#define eabs(x) ((x) < 0 ? -(x) : (x))
-/* Return a fixnum or float, depending on whether VAL fits in a Lisp
- fixnum. */
+/* Return a fixnum or float, depending on whether the integer VAL fits
+ in a Lisp fixnum. */
#define make_fixnum_or_float(val) \
(FIXNUM_OVERFLOW_P (val) ? make_float (val) : make_number (val))
--- /dev/null
+;;; floatfn-tests.el --- tests for floating point operations
+
+;; Copyright 2017 Free Software Foundation, Inc.
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
+
+(require 'ert)
+
+(ert-deftest divide-extreme-sign ()
+ (should-error (ceiling most-negative-fixnum -1.0))
+ (should-error (floor most-negative-fixnum -1.0))
+ (should-error (round most-negative-fixnum -1.0))
+ (should-error (truncate most-negative-fixnum -1.0)))
+
+(provide 'floatfns-tests)