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