diff options
| author | Paul Eggert | 2018-08-27 21:27:50 -0700 |
|---|---|---|
| committer | Paul Eggert | 2018-08-27 21:45:22 -0700 |
| commit | 9abaf5f3581ecb76f30e8a6e7ee0e9633c133d1c (patch) | |
| tree | c39260a6e26845b0a1307be98b38581468925c58 /src/bignum.c | |
| parent | bf1b147b55e1328efca6e40181e79dd9a369895d (diff) | |
| download | emacs-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.c | 161 |
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 | |||
| 3 | Copyright 2018 Free Software Foundation, Inc. | ||
| 4 | |||
| 5 | This file is part of GNU Emacs. | ||
| 6 | |||
| 7 | GNU Emacs is free software: you can redistribute it and/or modify | ||
| 8 | it under the terms of the GNU General Public License as published by | ||
| 9 | the Free Software Foundation, either version 3 of the License, or (at | ||
| 10 | your option) any later version. | ||
| 11 | |||
| 12 | GNU Emacs is distributed in the hope that it will be useful, | ||
| 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | GNU General Public License for more details. | ||
| 16 | |||
| 17 | You should have received a copy of the GNU General Public License | ||
| 18 | along 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. */ | ||
| 27 | double | ||
| 28 | bignum_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. */ | ||
| 34 | Lisp_Object | ||
| 35 | double_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. */ | ||
| 46 | static Lisp_Object | ||
| 47 | make_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. */ | ||
| 64 | static Lisp_Object | ||
| 65 | make_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. */ | ||
| 71 | Lisp_Object | ||
| 72 | make_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. */ | ||
| 84 | Lisp_Object | ||
| 85 | make_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 | |||
| 112 | void | ||
| 113 | mpz_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 | |||
| 133 | Lisp_Object | ||
| 134 | bignum_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 | |||
| 152 | Lisp_Object | ||
| 153 | make_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 | } | ||