Austin Schuh | dace2a6 | 2020-08-18 10:56:48 -0700 | [diff] [blame] | 1 | /* Reference floating point routines. |
| 2 | |
| 3 | Copyright 1996, 2001, 2004, 2005 Free Software Foundation, Inc. |
| 4 | |
| 5 | This file is part of the GNU MP Library test suite. |
| 6 | |
| 7 | The GNU MP Library test suite is free software; you can redistribute it |
| 8 | and/or modify it under the terms of the GNU General Public License as |
| 9 | published by the Free Software Foundation; either version 3 of the License, |
| 10 | or (at your option) any later version. |
| 11 | |
| 12 | The GNU MP Library test suite is distributed in the hope that it will be |
| 13 | useful, but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
| 15 | Public License for more details. |
| 16 | |
| 17 | You should have received a copy of the GNU General Public License along with |
| 18 | the GNU MP Library test suite. If not, see https://www.gnu.org/licenses/. */ |
| 19 | |
| 20 | #include <stdio.h> |
| 21 | #include <stdlib.h> |
| 22 | |
| 23 | #include "gmp-impl.h" |
| 24 | #include "tests.h" |
| 25 | |
| 26 | |
| 27 | void |
| 28 | refmpf_add (mpf_ptr w, mpf_srcptr u, mpf_srcptr v) |
| 29 | { |
| 30 | mp_size_t hi, lo, size; |
| 31 | mp_ptr ut, vt, wt; |
| 32 | int neg; |
| 33 | mp_exp_t exp; |
| 34 | mp_limb_t cy; |
| 35 | TMP_DECL; |
| 36 | |
| 37 | TMP_MARK; |
| 38 | |
| 39 | if (SIZ (u) == 0) |
| 40 | { |
| 41 | size = ABSIZ (v); |
| 42 | wt = TMP_ALLOC_LIMBS (size + 1); |
| 43 | MPN_COPY (wt, PTR (v), size); |
| 44 | exp = EXP (v); |
| 45 | neg = SIZ (v) < 0; |
| 46 | goto done; |
| 47 | } |
| 48 | if (SIZ (v) == 0) |
| 49 | { |
| 50 | size = ABSIZ (u); |
| 51 | wt = TMP_ALLOC_LIMBS (size + 1); |
| 52 | MPN_COPY (wt, PTR (u), size); |
| 53 | exp = EXP (u); |
| 54 | neg = SIZ (u) < 0; |
| 55 | goto done; |
| 56 | } |
| 57 | if ((SIZ (u) ^ SIZ (v)) < 0) |
| 58 | { |
| 59 | mpf_t tmp; |
| 60 | SIZ (tmp) = -SIZ (v); |
| 61 | EXP (tmp) = EXP (v); |
| 62 | PTR (tmp) = PTR (v); |
| 63 | refmpf_sub (w, u, tmp); |
| 64 | return; |
| 65 | } |
| 66 | neg = SIZ (u) < 0; |
| 67 | |
| 68 | /* Compute the significance of the hi and lo end of the result. */ |
| 69 | hi = MAX (EXP (u), EXP (v)); |
| 70 | lo = MIN (EXP (u) - ABSIZ (u), EXP (v) - ABSIZ (v)); |
| 71 | size = hi - lo; |
| 72 | ut = TMP_ALLOC_LIMBS (size + 1); |
| 73 | vt = TMP_ALLOC_LIMBS (size + 1); |
| 74 | wt = TMP_ALLOC_LIMBS (size + 1); |
| 75 | MPN_ZERO (ut, size); |
| 76 | MPN_ZERO (vt, size); |
| 77 | {int off; |
| 78 | off = size + (EXP (u) - hi) - ABSIZ (u); |
| 79 | MPN_COPY (ut + off, PTR (u), ABSIZ (u)); |
| 80 | off = size + (EXP (v) - hi) - ABSIZ (v); |
| 81 | MPN_COPY (vt + off, PTR (v), ABSIZ (v)); |
| 82 | } |
| 83 | |
| 84 | cy = mpn_add_n (wt, ut, vt, size); |
| 85 | wt[size] = cy; |
| 86 | size += cy; |
| 87 | exp = hi + cy; |
| 88 | |
| 89 | done: |
| 90 | if (size > PREC (w)) |
| 91 | { |
| 92 | wt += size - PREC (w); |
| 93 | size = PREC (w); |
| 94 | } |
| 95 | MPN_COPY (PTR (w), wt, size); |
| 96 | SIZ (w) = neg == 0 ? size : -size; |
| 97 | EXP (w) = exp; |
| 98 | TMP_FREE; |
| 99 | } |
| 100 | |
| 101 | |
| 102 | /* Add 1 "unit in last place" (ie. in the least significant limb) to f. |
| 103 | f cannot be zero, since that has no well-defined "last place". |
| 104 | |
| 105 | This routine is designed for use in cases where we pay close attention to |
| 106 | the size of the data value and are using that (and the exponent) to |
| 107 | indicate the accurate part of a result, or similar. For this reason, if |
| 108 | there's a carry out we don't store 1 and adjust the exponent, we just |
| 109 | leave 100..00. We don't even adjust if there's a carry out of prec+1 |
| 110 | limbs, but instead give up in that case (which we intend shouldn't arise |
| 111 | in normal circumstances). */ |
| 112 | |
| 113 | void |
| 114 | refmpf_add_ulp (mpf_ptr f) |
| 115 | { |
| 116 | mp_ptr fp = PTR(f); |
| 117 | mp_size_t fsize = SIZ(f); |
| 118 | mp_size_t abs_fsize = ABSIZ(f); |
| 119 | mp_limb_t c; |
| 120 | |
| 121 | if (fsize == 0) |
| 122 | { |
| 123 | printf ("Oops, refmpf_add_ulp called with f==0\n"); |
| 124 | abort (); |
| 125 | } |
| 126 | |
| 127 | c = refmpn_add_1 (fp, fp, abs_fsize, CNST_LIMB(1)); |
| 128 | if (c != 0) |
| 129 | { |
| 130 | if (abs_fsize >= PREC(f) + 1) |
| 131 | { |
| 132 | printf ("Oops, refmpf_add_ulp carried out of prec+1 limbs\n"); |
| 133 | abort (); |
| 134 | } |
| 135 | |
| 136 | fp[abs_fsize] = c; |
| 137 | abs_fsize++; |
| 138 | SIZ(f) = (fsize > 0 ? abs_fsize : - abs_fsize); |
| 139 | EXP(f)++; |
| 140 | } |
| 141 | } |
| 142 | |
| 143 | /* Fill f with size limbs of the given value, setup as an integer. */ |
| 144 | void |
| 145 | refmpf_fill (mpf_ptr f, mp_size_t size, mp_limb_t value) |
| 146 | { |
| 147 | ASSERT (size >= 0); |
| 148 | size = MIN (PREC(f) + 1, size); |
| 149 | SIZ(f) = size; |
| 150 | EXP(f) = size; |
| 151 | refmpn_fill (PTR(f), size, value); |
| 152 | } |
| 153 | |
| 154 | /* Strip high zero limbs from the f data, adjusting exponent accordingly. */ |
| 155 | void |
| 156 | refmpf_normalize (mpf_ptr f) |
| 157 | { |
| 158 | while (SIZ(f) != 0 && PTR(f)[ABSIZ(f)-1] == 0) |
| 159 | { |
| 160 | SIZ(f) = (SIZ(f) >= 0 ? SIZ(f)-1 : SIZ(f)+1); |
| 161 | EXP(f) --; |
| 162 | } |
| 163 | if (SIZ(f) == 0) |
| 164 | EXP(f) = 0; |
| 165 | } |
| 166 | |
| 167 | /* refmpf_set_overlap sets up dst as a copy of src, but with PREC(dst) |
| 168 | unchanged, in preparation for an overlap test. |
| 169 | |
| 170 | The full value of src is copied, and the space at PTR(dst) is extended as |
| 171 | necessary. The way PREC(dst) is unchanged is as per an mpf_set_prec_raw. |
| 172 | The return value is the new PTR(dst) space precision, in bits, ready for |
| 173 | a restoring mpf_set_prec_raw before mpf_clear. */ |
| 174 | |
| 175 | unsigned long |
| 176 | refmpf_set_overlap (mpf_ptr dst, mpf_srcptr src) |
| 177 | { |
| 178 | mp_size_t dprec = PREC(dst); |
| 179 | mp_size_t ssize = ABSIZ(src); |
| 180 | unsigned long ret; |
| 181 | |
| 182 | refmpf_set_prec_limbs (dst, (unsigned long) MAX (dprec, ssize)); |
| 183 | mpf_set (dst, src); |
| 184 | |
| 185 | ret = mpf_get_prec (dst); |
| 186 | PREC(dst) = dprec; |
| 187 | return ret; |
| 188 | } |
| 189 | |
| 190 | /* Like mpf_set_prec, but taking a precision in limbs. |
| 191 | PREC(f) ends up as the given "prec" value. */ |
| 192 | void |
| 193 | refmpf_set_prec_limbs (mpf_ptr f, unsigned long prec) |
| 194 | { |
| 195 | mpf_set_prec (f, __GMPF_PREC_TO_BITS (prec)); |
| 196 | } |
| 197 | |
| 198 | |
| 199 | void |
| 200 | refmpf_sub (mpf_ptr w, mpf_srcptr u, mpf_srcptr v) |
| 201 | { |
| 202 | mp_size_t hi, lo, size; |
| 203 | mp_ptr ut, vt, wt; |
| 204 | int neg; |
| 205 | mp_exp_t exp; |
| 206 | TMP_DECL; |
| 207 | |
| 208 | TMP_MARK; |
| 209 | |
| 210 | if (SIZ (u) == 0) |
| 211 | { |
| 212 | size = ABSIZ (v); |
| 213 | wt = TMP_ALLOC_LIMBS (size + 1); |
| 214 | MPN_COPY (wt, PTR (v), size); |
| 215 | exp = EXP (v); |
| 216 | neg = SIZ (v) > 0; |
| 217 | goto done; |
| 218 | } |
| 219 | if (SIZ (v) == 0) |
| 220 | { |
| 221 | size = ABSIZ (u); |
| 222 | wt = TMP_ALLOC_LIMBS (size + 1); |
| 223 | MPN_COPY (wt, PTR (u), size); |
| 224 | exp = EXP (u); |
| 225 | neg = SIZ (u) < 0; |
| 226 | goto done; |
| 227 | } |
| 228 | if ((SIZ (u) ^ SIZ (v)) < 0) |
| 229 | { |
| 230 | mpf_t tmp; |
| 231 | SIZ (tmp) = -SIZ (v); |
| 232 | EXP (tmp) = EXP (v); |
| 233 | PTR (tmp) = PTR (v); |
| 234 | refmpf_add (w, u, tmp); |
| 235 | if (SIZ (u) < 0) |
| 236 | mpf_neg (w, w); |
| 237 | return; |
| 238 | } |
| 239 | neg = SIZ (u) < 0; |
| 240 | |
| 241 | /* Compute the significance of the hi and lo end of the result. */ |
| 242 | hi = MAX (EXP (u), EXP (v)); |
| 243 | lo = MIN (EXP (u) - ABSIZ (u), EXP (v) - ABSIZ (v)); |
| 244 | size = hi - lo; |
| 245 | ut = TMP_ALLOC_LIMBS (size + 1); |
| 246 | vt = TMP_ALLOC_LIMBS (size + 1); |
| 247 | wt = TMP_ALLOC_LIMBS (size + 1); |
| 248 | MPN_ZERO (ut, size); |
| 249 | MPN_ZERO (vt, size); |
| 250 | {int off; |
| 251 | off = size + (EXP (u) - hi) - ABSIZ (u); |
| 252 | MPN_COPY (ut + off, PTR (u), ABSIZ (u)); |
| 253 | off = size + (EXP (v) - hi) - ABSIZ (v); |
| 254 | MPN_COPY (vt + off, PTR (v), ABSIZ (v)); |
| 255 | } |
| 256 | |
| 257 | if (mpn_cmp (ut, vt, size) >= 0) |
| 258 | mpn_sub_n (wt, ut, vt, size); |
| 259 | else |
| 260 | { |
| 261 | mpn_sub_n (wt, vt, ut, size); |
| 262 | neg ^= 1; |
| 263 | } |
| 264 | exp = hi; |
| 265 | while (size != 0 && wt[size - 1] == 0) |
| 266 | { |
| 267 | size--; |
| 268 | exp--; |
| 269 | } |
| 270 | |
| 271 | done: |
| 272 | if (size > PREC (w)) |
| 273 | { |
| 274 | wt += size - PREC (w); |
| 275 | size = PREC (w); |
| 276 | } |
| 277 | MPN_COPY (PTR (w), wt, size); |
| 278 | SIZ (w) = neg == 0 ? size : -size; |
| 279 | EXP (w) = exp; |
| 280 | TMP_FREE; |
| 281 | } |
| 282 | |
| 283 | |
| 284 | /* Validate got by comparing to want. Return 1 if good, 0 if bad. |
| 285 | |
| 286 | The data in got is compared to that in want, up to either PREC(got) limbs |
| 287 | or the size of got, whichever is bigger. Clearly we always demand |
| 288 | PREC(got) of accuracy, but we go further and say that if got is bigger |
| 289 | then any extra must be correct too. |
| 290 | |
| 291 | want needs to have enough data to allow this comparison. The size in |
| 292 | want doesn't have to be that big though, if it's smaller then further low |
| 293 | limbs are taken to be zero. |
| 294 | |
| 295 | This validation approach is designed to allow some flexibility in exactly |
| 296 | how much data is generated by an mpf function, ie. either prec or prec+1 |
| 297 | limbs. We don't try to make a reference function that emulates that same |
| 298 | size decision, instead the idea is for a validation function to generate |
| 299 | at least as much data as the real function, then compare. */ |
| 300 | |
| 301 | int |
| 302 | refmpf_validate (const char *name, mpf_srcptr got, mpf_srcptr want) |
| 303 | { |
| 304 | int bad = 0; |
| 305 | mp_size_t gsize, wsize, cmpsize, i; |
| 306 | mp_srcptr gp, wp; |
| 307 | mp_limb_t glimb, wlimb; |
| 308 | |
| 309 | MPF_CHECK_FORMAT (got); |
| 310 | |
| 311 | if (EXP (got) != EXP (want)) |
| 312 | { |
| 313 | printf ("%s: wrong exponent\n", name); |
| 314 | bad = 1; |
| 315 | } |
| 316 | |
| 317 | gsize = SIZ (got); |
| 318 | wsize = SIZ (want); |
| 319 | if ((gsize < 0 && wsize > 0) || (gsize > 0 && wsize < 0)) |
| 320 | { |
| 321 | printf ("%s: wrong sign\n", name); |
| 322 | bad = 1; |
| 323 | } |
| 324 | |
| 325 | gsize = ABS (gsize); |
| 326 | wsize = ABS (wsize); |
| 327 | |
| 328 | /* most significant limb of respective data */ |
| 329 | gp = PTR (got) + gsize - 1; |
| 330 | wp = PTR (want) + wsize - 1; |
| 331 | |
| 332 | /* compare limb data */ |
| 333 | cmpsize = MAX (PREC (got), gsize); |
| 334 | for (i = 0; i < cmpsize; i++) |
| 335 | { |
| 336 | glimb = (i < gsize ? gp[-i] : 0); |
| 337 | wlimb = (i < wsize ? wp[-i] : 0); |
| 338 | |
| 339 | if (glimb != wlimb) |
| 340 | { |
| 341 | printf ("%s: wrong data starting at index %ld from top\n", |
| 342 | name, (long) i); |
| 343 | bad = 1; |
| 344 | break; |
| 345 | } |
| 346 | } |
| 347 | |
| 348 | if (bad) |
| 349 | { |
| 350 | printf (" prec %d\n", PREC(got)); |
| 351 | printf (" exp got %ld\n", (long) EXP(got)); |
| 352 | printf (" exp want %ld\n", (long) EXP(want)); |
| 353 | printf (" size got %d\n", SIZ(got)); |
| 354 | printf (" size want %d\n", SIZ(want)); |
| 355 | printf (" limbs (high to low)\n"); |
| 356 | printf (" got "); |
| 357 | for (i = ABSIZ(got)-1; i >= 0; i--) |
| 358 | { |
| 359 | gmp_printf ("%MX", PTR(got)[i]); |
| 360 | if (i != 0) |
| 361 | printf (","); |
| 362 | } |
| 363 | printf ("\n"); |
| 364 | printf (" want "); |
| 365 | for (i = ABSIZ(want)-1; i >= 0; i--) |
| 366 | { |
| 367 | gmp_printf ("%MX", PTR(want)[i]); |
| 368 | if (i != 0) |
| 369 | printf (","); |
| 370 | } |
| 371 | printf ("\n"); |
| 372 | return 0; |
| 373 | } |
| 374 | |
| 375 | return 1; |
| 376 | } |
| 377 | |
| 378 | |
| 379 | int |
| 380 | refmpf_validate_division (const char *name, mpf_srcptr got, |
| 381 | mpf_srcptr n, mpf_srcptr d) |
| 382 | { |
| 383 | mp_size_t nsize, dsize, sign, prec, qsize, tsize; |
| 384 | mp_srcptr np, dp; |
| 385 | mp_ptr tp, qp, rp; |
| 386 | mpf_t want; |
| 387 | int ret; |
| 388 | |
| 389 | nsize = SIZ (n); |
| 390 | dsize = SIZ (d); |
| 391 | ASSERT_ALWAYS (dsize != 0); |
| 392 | |
| 393 | sign = nsize ^ dsize; |
| 394 | nsize = ABS (nsize); |
| 395 | dsize = ABS (dsize); |
| 396 | |
| 397 | np = PTR (n); |
| 398 | dp = PTR (d); |
| 399 | prec = PREC (got); |
| 400 | |
| 401 | EXP (want) = EXP (n) - EXP (d) + 1; |
| 402 | |
| 403 | qsize = prec + 2; /* at least prec+1 limbs, after high zero */ |
| 404 | tsize = qsize + dsize - 1; /* dividend size to give desired qsize */ |
| 405 | |
| 406 | /* dividend n, extended or truncated */ |
| 407 | tp = refmpn_malloc_limbs (tsize); |
| 408 | refmpn_copy_extend (tp, tsize, np, nsize); |
| 409 | |
| 410 | qp = refmpn_malloc_limbs (qsize); |
| 411 | rp = refmpn_malloc_limbs (dsize); /* remainder, unused */ |
| 412 | |
| 413 | ASSERT_ALWAYS (qsize == tsize - dsize + 1); |
| 414 | refmpn_tdiv_qr (qp, rp, (mp_size_t) 0, tp, tsize, dp, dsize); |
| 415 | |
| 416 | PTR (want) = qp; |
| 417 | SIZ (want) = (sign >= 0 ? qsize : -qsize); |
| 418 | refmpf_normalize (want); |
| 419 | |
| 420 | ret = refmpf_validate (name, got, want); |
| 421 | |
| 422 | free (tp); |
| 423 | free (qp); |
| 424 | free (rp); |
| 425 | |
| 426 | return ret; |
| 427 | } |