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 traps)238 mpd_qsettraps(mpd_context_t *ctx, uint32_t traps)
239 {
240     if (traps > MPD_Max_status) {
241         return 0;
242     }
243     ctx->traps = traps;
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