Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/powerpc/fpu/fpu_implode.c
39507 views
1
/* $NetBSD: fpu_implode.c,v 1.6 2005/12/11 12:18:42 christos Exp $ */
2
3
/*-
4
* SPDX-License-Identifier: BSD-3-Clause
5
*
6
* Copyright (c) 1992, 1993
7
* The Regents of the University of California. All rights reserved.
8
*
9
* This software was developed by the Computer Systems Engineering group
10
* at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
11
* contributed to Berkeley.
12
*
13
* All advertising materials mentioning features or use of this software
14
* must display the following acknowledgement:
15
* This product includes software developed by the University of
16
* California, Lawrence Berkeley Laboratory.
17
*
18
* Redistribution and use in source and binary forms, with or without
19
* modification, are permitted provided that the following conditions
20
* are met:
21
* 1. Redistributions of source code must retain the above copyright
22
* notice, this list of conditions and the following disclaimer.
23
* 2. Redistributions in binary form must reproduce the above copyright
24
* notice, this list of conditions and the following disclaimer in the
25
* documentation and/or other materials provided with the distribution.
26
* 3. Neither the name of the University nor the names of its contributors
27
* may be used to endorse or promote products derived from this software
28
* without specific prior written permission.
29
*
30
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
31
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
34
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
35
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
36
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
37
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
38
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
39
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40
* SUCH DAMAGE.
41
*/
42
43
/*
44
* FPU subroutines: `implode' internal format numbers into the machine's
45
* `packed binary' format.
46
*/
47
48
#include <sys/types.h>
49
#include <sys/systm.h>
50
51
#include <machine/fpu.h>
52
#include <machine/ieee.h>
53
#include <machine/ieeefp.h>
54
55
#include <powerpc/fpu/fpu_arith.h>
56
#include <powerpc/fpu/fpu_emu.h>
57
#include <powerpc/fpu/fpu_extern.h>
58
#include <powerpc/fpu/fpu_instr.h>
59
60
static int round(struct fpemu *, struct fpn *);
61
static int toinf(struct fpemu *, int);
62
63
/*
64
* Round a number (algorithm from Motorola MC68882 manual, modified for
65
* our internal format). Set inexact exception if rounding is required.
66
* Return true iff we rounded up.
67
*
68
* After rounding, we discard the guard and round bits by shifting right
69
* 2 bits (a la fpu_shr(), but we do not bother with fp->fp_sticky).
70
* This saves effort later.
71
*
72
* Note that we may leave the value 2.0 in fp->fp_mant; it is the caller's
73
* responsibility to fix this if necessary.
74
*/
75
static int
76
round(struct fpemu *fe, struct fpn *fp)
77
{
78
u_int m0, m1, m2, m3;
79
int gr, s;
80
FPU_DECL_CARRY;
81
82
m0 = fp->fp_mant[0];
83
m1 = fp->fp_mant[1];
84
m2 = fp->fp_mant[2];
85
m3 = fp->fp_mant[3];
86
gr = m3 & 3;
87
s = fp->fp_sticky;
88
89
/* mant >>= FP_NG */
90
m3 = (m3 >> FP_NG) | (m2 << (32 - FP_NG));
91
m2 = (m2 >> FP_NG) | (m1 << (32 - FP_NG));
92
m1 = (m1 >> FP_NG) | (m0 << (32 - FP_NG));
93
m0 >>= FP_NG;
94
95
if ((gr | s) == 0) /* result is exact: no rounding needed */
96
goto rounddown;
97
98
fe->fe_cx |= FPSCR_XX|FPSCR_FI; /* inexact */
99
100
/* Go to rounddown to round down; break to round up. */
101
switch ((fe->fe_fpscr) & FPSCR_RN) {
102
case FP_RN:
103
default:
104
/*
105
* Round only if guard is set (gr & 2). If guard is set,
106
* but round & sticky both clear, then we want to round
107
* but have a tie, so round to even, i.e., add 1 iff odd.
108
*/
109
if ((gr & 2) == 0)
110
goto rounddown;
111
if ((gr & 1) || fp->fp_sticky || (m3 & 1))
112
break;
113
goto rounddown;
114
115
case FP_RZ:
116
/* Round towards zero, i.e., down. */
117
goto rounddown;
118
119
case FP_RM:
120
/* Round towards -Inf: up if negative, down if positive. */
121
if (fp->fp_sign)
122
break;
123
goto rounddown;
124
125
case FP_RP:
126
/* Round towards +Inf: up if positive, down otherwise. */
127
if (!fp->fp_sign)
128
break;
129
goto rounddown;
130
}
131
132
/* Bump low bit of mantissa, with carry. */
133
fe->fe_cx |= FPSCR_FR;
134
135
FPU_ADDS(m3, m3, 1);
136
FPU_ADDCS(m2, m2, 0);
137
FPU_ADDCS(m1, m1, 0);
138
FPU_ADDC(m0, m0, 0);
139
fp->fp_mant[0] = m0;
140
fp->fp_mant[1] = m1;
141
fp->fp_mant[2] = m2;
142
fp->fp_mant[3] = m3;
143
return (1);
144
145
rounddown:
146
fp->fp_mant[0] = m0;
147
fp->fp_mant[1] = m1;
148
fp->fp_mant[2] = m2;
149
fp->fp_mant[3] = m3;
150
return (0);
151
}
152
153
/*
154
* For overflow: return true if overflow is to go to +/-Inf, according
155
* to the sign of the overflowing result. If false, overflow is to go
156
* to the largest magnitude value instead.
157
*/
158
static int
159
toinf(struct fpemu *fe, int sign)
160
{
161
int inf;
162
163
/* look at rounding direction */
164
switch ((fe->fe_fpscr) & FPSCR_RN) {
165
default:
166
case FP_RN: /* the nearest value is always Inf */
167
inf = 1;
168
break;
169
170
case FP_RZ: /* toward 0 => never towards Inf */
171
inf = 0;
172
break;
173
174
case FP_RP: /* toward +Inf iff positive */
175
inf = sign == 0;
176
break;
177
178
case FP_RM: /* toward -Inf iff negative */
179
inf = sign;
180
break;
181
}
182
if (inf)
183
fe->fe_cx |= FPSCR_OX;
184
return (inf);
185
}
186
187
/*
188
* fpn -> int (int value returned as return value).
189
*
190
* N.B.: this conversion always rounds towards zero (this is a peculiarity
191
* of the SPARC instruction set).
192
*/
193
u_int
194
fpu_ftoi(struct fpemu *fe, struct fpn *fp)
195
{
196
u_int i;
197
int sign, exp;
198
199
sign = fp->fp_sign;
200
switch (fp->fp_class) {
201
case FPC_ZERO:
202
return (0);
203
204
case FPC_NUM:
205
/*
206
* If exp >= 2^32, overflow. Otherwise shift value right
207
* into last mantissa word (this will not exceed 0xffffffff),
208
* shifting any guard and round bits out into the sticky
209
* bit. Then ``round'' towards zero, i.e., just set an
210
* inexact exception if sticky is set (see round()).
211
* If the result is > 0x80000000, or is positive and equals
212
* 0x80000000, overflow; otherwise the last fraction word
213
* is the result.
214
*/
215
if ((exp = fp->fp_exp) >= 32)
216
break;
217
/* NB: the following includes exp < 0 cases */
218
if (fpu_shr(fp, FP_NMANT - 1 - exp) != 0)
219
fe->fe_cx |= FPSCR_UX;
220
i = fp->fp_mant[3];
221
if (i >= ((u_int)0x80000000 + sign))
222
break;
223
return (sign ? -i : i);
224
225
default: /* Inf, qNaN, sNaN */
226
break;
227
}
228
/* overflow: replace any inexact exception with invalid */
229
fe->fe_cx |= FPSCR_VXCVI;
230
return (0x7fffffff + sign);
231
}
232
233
/*
234
* fpn -> extended int (high bits of int value returned as return value).
235
*
236
* N.B.: this conversion always rounds towards zero (this is a peculiarity
237
* of the SPARC instruction set).
238
*/
239
u_int
240
fpu_ftox(struct fpemu *fe, struct fpn *fp, u_int *res)
241
{
242
u_int64_t i;
243
int sign, exp;
244
245
sign = fp->fp_sign;
246
switch (fp->fp_class) {
247
case FPC_ZERO:
248
res[1] = 0;
249
return (0);
250
251
case FPC_NUM:
252
/*
253
* If exp >= 2^64, overflow. Otherwise shift value right
254
* into last mantissa word (this will not exceed 0xffffffffffffffff),
255
* shifting any guard and round bits out into the sticky
256
* bit. Then ``round'' towards zero, i.e., just set an
257
* inexact exception if sticky is set (see round()).
258
* If the result is > 0x8000000000000000, or is positive and equals
259
* 0x8000000000000000, overflow; otherwise the last fraction word
260
* is the result.
261
*/
262
if ((exp = fp->fp_exp) >= 64)
263
break;
264
/* NB: the following includes exp < 0 cases */
265
if (fpu_shr(fp, FP_NMANT - 1 - exp) != 0)
266
fe->fe_cx |= FPSCR_UX;
267
i = ((u_int64_t)fp->fp_mant[2]<<32)|fp->fp_mant[3];
268
if (i >= ((u_int64_t)0x8000000000000000LL + sign))
269
break;
270
return (sign ? -i : i);
271
272
default: /* Inf, qNaN, sNaN */
273
break;
274
}
275
/* overflow: replace any inexact exception with invalid */
276
fe->fe_cx |= FPSCR_VXCVI;
277
return (0x7fffffffffffffffLL + sign);
278
}
279
280
/*
281
* fpn -> single (32 bit single returned as return value).
282
* We assume <= 29 bits in a single-precision fraction (1.f part).
283
*/
284
u_int
285
fpu_ftos(struct fpemu *fe, struct fpn *fp)
286
{
287
u_int sign = fp->fp_sign << 31;
288
int exp;
289
290
#define SNG_EXP(e) ((e) << SNG_FRACBITS) /* makes e an exponent */
291
#define SNG_MASK (SNG_EXP(1) - 1) /* mask for fraction */
292
293
/* Take care of non-numbers first. */
294
if (ISNAN(fp)) {
295
/*
296
* Preserve upper bits of NaN, per SPARC V8 appendix N.
297
* Note that fp->fp_mant[0] has the quiet bit set,
298
* even if it is classified as a signalling NaN.
299
*/
300
(void) fpu_shr(fp, FP_NMANT - 1 - SNG_FRACBITS);
301
exp = SNG_EXP_INFNAN;
302
goto done;
303
}
304
if (ISINF(fp))
305
return (sign | SNG_EXP(SNG_EXP_INFNAN));
306
if (ISZERO(fp))
307
return (sign);
308
309
/*
310
* Normals (including subnormals). Drop all the fraction bits
311
* (including the explicit ``implied'' 1 bit) down into the
312
* single-precision range. If the number is subnormal, move
313
* the ``implied'' 1 into the explicit range as well, and shift
314
* right to introduce leading zeroes. Rounding then acts
315
* differently for normals and subnormals: the largest subnormal
316
* may round to the smallest normal (1.0 x 2^minexp), or may
317
* remain subnormal. In the latter case, signal an underflow
318
* if the result was inexact or if underflow traps are enabled.
319
*
320
* Rounding a normal, on the other hand, always produces another
321
* normal (although either way the result might be too big for
322
* single precision, and cause an overflow). If rounding a
323
* normal produces 2.0 in the fraction, we need not adjust that
324
* fraction at all, since both 1.0 and 2.0 are zero under the
325
* fraction mask.
326
*
327
* Note that the guard and round bits vanish from the number after
328
* rounding.
329
*/
330
if ((exp = fp->fp_exp + SNG_EXP_BIAS) <= 0) { /* subnormal */
331
/* -NG for g,r; -SNG_FRACBITS-exp for fraction */
332
(void) fpu_shr(fp, FP_NMANT - FP_NG - SNG_FRACBITS - exp);
333
if (round(fe, fp) && fp->fp_mant[3] == SNG_EXP(1))
334
return (sign | SNG_EXP(1) | 0);
335
if ((fe->fe_cx & FPSCR_FI) ||
336
(fe->fe_fpscr & FPSCR_UX))
337
fe->fe_cx |= FPSCR_UX;
338
return (sign | SNG_EXP(0) | fp->fp_mant[3]);
339
}
340
/* -FP_NG for g,r; -1 for implied 1; -SNG_FRACBITS for fraction */
341
(void) fpu_shr(fp, FP_NMANT - FP_NG - 1 - SNG_FRACBITS);
342
#ifdef DIAGNOSTIC
343
if ((fp->fp_mant[3] & SNG_EXP(1 << FP_NG)) == 0)
344
panic("fpu_ftos");
345
#endif
346
if (round(fe, fp) && fp->fp_mant[3] == SNG_EXP(2))
347
exp++;
348
if (exp >= SNG_EXP_INFNAN) {
349
/* overflow to inf or to max single */
350
if (toinf(fe, sign))
351
return (sign | SNG_EXP(SNG_EXP_INFNAN));
352
return (sign | SNG_EXP(SNG_EXP_INFNAN - 1) | SNG_MASK);
353
}
354
done:
355
/* phew, made it */
356
return (sign | SNG_EXP(exp) | (fp->fp_mant[3] & SNG_MASK));
357
}
358
359
/*
360
* fpn -> double (32 bit high-order result returned; 32-bit low order result
361
* left in res[1]). Assumes <= 61 bits in double precision fraction.
362
*
363
* This code mimics fpu_ftos; see it for comments.
364
*/
365
u_int
366
fpu_ftod(struct fpemu *fe, struct fpn *fp, u_int *res)
367
{
368
u_int sign = fp->fp_sign << 31;
369
int exp;
370
371
#define DBL_EXP(e) ((e) << (DBL_FRACBITS & 31))
372
#define DBL_MASK (DBL_EXP(1) - 1)
373
374
if (ISNAN(fp)) {
375
(void) fpu_shr(fp, FP_NMANT - 1 - DBL_FRACBITS);
376
exp = DBL_EXP_INFNAN;
377
goto done;
378
}
379
if (ISINF(fp)) {
380
sign |= DBL_EXP(DBL_EXP_INFNAN);
381
goto zero;
382
}
383
if (ISZERO(fp)) {
384
zero: res[1] = 0;
385
return (sign);
386
}
387
388
if ((exp = fp->fp_exp + DBL_EXP_BIAS) <= 0) {
389
(void) fpu_shr(fp, FP_NMANT - FP_NG - DBL_FRACBITS - exp);
390
if (round(fe, fp) && fp->fp_mant[2] == DBL_EXP(1)) {
391
res[1] = 0;
392
return (sign | DBL_EXP(1) | 0);
393
}
394
if ((fe->fe_cx & FPSCR_FI) ||
395
(fe->fe_fpscr & FPSCR_UX))
396
fe->fe_cx |= FPSCR_UX;
397
exp = 0;
398
goto done;
399
}
400
(void) fpu_shr(fp, FP_NMANT - FP_NG - 1 - DBL_FRACBITS);
401
if (round(fe, fp) && fp->fp_mant[2] == DBL_EXP(2))
402
exp++;
403
if (exp >= DBL_EXP_INFNAN) {
404
fe->fe_cx |= FPSCR_OX | FPSCR_UX;
405
if (toinf(fe, sign)) {
406
res[1] = 0;
407
return (sign | DBL_EXP(DBL_EXP_INFNAN) | 0);
408
}
409
res[1] = ~0;
410
return (sign | DBL_EXP(DBL_EXP_INFNAN) | DBL_MASK);
411
}
412
done:
413
res[1] = fp->fp_mant[3];
414
return (sign | DBL_EXP(exp) | (fp->fp_mant[2] & DBL_MASK));
415
}
416
417
/*
418
* Implode an fpn, writing the result into the given space.
419
*/
420
void
421
fpu_implode(struct fpemu *fe, struct fpn *fp, int type, u_int *space)
422
{
423
424
switch (type) {
425
case FTYPE_LNG:
426
space[0] = fpu_ftox(fe, fp, space);
427
DPRINTF(FPE_REG, ("fpu_implode: long %x %x\n",
428
space[0], space[1]));
429
break;
430
431
case FTYPE_INT:
432
space[0] = 0;
433
space[1] = fpu_ftoi(fe, fp);
434
DPRINTF(FPE_REG, ("fpu_implode: int %x\n",
435
space[1]));
436
break;
437
438
case FTYPE_SNG:
439
space[0] = fpu_ftos(fe, fp);
440
DPRINTF(FPE_REG, ("fpu_implode: single %x\n",
441
space[0]));
442
break;
443
444
case FTYPE_DBL:
445
space[0] = fpu_ftod(fe, fp, space);
446
DPRINTF(FPE_REG, ("fpu_implode: double %x %x\n",
447
space[0], space[1]));
448
break; break;
449
450
default:
451
panic("fpu_implode: invalid type %d", type);
452
}
453
}
454
455