aboutsummaryrefslogtreecommitdiffstats
path: root/src/bignum.c
diff options
context:
space:
mode:
authorPaul Eggert2018-08-27 21:27:50 -0700
committerPaul Eggert2018-08-27 21:45:22 -0700
commit9abaf5f3581ecb76f30e8a6e7ee0e9633c133d1c (patch)
treec39260a6e26845b0a1307be98b38581468925c58 /src/bignum.c
parentbf1b147b55e1328efca6e40181e79dd9a369895d (diff)
downloademacs-9abaf5f3581ecb76f30e8a6e7ee0e9633c133d1c.tar.gz
emacs-9abaf5f3581ecb76f30e8a6e7ee0e9633c133d1c.zip
Modularize bignums better
* src/bignum.c, src/bignum.h: New files. Only modules that need to know how bignums are implemented should include bignum.h. Currently these are alloc.c, bignum.c (of course), data.c, emacs.c, emacs-module.c, floatfns.c, fns.c, print.c. * src/Makefile.in (base_obj): Add bignum.o. * src/alloc.c (make_bignum_str): Move to bignum.c. (make_number): Remove; replaced by bignum.c’s make_integer. All callers changed. * src/conf_post.h (ARG_NONNULL): New macro. * src/json.c (json_to_lisp): Use it. * src/data.c (Fnatnump): Move NATNUMP’s implementation here from lisp.h. * src/data.c (Fnumber_to_string): * src/editfns.c (styled_format): Move conversion of string to bignum to bignum_to_string, and call it here. * src/emacs-module.c (module_make_integer): * src/floatfns.c (Fabs): Simplify by using make_int. * src/emacs.c: Include bignum.h, to expand its inline fns. * src/floatfns.c (Ffloat): Simplify by using XFLOATINT. (rounding_driver): Simplify by using double_to_bignum. (rounddiv_q): Clarify use of temporaries. * src/lisp.h: Move decls that need to know bignum internals to bignum.h. Do not include gmp.h or mini-gmp.h; that is now bignum.h’s job. (GMP_NUM_BITS, struct Lisp_Bignum, XBIGNUM, mpz_set_intmax): Move to bignum.h. (make_int): New function. (NATNUMP): Remove; all callers changed to use Fnatnump. (XFLOATINT): If arg is a bignum, use bignum_to_double, so that bignum internals are not exposed here. * src/print.c (print_vectorlike): Use SAFE_ALLOCA to avoid the need for a record_unwind_protect_ptr.
Diffstat (limited to 'src/bignum.c')
-rw-r--r--src/bignum.c161
1 files changed, 161 insertions, 0 deletions
diff --git a/src/bignum.c b/src/bignum.c
new file mode 100644
index 00000000000..18f94e7ed63
--- /dev/null
+++ b/src/bignum.c
@@ -0,0 +1,161 @@
1/* Big numbers for Emacs.
2
3Copyright 2018 Free Software Foundation, Inc.
4
5This file is part of GNU Emacs.
6
7GNU Emacs is free software: you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation, either version 3 of the License, or (at
10your option) any later version.
11
12GNU Emacs is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
19
20#include <config.h>
21
22#include "bignum.h"
23
24#include "lisp.h"
25
26/* Return the value of the Lisp bignum N, as a double. */
27double
28bignum_to_double (Lisp_Object n)
29{
30 return mpz_get_d (XBIGNUM (n)->value);
31}
32
33/* Return D, converted to a bignum. Discard any fraction. */
34Lisp_Object
35double_to_bignum (double d)
36{
37 mpz_t z;
38 mpz_init_set_d (z, d);
39 Lisp_Object result = make_integer (z);
40 mpz_clear (z);
41 return result;
42}
43
44/* Return a Lisp integer equal to OP, which has BITS bits and which
45 must not be in fixnum range. */
46static Lisp_Object
47make_bignum_bits (mpz_t const op, size_t bits)
48{
49 /* The documentation says integer-width should be nonnegative, so
50 a single comparison suffices even though 'bits' is unsigned. */
51 if (integer_width < bits)
52 range_error ();
53
54 struct Lisp_Bignum *b = ALLOCATE_PSEUDOVECTOR (struct Lisp_Bignum, value,
55 PVEC_BIGNUM);
56 /* We could mpz_init + mpz_swap here, to avoid a copy, but the
57 resulting API seemed possibly confusing. */
58 mpz_init_set (b->value, op);
59
60 return make_lisp_ptr (b, Lisp_Vectorlike);
61}
62
63/* Return a Lisp integer equal to OP, which must not be in fixnum range. */
64static Lisp_Object
65make_bignum (mpz_t const op)
66{
67 return make_bignum_bits (op, mpz_sizeinbase (op, 2));
68}
69
70/* Return a Lisp integer equal to N, which must not be in fixnum range. */
71Lisp_Object
72make_bigint (intmax_t n)
73{
74 eassert (FIXNUM_OVERFLOW_P (n));
75 mpz_t z;
76 mpz_init (z);
77 mpz_set_intmax (z, n);
78 Lisp_Object result = make_bignum (z);
79 mpz_clear (z);
80 return result;
81}
82
83/* Return a Lisp integer with value taken from OP. */
84Lisp_Object
85make_integer (mpz_t const op)
86{
87 size_t bits = mpz_sizeinbase (op, 2);
88
89 if (bits <= FIXNUM_BITS)
90 {
91 EMACS_INT v = 0;
92 int i = 0, shift = 0;
93
94 do
95 {
96 EMACS_INT limb = mpz_getlimbn (op, i++);
97 v += limb << shift;
98 shift += GMP_NUMB_BITS;
99 }
100 while (shift < bits);
101
102 if (mpz_sgn (op) < 0)
103 v = -v;
104
105 if (!FIXNUM_OVERFLOW_P (v))
106 return make_fixnum (v);
107 }
108
109 return make_bignum_bits (op, bits);
110}
111
112void
113mpz_set_intmax_slow (mpz_t result, intmax_t v)
114{
115 bool complement = v < 0;
116 if (complement)
117 v = -1 - v;
118
119 enum { nails = sizeof v * CHAR_BIT - INTMAX_WIDTH };
120# ifndef HAVE_GMP
121 /* mini-gmp requires NAILS to be zero, which is true for all
122 likely Emacs platforms. Sanity-check this. */
123 verify (nails == 0);
124# endif
125
126 mpz_import (result, 1, -1, sizeof v, 0, nails, &v);
127 if (complement)
128 mpz_com (result, result);
129}
130
131/* Convert NUM to a base-BASE Lisp string. */
132
133Lisp_Object
134bignum_to_string (Lisp_Object num, int base)
135{
136 ptrdiff_t n = mpz_sizeinbase (XBIGNUM (num)->value, base) - 1;
137 USE_SAFE_ALLOCA;
138 char *str = SAFE_ALLOCA (n + 3);
139 mpz_get_str (str, base, XBIGNUM (num)->value);
140 while (str[n])
141 n++;
142 Lisp_Object result = make_unibyte_string (str, n);
143 SAFE_FREE ();
144 return result;
145}
146
147/* Create a bignum by scanning NUM, with digits in BASE.
148 NUM must consist of an optional '-', a nonempty sequence
149 of base-BASE digits, and a terminating null byte, and
150 the represented number must not be in fixnum range. */
151
152Lisp_Object
153make_bignum_str (char const *num, int base)
154{
155 struct Lisp_Bignum *b = ALLOCATE_PSEUDOVECTOR (struct Lisp_Bignum, value,
156 PVEC_BIGNUM);
157 mpz_init (b->value);
158 int check = mpz_set_str (b->value, num, base);
159 eassert (check == 0);
160 return make_lisp_ptr (b, Lisp_Vectorlike);
161}