1  /*
2   * Copyright (c) 2008-2020 Stefan Krah. All rights reserved.
3   *
4   * Redistribution and use in source and binary forms, with or without
5   * modification, are permitted provided that the following conditions
6   * are met:
7   *
8   * 1. Redistributions of source code must retain the above copyright
9   *    notice, this list of conditions and the following disclaimer.
10   *
11   * 2. Redistributions in binary form must reproduce the above copyright
12   *    notice, this list of conditions and the following disclaimer in the
13   *    documentation and/or other materials provided with the distribution.
14   *
15   * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
16   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18   * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19   * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21   * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24   * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25   * SUCH DAMAGE.
26   */
27  
28  
29  #include "mpdecimal.h"
30  
31  #include <signal.h>
32  #include <stdio.h>
33  #include <string.h>
34  
35  
36  void
mpd_dflt_traphandler(mpd_context_t * ctx)37  mpd_dflt_traphandler(mpd_context_t *ctx)
38  {
39      (void)ctx;
40      raise(SIGFPE);
41  }
42  
43  void (* mpd_traphandler)(mpd_context_t *) = mpd_dflt_traphandler;
44  
45  
46  /* Set guaranteed minimum number of coefficient words. The function may
47     be used once at program start. Setting MPD_MINALLOC to out-of-bounds
48     values is a catastrophic error, so in that case the function exits rather
49     than relying on the user to check a return value. */
50  void
mpd_setminalloc(mpd_ssize_t n)51  mpd_setminalloc(mpd_ssize_t n)
52  {
53      static int minalloc_is_set = 0;
54  
55      if (minalloc_is_set) {
56          mpd_err_warn("mpd_setminalloc: ignoring request to set "
57                       "MPD_MINALLOC a second time\n");
58          return;
59      }
60      if (n < MPD_MINALLOC_MIN || n > MPD_MINALLOC_MAX) {
61          mpd_err_fatal("illegal value for MPD_MINALLOC"); /* GCOV_NOT_REACHED */
62      }
63      MPD_MINALLOC = n;
64      minalloc_is_set = 1;
65  }
66  
67  void
mpd_init(mpd_context_t * ctx,mpd_ssize_t prec)68  mpd_init(mpd_context_t *ctx, mpd_ssize_t prec)
69  {
70      mpd_ssize_t ideal_minalloc;
71  
72      mpd_defaultcontext(ctx);
73  
74      if (!mpd_qsetprec(ctx, prec)) {
75          mpd_addstatus_raise(ctx, MPD_Invalid_context);
76          return;
77      }
78  
79      ideal_minalloc = 2 * ((prec+MPD_RDIGITS-1) / MPD_RDIGITS);
80      if (ideal_minalloc < MPD_MINALLOC_MIN) ideal_minalloc = MPD_MINALLOC_MIN;
81      if (ideal_minalloc > MPD_MINALLOC_MAX) ideal_minalloc = MPD_MINALLOC_MAX;
82  
83      mpd_setminalloc(ideal_minalloc);
84  }
85  
86  void
mpd_maxcontext(mpd_context_t * ctx)87  mpd_maxcontext(mpd_context_t *ctx)
88  {
89      ctx->prec=MPD_MAX_PREC;
90      ctx->emax=MPD_MAX_EMAX;
91      ctx->emin=MPD_MIN_EMIN;
92      ctx->round=MPD_ROUND_HALF_EVEN;
93      ctx->traps=MPD_Traps;
94      ctx->status=0;
95      ctx->newtrap=0;
96      ctx->clamp=0;
97      ctx->allcr=1;
98  }
99  
100  void
mpd_defaultcontext(mpd_context_t * ctx)101  mpd_defaultcontext(mpd_context_t *ctx)
102  {
103      ctx->prec=2*MPD_RDIGITS;
104      ctx->emax=MPD_MAX_EMAX;
105      ctx->emin=MPD_MIN_EMIN;
106      ctx->round=MPD_ROUND_HALF_UP;
107      ctx->traps=MPD_Traps;
108      ctx->status=0;
109      ctx->newtrap=0;
110      ctx->clamp=0;
111      ctx->allcr=1;
112  }
113  
114  void
mpd_basiccontext(mpd_context_t * ctx)115  mpd_basiccontext(mpd_context_t *ctx)
116  {
117      ctx->prec=9;
118      ctx->emax=MPD_MAX_EMAX;
119      ctx->emin=MPD_MIN_EMIN;
120      ctx->round=MPD_ROUND_HALF_UP;
121      ctx->traps=MPD_Traps|MPD_Clamped;
122      ctx->status=0;
123      ctx->newtrap=0;
124      ctx->clamp=0;
125      ctx->allcr=1;
126  }
127  
128  int
mpd_ieee_context(mpd_context_t * ctx,int bits)129  mpd_ieee_context(mpd_context_t *ctx, int bits)
130  {
131      if (bits <= 0 || bits > MPD_IEEE_CONTEXT_MAX_BITS || bits % 32) {
132          return -1;
133      }
134  
135      ctx->prec = 9 * (bits/32) - 2;
136      ctx->emax = 3 * ((mpd_ssize_t)1<<(bits/16+3));
137      ctx->emin = 1 - ctx->emax;
138      ctx->round=MPD_ROUND_HALF_EVEN;
139      ctx->traps=0;
140      ctx->status=0;
141      ctx->newtrap=0;
142      ctx->clamp=1;
143      ctx->allcr=1;
144  
145      return 0;
146  }
147  
148  mpd_ssize_t
mpd_getprec(const mpd_context_t * ctx)149  mpd_getprec(const mpd_context_t *ctx)
150  {
151      return ctx->prec;
152  }
153  
154  mpd_ssize_t
mpd_getemax(const mpd_context_t * ctx)155  mpd_getemax(const mpd_context_t *ctx)
156  {
157      return ctx->emax;
158  }
159  
160  mpd_ssize_t
mpd_getemin(const mpd_context_t * ctx)161  mpd_getemin(const mpd_context_t *ctx)
162  {
163      return ctx->emin;
164  }
165  
166  int
mpd_getround(const mpd_context_t * ctx)167  mpd_getround(const mpd_context_t *ctx)
168  {
169      return ctx->round;
170  }
171  
172  uint32_t
mpd_gettraps(const mpd_context_t * ctx)173  mpd_gettraps(const mpd_context_t *ctx)
174  {
175      return ctx->traps;
176  }
177  
178  uint32_t
mpd_getstatus(const mpd_context_t * ctx)179  mpd_getstatus(const mpd_context_t *ctx)
180  {
181      return ctx->status;
182  }
183  
184  int
mpd_getclamp(const mpd_context_t * ctx)185  mpd_getclamp(const mpd_context_t *ctx)
186  {
187      return ctx->clamp;
188  }
189  
190  int
mpd_getcr(const mpd_context_t * ctx)191  mpd_getcr(const mpd_context_t *ctx)
192  {
193      return ctx->allcr;
194  }
195  
196  
197  int
mpd_qsetprec(mpd_context_t * ctx,mpd_ssize_t prec)198  mpd_qsetprec(mpd_context_t *ctx, mpd_ssize_t prec)
199  {
200      if (prec <= 0 || prec > MPD_MAX_PREC) {
201          return 0;
202      }
203      ctx->prec = prec;
204      return 1;
205  }
206  
207  int
mpd_qsetemax(mpd_context_t * ctx,mpd_ssize_t emax)208  mpd_qsetemax(mpd_context_t *ctx, mpd_ssize_t emax)
209  {
210      if (emax < 0 || emax > MPD_MAX_EMAX) {
211          return 0;
212      }
213      ctx->emax = emax;
214      return 1;
215  }
216  
217  int
mpd_qsetemin(mpd_context_t * ctx,mpd_ssize_t emin)218  mpd_qsetemin(mpd_context_t *ctx, mpd_ssize_t emin)
219  {
220      if (emin > 0 || emin < MPD_MIN_EMIN) {
221          return 0;
222      }
223      ctx->emin = emin;
224      return 1;
225  }
226  
227  int
mpd_qsetround(mpd_context_t * ctx,int round)228  mpd_qsetround(mpd_context_t *ctx, int round)
229  {
230      if (!(0 <= round && round < MPD_ROUND_GUARD)) {
231          return 0;
232      }
233      ctx->round = round;
234      return 1;
235  }
236  
237  int
mpd_qsettraps(mpd_context_t * ctx,uint32_t flags)238  mpd_qsettraps(mpd_context_t *ctx, uint32_t flags)
239  {
240      if (flags > MPD_Max_status) {
241          return 0;
242      }
243      ctx->traps = flags;
244      return 1;
245  }
246  
247  int
mpd_qsetstatus(mpd_context_t * ctx,uint32_t flags)248  mpd_qsetstatus(mpd_context_t *ctx, uint32_t flags)
249  {
250      if (flags > MPD_Max_status) {
251          return 0;
252      }
253      ctx->status = flags;
254      return 1;
255  }
256  
257  int
mpd_qsetclamp(mpd_context_t * ctx,int c)258  mpd_qsetclamp(mpd_context_t *ctx, int c)
259  {
260      if (c != 0 && c != 1) {
261          return 0;
262      }
263      ctx->clamp = c;
264      return 1;
265  }
266  
267  int
mpd_qsetcr(mpd_context_t * ctx,int c)268  mpd_qsetcr(mpd_context_t *ctx, int c)
269  {
270      if (c != 0 && c != 1) {
271          return 0;
272      }
273      ctx->allcr = c;
274      return 1;
275  }
276  
277  
278  void
mpd_addstatus_raise(mpd_context_t * ctx,uint32_t flags)279  mpd_addstatus_raise(mpd_context_t *ctx, uint32_t flags)
280  {
281      ctx->status |= flags;
282      if (flags&ctx->traps) {
283          ctx->newtrap = (flags&ctx->traps);
284          mpd_traphandler(ctx);
285      }
286  }
287