Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | ||
3 | fp_arith.c: floating-point math routines for the Linux-m68k | |
4 | floating point emulator. | |
5 | ||
6 | Copyright (c) 1998-1999 David Huggins-Daines. | |
7 | ||
8 | Somewhat based on the AlphaLinux floating point emulator, by David | |
9 | Mosberger-Tang. | |
10 | ||
11 | You may copy, modify, and redistribute this file under the terms of | |
12 | the GNU General Public License, version 2, or any later version, at | |
13 | your convenience. | |
14 | */ | |
15 | ||
16 | #include "fp_emu.h" | |
17 | #include "multi_arith.h" | |
18 | #include "fp_arith.h" | |
19 | ||
20 | const struct fp_ext fp_QNaN = | |
21 | { | |
22 | .exp = 0x7fff, | |
23 | .mant = { .m64 = ~0 } | |
24 | }; | |
25 | ||
26 | const struct fp_ext fp_Inf = | |
27 | { | |
28 | .exp = 0x7fff, | |
29 | }; | |
30 | ||
31 | /* let's start with the easy ones */ | |
32 | ||
33 | struct fp_ext * | |
34 | fp_fabs(struct fp_ext *dest, struct fp_ext *src) | |
35 | { | |
36 | dprint(PINSTR, "fabs\n"); | |
37 | ||
38 | fp_monadic_check(dest, src); | |
39 | ||
40 | dest->sign = 0; | |
41 | ||
42 | return dest; | |
43 | } | |
44 | ||
45 | struct fp_ext * | |
46 | fp_fneg(struct fp_ext *dest, struct fp_ext *src) | |
47 | { | |
48 | dprint(PINSTR, "fneg\n"); | |
49 | ||
50 | fp_monadic_check(dest, src); | |
51 | ||
52 | dest->sign = !dest->sign; | |
53 | ||
54 | return dest; | |
55 | } | |
56 | ||
57 | /* Now, the slightly harder ones */ | |
58 | ||
59 | /* fp_fadd: Implements the kernel of the FADD, FSADD, FDADD, FSUB, | |
60 | FDSUB, and FCMP instructions. */ | |
61 | ||
62 | struct fp_ext * | |
63 | fp_fadd(struct fp_ext *dest, struct fp_ext *src) | |
64 | { | |
65 | int diff; | |
66 | ||
67 | dprint(PINSTR, "fadd\n"); | |
68 | ||
69 | fp_dyadic_check(dest, src); | |
70 | ||
71 | if (IS_INF(dest)) { | |
72 | /* infinity - infinity == NaN */ | |
73 | if (IS_INF(src) && (src->sign != dest->sign)) | |
74 | fp_set_nan(dest); | |
75 | return dest; | |
76 | } | |
77 | if (IS_INF(src)) { | |
78 | fp_copy_ext(dest, src); | |
79 | return dest; | |
80 | } | |
81 | ||
82 | if (IS_ZERO(dest)) { | |
83 | if (IS_ZERO(src)) { | |
84 | if (src->sign != dest->sign) { | |
85 | if (FPDATA->rnd == FPCR_ROUND_RM) | |
86 | dest->sign = 1; | |
87 | else | |
88 | dest->sign = 0; | |
89 | } | |
90 | } else | |
91 | fp_copy_ext(dest, src); | |
92 | return dest; | |
93 | } | |
94 | ||
95 | dest->lowmant = src->lowmant = 0; | |
96 | ||
97 | if ((diff = dest->exp - src->exp) > 0) | |
98 | fp_denormalize(src, diff); | |
99 | else if ((diff = -diff) > 0) | |
100 | fp_denormalize(dest, diff); | |
101 | ||
102 | if (dest->sign == src->sign) { | |
103 | if (fp_addmant(dest, src)) | |
104 | if (!fp_addcarry(dest)) | |
105 | return dest; | |
106 | } else { | |
107 | if (dest->mant.m64 < src->mant.m64) { | |
108 | fp_submant(dest, src, dest); | |
109 | dest->sign = !dest->sign; | |
110 | } else | |
111 | fp_submant(dest, dest, src); | |
112 | } | |
113 | ||
114 | return dest; | |
115 | } | |
116 | ||
117 | /* fp_fsub: Implements the kernel of the FSUB, FSSUB, and FDSUB | |
118 | instructions. | |
119 | ||
120 | Remember that the arguments are in assembler-syntax order! */ | |
121 | ||
122 | struct fp_ext * | |
123 | fp_fsub(struct fp_ext *dest, struct fp_ext *src) | |
124 | { | |
125 | dprint(PINSTR, "fsub "); | |
126 | ||
127 | src->sign = !src->sign; | |
128 | return fp_fadd(dest, src); | |
129 | } | |
130 | ||
131 | ||
132 | struct fp_ext * | |
133 | fp_fcmp(struct fp_ext *dest, struct fp_ext *src) | |
134 | { | |
135 | dprint(PINSTR, "fcmp "); | |
136 | ||
137 | FPDATA->temp[1] = *dest; | |
138 | src->sign = !src->sign; | |
139 | return fp_fadd(&FPDATA->temp[1], src); | |
140 | } | |
141 | ||
142 | struct fp_ext * | |
143 | fp_ftst(struct fp_ext *dest, struct fp_ext *src) | |
144 | { | |
145 | dprint(PINSTR, "ftst\n"); | |
146 | ||
147 | (void)dest; | |
148 | ||
149 | return src; | |
150 | } | |
151 | ||
152 | struct fp_ext * | |
153 | fp_fmul(struct fp_ext *dest, struct fp_ext *src) | |
154 | { | |
155 | union fp_mant128 temp; | |
156 | int exp; | |
157 | ||
158 | dprint(PINSTR, "fmul\n"); | |
159 | ||
160 | fp_dyadic_check(dest, src); | |
161 | ||
162 | /* calculate the correct sign now, as it's necessary for infinities */ | |
163 | dest->sign = src->sign ^ dest->sign; | |
164 | ||
165 | /* Handle infinities */ | |
166 | if (IS_INF(dest)) { | |
167 | if (IS_ZERO(src)) | |
168 | fp_set_nan(dest); | |
169 | return dest; | |
170 | } | |
171 | if (IS_INF(src)) { | |
172 | if (IS_ZERO(dest)) | |
173 | fp_set_nan(dest); | |
174 | else | |
175 | fp_copy_ext(dest, src); | |
176 | return dest; | |
177 | } | |
178 | ||
179 | /* Of course, as we all know, zero * anything = zero. You may | |
180 | not have known that it might be a positive or negative | |
181 | zero... */ | |
182 | if (IS_ZERO(dest) || IS_ZERO(src)) { | |
183 | dest->exp = 0; | |
184 | dest->mant.m64 = 0; | |
185 | dest->lowmant = 0; | |
186 | ||
187 | return dest; | |
188 | } | |
189 | ||
190 | exp = dest->exp + src->exp - 0x3ffe; | |
191 | ||
192 | /* shift up the mantissa for denormalized numbers, | |
193 | so that the highest bit is set, this makes the | |
194 | shift of the result below easier */ | |
195 | if ((long)dest->mant.m32[0] >= 0) | |
196 | exp -= fp_overnormalize(dest); | |
197 | if ((long)src->mant.m32[0] >= 0) | |
198 | exp -= fp_overnormalize(src); | |
199 | ||
200 | /* now, do a 64-bit multiply with expansion */ | |
201 | fp_multiplymant(&temp, dest, src); | |
202 | ||
203 | /* normalize it back to 64 bits and stuff it back into the | |
204 | destination struct */ | |
205 | if ((long)temp.m32[0] > 0) { | |
206 | exp--; | |
207 | fp_putmant128(dest, &temp, 1); | |
208 | } else | |
209 | fp_putmant128(dest, &temp, 0); | |
210 | ||
211 | if (exp >= 0x7fff) { | |
212 | fp_set_ovrflw(dest); | |
213 | return dest; | |
214 | } | |
215 | dest->exp = exp; | |
216 | if (exp < 0) { | |
217 | fp_set_sr(FPSR_EXC_UNFL); | |
218 | fp_denormalize(dest, -exp); | |
219 | } | |
220 | ||
221 | return dest; | |
222 | } | |
223 | ||
224 | /* fp_fdiv: Implements the "kernel" of the FDIV, FSDIV, FDDIV and | |
225 | FSGLDIV instructions. | |
226 | ||
227 | Note that the order of the operands is counter-intuitive: instead | |
228 | of src / dest, the result is actually dest / src. */ | |
229 | ||
230 | struct fp_ext * | |
231 | fp_fdiv(struct fp_ext *dest, struct fp_ext *src) | |
232 | { | |
233 | union fp_mant128 temp; | |
234 | int exp; | |
235 | ||
236 | dprint(PINSTR, "fdiv\n"); | |
237 | ||
238 | fp_dyadic_check(dest, src); | |
239 | ||
240 | /* calculate the correct sign now, as it's necessary for infinities */ | |
241 | dest->sign = src->sign ^ dest->sign; | |
242 | ||
243 | /* Handle infinities */ | |
244 | if (IS_INF(dest)) { | |
245 | /* infinity / infinity = NaN (quiet, as always) */ | |
246 | if (IS_INF(src)) | |
247 | fp_set_nan(dest); | |
248 | /* infinity / anything else = infinity (with approprate sign) */ | |
249 | return dest; | |
250 | } | |
251 | if (IS_INF(src)) { | |
252 | /* anything / infinity = zero (with appropriate sign) */ | |
253 | dest->exp = 0; | |
254 | dest->mant.m64 = 0; | |
255 | dest->lowmant = 0; | |
256 | ||
257 | return dest; | |
258 | } | |
259 | ||
260 | /* zeroes */ | |
261 | if (IS_ZERO(dest)) { | |
262 | /* zero / zero = NaN */ | |
263 | if (IS_ZERO(src)) | |
264 | fp_set_nan(dest); | |
265 | /* zero / anything else = zero */ | |
266 | return dest; | |
267 | } | |
268 | if (IS_ZERO(src)) { | |
269 | /* anything / zero = infinity (with appropriate sign) */ | |
270 | fp_set_sr(FPSR_EXC_DZ); | |
271 | dest->exp = 0x7fff; | |
272 | dest->mant.m64 = 0; | |
273 | ||
274 | return dest; | |
275 | } | |
276 | ||
277 | exp = dest->exp - src->exp + 0x3fff; | |
278 | ||
279 | /* shift up the mantissa for denormalized numbers, | |
280 | so that the highest bit is set, this makes lots | |
281 | of things below easier */ | |
282 | if ((long)dest->mant.m32[0] >= 0) | |
283 | exp -= fp_overnormalize(dest); | |
284 | if ((long)src->mant.m32[0] >= 0) | |
285 | exp -= fp_overnormalize(src); | |
286 | ||
287 | /* now, do the 64-bit divide */ | |
288 | fp_dividemant(&temp, dest, src); | |
289 | ||
290 | /* normalize it back to 64 bits and stuff it back into the | |
291 | destination struct */ | |
292 | if (!temp.m32[0]) { | |
293 | exp--; | |
294 | fp_putmant128(dest, &temp, 32); | |
295 | } else | |
296 | fp_putmant128(dest, &temp, 31); | |
297 | ||
298 | if (exp >= 0x7fff) { | |
299 | fp_set_ovrflw(dest); | |
300 | return dest; | |
301 | } | |
302 | dest->exp = exp; | |
303 | if (exp < 0) { | |
304 | fp_set_sr(FPSR_EXC_UNFL); | |
305 | fp_denormalize(dest, -exp); | |
306 | } | |
307 | ||
308 | return dest; | |
309 | } | |
310 | ||
311 | struct fp_ext * | |
312 | fp_fsglmul(struct fp_ext *dest, struct fp_ext *src) | |
313 | { | |
314 | int exp; | |
315 | ||
316 | dprint(PINSTR, "fsglmul\n"); | |
317 | ||
318 | fp_dyadic_check(dest, src); | |
319 | ||
320 | /* calculate the correct sign now, as it's necessary for infinities */ | |
321 | dest->sign = src->sign ^ dest->sign; | |
322 | ||
323 | /* Handle infinities */ | |
324 | if (IS_INF(dest)) { | |
325 | if (IS_ZERO(src)) | |
326 | fp_set_nan(dest); | |
327 | return dest; | |
328 | } | |
329 | if (IS_INF(src)) { | |
330 | if (IS_ZERO(dest)) | |
331 | fp_set_nan(dest); | |
332 | else | |
333 | fp_copy_ext(dest, src); | |
334 | return dest; | |
335 | } | |
336 | ||
337 | /* Of course, as we all know, zero * anything = zero. You may | |
338 | not have known that it might be a positive or negative | |
339 | zero... */ | |
340 | if (IS_ZERO(dest) || IS_ZERO(src)) { | |
341 | dest->exp = 0; | |
342 | dest->mant.m64 = 0; | |
343 | dest->lowmant = 0; | |
344 | ||
345 | return dest; | |
346 | } | |
347 | ||
348 | exp = dest->exp + src->exp - 0x3ffe; | |
349 | ||
350 | /* do a 32-bit multiply */ | |
351 | fp_mul64(dest->mant.m32[0], dest->mant.m32[1], | |
352 | dest->mant.m32[0] & 0xffffff00, | |
353 | src->mant.m32[0] & 0xffffff00); | |
354 | ||
355 | if (exp >= 0x7fff) { | |
356 | fp_set_ovrflw(dest); | |
357 | return dest; | |
358 | } | |
359 | dest->exp = exp; | |
360 | if (exp < 0) { | |
361 | fp_set_sr(FPSR_EXC_UNFL); | |
362 | fp_denormalize(dest, -exp); | |
363 | } | |
364 | ||
365 | return dest; | |
366 | } | |
367 | ||
368 | struct fp_ext * | |
369 | fp_fsgldiv(struct fp_ext *dest, struct fp_ext *src) | |
370 | { | |
371 | int exp; | |
372 | unsigned long quot, rem; | |
373 | ||
374 | dprint(PINSTR, "fsgldiv\n"); | |
375 | ||
376 | fp_dyadic_check(dest, src); | |
377 | ||
378 | /* calculate the correct sign now, as it's necessary for infinities */ | |
379 | dest->sign = src->sign ^ dest->sign; | |
380 | ||
381 | /* Handle infinities */ | |
382 | if (IS_INF(dest)) { | |
383 | /* infinity / infinity = NaN (quiet, as always) */ | |
384 | if (IS_INF(src)) | |
385 | fp_set_nan(dest); | |
386 | /* infinity / anything else = infinity (with approprate sign) */ | |
387 | return dest; | |
388 | } | |
389 | if (IS_INF(src)) { | |
390 | /* anything / infinity = zero (with appropriate sign) */ | |
391 | dest->exp = 0; | |
392 | dest->mant.m64 = 0; | |
393 | dest->lowmant = 0; | |
394 | ||
395 | return dest; | |
396 | } | |
397 | ||
398 | /* zeroes */ | |
399 | if (IS_ZERO(dest)) { | |
400 | /* zero / zero = NaN */ | |
401 | if (IS_ZERO(src)) | |
402 | fp_set_nan(dest); | |
403 | /* zero / anything else = zero */ | |
404 | return dest; | |
405 | } | |
406 | if (IS_ZERO(src)) { | |
407 | /* anything / zero = infinity (with appropriate sign) */ | |
408 | fp_set_sr(FPSR_EXC_DZ); | |
409 | dest->exp = 0x7fff; | |
410 | dest->mant.m64 = 0; | |
411 | ||
412 | return dest; | |
413 | } | |
414 | ||
415 | exp = dest->exp - src->exp + 0x3fff; | |
416 | ||
417 | dest->mant.m32[0] &= 0xffffff00; | |
418 | src->mant.m32[0] &= 0xffffff00; | |
419 | ||
420 | /* do the 32-bit divide */ | |
421 | if (dest->mant.m32[0] >= src->mant.m32[0]) { | |
422 | fp_sub64(dest->mant, src->mant); | |
423 | fp_div64(quot, rem, dest->mant.m32[0], 0, src->mant.m32[0]); | |
424 | dest->mant.m32[0] = 0x80000000 | (quot >> 1); | |
425 | dest->mant.m32[1] = (quot & 1) | rem; /* only for rounding */ | |
426 | } else { | |
427 | fp_div64(quot, rem, dest->mant.m32[0], 0, src->mant.m32[0]); | |
428 | dest->mant.m32[0] = quot; | |
429 | dest->mant.m32[1] = rem; /* only for rounding */ | |
430 | exp--; | |
431 | } | |
432 | ||
433 | if (exp >= 0x7fff) { | |
434 | fp_set_ovrflw(dest); | |
435 | return dest; | |
436 | } | |
437 | dest->exp = exp; | |
438 | if (exp < 0) { | |
439 | fp_set_sr(FPSR_EXC_UNFL); | |
440 | fp_denormalize(dest, -exp); | |
441 | } | |
442 | ||
443 | return dest; | |
444 | } | |
445 | ||
446 | /* fp_roundint: Internal rounding function for use by several of these | |
447 | emulated instructions. | |
448 | ||
449 | This one rounds off the fractional part using the rounding mode | |
450 | specified. */ | |
451 | ||
452 | static void fp_roundint(struct fp_ext *dest, int mode) | |
453 | { | |
454 | union fp_mant64 oldmant; | |
455 | unsigned long mask; | |
456 | ||
457 | if (!fp_normalize_ext(dest)) | |
458 | return; | |
459 | ||
460 | /* infinities and zeroes */ | |
461 | if (IS_INF(dest) || IS_ZERO(dest)) | |
462 | return; | |
463 | ||
464 | /* first truncate the lower bits */ | |
465 | oldmant = dest->mant; | |
466 | switch (dest->exp) { | |
467 | case 0 ... 0x3ffe: | |
468 | dest->mant.m64 = 0; | |
469 | break; | |
470 | case 0x3fff ... 0x401e: | |
471 | dest->mant.m32[0] &= 0xffffffffU << (0x401e - dest->exp); | |
472 | dest->mant.m32[1] = 0; | |
473 | if (oldmant.m64 == dest->mant.m64) | |
474 | return; | |
475 | break; | |
476 | case 0x401f ... 0x403e: | |
477 | dest->mant.m32[1] &= 0xffffffffU << (0x403e - dest->exp); | |
478 | if (oldmant.m32[1] == dest->mant.m32[1]) | |
479 | return; | |
480 | break; | |
481 | default: | |
482 | return; | |
483 | } | |
484 | fp_set_sr(FPSR_EXC_INEX2); | |
485 | ||
486 | /* We might want to normalize upwards here... however, since | |
487 | we know that this is only called on the output of fp_fdiv, | |
488 | or with the input to fp_fint or fp_fintrz, and the inputs | |
489 | to all these functions are either normal or denormalized | |
490 | (no subnormals allowed!), there's really no need. | |
491 | ||
492 | In the case of fp_fdiv, observe that 0x80000000 / 0xffff = | |
493 | 0xffff8000, and the same holds for 128-bit / 64-bit. (i.e. the | |
494 | smallest possible normal dividend and the largest possible normal | |
495 | divisor will still produce a normal quotient, therefore, (normal | |
496 | << 64) / normal is normal in all cases) */ | |
497 | ||
498 | switch (mode) { | |
499 | case FPCR_ROUND_RN: | |
500 | switch (dest->exp) { | |
501 | case 0 ... 0x3ffd: | |
502 | return; | |
503 | case 0x3ffe: | |
504 | /* As noted above, the input is always normal, so the | |
505 | guard bit (bit 63) is always set. therefore, the | |
506 | only case in which we will NOT round to 1.0 is when | |
507 | the input is exactly 0.5. */ | |
508 | if (oldmant.m64 == (1ULL << 63)) | |
509 | return; | |
510 | break; | |
511 | case 0x3fff ... 0x401d: | |
512 | mask = 1 << (0x401d - dest->exp); | |
513 | if (!(oldmant.m32[0] & mask)) | |
514 | return; | |
515 | if (oldmant.m32[0] & (mask << 1)) | |
516 | break; | |
517 | if (!(oldmant.m32[0] << (dest->exp - 0x3ffd)) && | |
518 | !oldmant.m32[1]) | |
519 | return; | |
520 | break; | |
521 | case 0x401e: | |
ddc2fc2c | 522 | if (oldmant.m32[1] & 0x80000000) |
1da177e4 LT |
523 | return; |
524 | if (oldmant.m32[0] & 1) | |
525 | break; | |
526 | if (!(oldmant.m32[1] << 1)) | |
527 | return; | |
528 | break; | |
529 | case 0x401f ... 0x403d: | |
530 | mask = 1 << (0x403d - dest->exp); | |
531 | if (!(oldmant.m32[1] & mask)) | |
532 | return; | |
533 | if (oldmant.m32[1] & (mask << 1)) | |
534 | break; | |
535 | if (!(oldmant.m32[1] << (dest->exp - 0x401d))) | |
536 | return; | |
537 | break; | |
538 | default: | |
539 | return; | |
540 | } | |
541 | break; | |
542 | case FPCR_ROUND_RZ: | |
543 | return; | |
544 | default: | |
545 | if (dest->sign ^ (mode - FPCR_ROUND_RM)) | |
546 | break; | |
547 | return; | |
548 | } | |
549 | ||
550 | switch (dest->exp) { | |
551 | case 0 ... 0x3ffe: | |
552 | dest->exp = 0x3fff; | |
553 | dest->mant.m64 = 1ULL << 63; | |
554 | break; | |
555 | case 0x3fff ... 0x401e: | |
556 | mask = 1 << (0x401e - dest->exp); | |
557 | if (dest->mant.m32[0] += mask) | |
558 | break; | |
559 | dest->mant.m32[0] = 0x80000000; | |
560 | dest->exp++; | |
561 | break; | |
562 | case 0x401f ... 0x403e: | |
563 | mask = 1 << (0x403e - dest->exp); | |
564 | if (dest->mant.m32[1] += mask) | |
565 | break; | |
566 | if (dest->mant.m32[0] += 1) | |
567 | break; | |
568 | dest->mant.m32[0] = 0x80000000; | |
569 | dest->exp++; | |
570 | break; | |
571 | } | |
572 | } | |
573 | ||
574 | /* modrem_kernel: Implementation of the FREM and FMOD instructions | |
575 | (which are exactly the same, except for the rounding used on the | |
576 | intermediate value) */ | |
577 | ||
578 | static struct fp_ext * | |
579 | modrem_kernel(struct fp_ext *dest, struct fp_ext *src, int mode) | |
580 | { | |
581 | struct fp_ext tmp; | |
582 | ||
583 | fp_dyadic_check(dest, src); | |
584 | ||
585 | /* Infinities and zeros */ | |
586 | if (IS_INF(dest) || IS_ZERO(src)) { | |
587 | fp_set_nan(dest); | |
588 | return dest; | |
589 | } | |
590 | if (IS_ZERO(dest) || IS_INF(src)) | |
591 | return dest; | |
592 | ||
593 | /* FIXME: there is almost certainly a smarter way to do this */ | |
594 | fp_copy_ext(&tmp, dest); | |
595 | fp_fdiv(&tmp, src); /* NOTE: src might be modified */ | |
596 | fp_roundint(&tmp, mode); | |
597 | fp_fmul(&tmp, src); | |
598 | fp_fsub(dest, &tmp); | |
599 | ||
600 | /* set the quotient byte */ | |
601 | fp_set_quotient((dest->mant.m64 & 0x7f) | (dest->sign << 7)); | |
602 | return dest; | |
603 | } | |
604 | ||
605 | /* fp_fmod: Implements the kernel of the FMOD instruction. | |
606 | ||
607 | Again, the argument order is backwards. The result, as defined in | |
608 | the Motorola manuals, is: | |
609 | ||
610 | fmod(src,dest) = (dest - (src * floor(dest / src))) */ | |
611 | ||
612 | struct fp_ext * | |
613 | fp_fmod(struct fp_ext *dest, struct fp_ext *src) | |
614 | { | |
615 | dprint(PINSTR, "fmod\n"); | |
616 | return modrem_kernel(dest, src, FPCR_ROUND_RZ); | |
617 | } | |
618 | ||
619 | /* fp_frem: Implements the kernel of the FREM instruction. | |
620 | ||
621 | frem(src,dest) = (dest - (src * round(dest / src))) | |
622 | */ | |
623 | ||
624 | struct fp_ext * | |
625 | fp_frem(struct fp_ext *dest, struct fp_ext *src) | |
626 | { | |
627 | dprint(PINSTR, "frem\n"); | |
628 | return modrem_kernel(dest, src, FPCR_ROUND_RN); | |
629 | } | |
630 | ||
631 | struct fp_ext * | |
632 | fp_fint(struct fp_ext *dest, struct fp_ext *src) | |
633 | { | |
634 | dprint(PINSTR, "fint\n"); | |
635 | ||
636 | fp_copy_ext(dest, src); | |
637 | ||
638 | fp_roundint(dest, FPDATA->rnd); | |
639 | ||
640 | return dest; | |
641 | } | |
642 | ||
643 | struct fp_ext * | |
644 | fp_fintrz(struct fp_ext *dest, struct fp_ext *src) | |
645 | { | |
646 | dprint(PINSTR, "fintrz\n"); | |
647 | ||
648 | fp_copy_ext(dest, src); | |
649 | ||
650 | fp_roundint(dest, FPCR_ROUND_RZ); | |
651 | ||
652 | return dest; | |
653 | } | |
654 | ||
655 | struct fp_ext * | |
656 | fp_fscale(struct fp_ext *dest, struct fp_ext *src) | |
657 | { | |
658 | int scale, oldround; | |
659 | ||
660 | dprint(PINSTR, "fscale\n"); | |
661 | ||
662 | fp_dyadic_check(dest, src); | |
663 | ||
664 | /* Infinities */ | |
665 | if (IS_INF(src)) { | |
666 | fp_set_nan(dest); | |
667 | return dest; | |
668 | } | |
669 | if (IS_INF(dest)) | |
670 | return dest; | |
671 | ||
672 | /* zeroes */ | |
673 | if (IS_ZERO(src) || IS_ZERO(dest)) | |
674 | return dest; | |
675 | ||
676 | /* Source exponent out of range */ | |
677 | if (src->exp >= 0x400c) { | |
678 | fp_set_ovrflw(dest); | |
679 | return dest; | |
680 | } | |
681 | ||
682 | /* src must be rounded with round to zero. */ | |
683 | oldround = FPDATA->rnd; | |
684 | FPDATA->rnd = FPCR_ROUND_RZ; | |
685 | scale = fp_conv_ext2long(src); | |
686 | FPDATA->rnd = oldround; | |
687 | ||
688 | /* new exponent */ | |
689 | scale += dest->exp; | |
690 | ||
691 | if (scale >= 0x7fff) { | |
692 | fp_set_ovrflw(dest); | |
693 | } else if (scale <= 0) { | |
694 | fp_set_sr(FPSR_EXC_UNFL); | |
695 | fp_denormalize(dest, -scale); | |
696 | } else | |
697 | dest->exp = scale; | |
698 | ||
699 | return dest; | |
700 | } | |
701 |