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