xref: /netbsd-src/external/gpl3/gcc.old/dist/libgcc/config/arm/fp16.c (revision 867d70fc718005c0918b8b8b2f9d7f2d52d0a0db)
1 /* Half-float conversion routines.
2 
3    Copyright (C) 2008-2019 Free Software Foundation, Inc.
4    Contributed by CodeSourcery.
5 
6    This file is free software; you can redistribute it and/or modify it
7    under the terms of the GNU General Public License as published by the
8    Free Software Foundation; either version 3, or (at your option) any
9    later version.
10 
11    This file is distributed in the hope that it will be useful, but
12    WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    General Public License for more details.
15 
16    Under Section 7 of GPL version 3, you are granted additional
17    permissions described in the GCC Runtime Library Exception, version
18    3.1, as published by the Free Software Foundation.
19 
20    You should have received a copy of the GNU General Public License and
21    a copy of the GCC Runtime Library Exception along with this program;
22    see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
23    <http://www.gnu.org/licenses/>.  */
24 
25 struct format
26 {
27   /* Number of bits.  */
28   unsigned long long size;
29   /* Exponent bias.  */
30   unsigned long long bias;
31   /* Exponent width in bits.  */
32   unsigned long long exponent;
33   /* Significand precision in explicitly stored bits.  */
34   unsigned long long significand;
35 };
36 
37 static const struct format
38 binary32 =
39 {
40   32,   /* size.  */
41   127,  /* bias.  */
42   8,    /* exponent.  */
43   23    /* significand.  */
44 };
45 
46 static const struct format
47 binary64 =
48 {
49   64,    /* size.  */
50   1023,  /* bias.  */
51   11,    /* exponent.  */
52   52     /* significand.  */
53 };
54 
55 static inline unsigned short
56 __gnu_float2h_internal (const struct format* fmt,
57 			unsigned long long a, int ieee)
58 {
59   unsigned long long point = 1ULL << fmt->significand;
60   unsigned short sign = (a >> (fmt->size - 16)) & 0x8000;
61   int aexp;
62   unsigned long long mantissa;
63   unsigned long long mask;
64   unsigned long long increment;
65 
66   /* Get the exponent and mantissa encodings.  */
67   mantissa = a & (point - 1);
68 
69   mask = (1 << fmt->exponent) - 1;
70   aexp = (a >> fmt->significand) & mask;
71 
72   /* Infinity, NaN and alternative format special case.  */
73   if (((unsigned int) aexp) == mask)
74     {
75       if (!ieee)
76 	return sign;
77       if (mantissa == 0)
78 	return sign | 0x7c00;	/* Infinity.  */
79       /* Remaining cases are NaNs.  Convert SNaN to QNaN.  */
80       return sign | 0x7e00 | (mantissa >> (fmt->significand - 10));
81     }
82 
83   /* Zero.  */
84   if (aexp == 0 && mantissa == 0)
85     return sign;
86 
87   /* Construct the exponent and mantissa.  */
88   aexp -= fmt->bias;
89 
90   /* Decimal point is immediately after the significand.  */
91   mantissa |= point;
92 
93   if (aexp < -14)
94     {
95       mask = point | (point - 1);
96       /* Minimum exponent for half-precision is 2^-24.  */
97       if (aexp >= -25)
98 	mask >>= 25 + aexp;
99     }
100   else
101     mask = (point - 1) >> 10;
102 
103   /* Round.  */
104   if (mantissa & mask)
105     {
106       increment = (mask + 1) >> 1;
107       if ((mantissa & mask) == increment)
108 	increment = mantissa & (increment << 1);
109       mantissa += increment;
110       if (mantissa >= (point << 1))
111 	{
112 	  mantissa >>= 1;
113 	  aexp++;
114 	}
115     }
116 
117   if (ieee)
118     {
119       if (aexp > 15)
120 	return sign | 0x7c00;
121     }
122   else
123     {
124       if (aexp > 16)
125 	return sign | 0x7fff;
126     }
127 
128   if (aexp < -24)
129     return sign;
130 
131   if (aexp < -14)
132     {
133       mantissa >>= -14 - aexp;
134       aexp = -14;
135     }
136 
137   /* Encode the final 16-bit floating-point value.
138 
139      This is formed of the sign bit, the bias-adjusted exponent, and the
140      calculated mantissa, with the following caveats:
141 
142      1.  The mantissa calculated after rounding could have a leading 1.
143 	 To compensate for this, subtract one from the exponent bias (15)
144 	 before adding it to the calculated exponent.
145      2.  When we were calculating rounding, we left the mantissa with the
146 	 number of bits of the source operand, it needs reduced to ten
147 	 bits (+1 for the afforementioned leading 1) by shifting right by
148 	 the number of bits in the source mantissa - 10.
149      3.  To ensure the leading 1 in the mantissa is applied to the exponent
150 	 we need to add the mantissa rather than apply an arithmetic "or"
151 	 to it.  */
152 
153   return sign | (((aexp + 14) << 10) + (mantissa >> (fmt->significand - 10)));
154 }
155 
156 static inline unsigned short
157 __gnu_f2h_internal (unsigned int a, int ieee)
158 {
159   return __gnu_float2h_internal (&binary32, (unsigned long long) a, ieee);
160 }
161 
162 static inline unsigned short
163 __gnu_d2h_internal (unsigned long long a, int ieee)
164 {
165   return __gnu_float2h_internal (&binary64, a, ieee);
166 }
167 
168 unsigned int
169 __gnu_h2f_internal(unsigned short a, int ieee)
170 {
171   unsigned int sign = (unsigned int)(a & 0x8000) << 16;
172   int aexp = (a >> 10) & 0x1f;
173   unsigned int mantissa = a & 0x3ff;
174 
175   if (aexp == 0x1f && ieee)
176     return sign | 0x7f800000 | (mantissa << 13);
177 
178   if (aexp == 0)
179     {
180       int shift;
181 
182       if (mantissa == 0)
183 	return sign;
184 
185       shift = __builtin_clz(mantissa) - 21;
186       mantissa <<= shift;
187       aexp = -shift;
188     }
189 
190   return sign | (((aexp + 0x70) << 23) + (mantissa << 13));
191 }
192 
193 unsigned short
194 __gnu_f2h_ieee(unsigned int a)
195 {
196   return __gnu_f2h_internal(a, 1);
197 }
198 
199 unsigned int
200 __gnu_h2f_ieee(unsigned short a)
201 {
202   return __gnu_h2f_internal(a, 1);
203 }
204 
205 unsigned short
206 __gnu_f2h_alternative(unsigned int x)
207 {
208   return __gnu_f2h_internal(x, 0);
209 }
210 
211 unsigned int
212 __gnu_h2f_alternative(unsigned short a)
213 {
214   return __gnu_h2f_internal(a, 0);
215 }
216 
217 unsigned short
218 __gnu_d2h_ieee (unsigned long long a)
219 {
220   return __gnu_d2h_internal (a, 1);
221 }
222 
223 unsigned short
224 __gnu_d2h_alternative (unsigned long long x)
225 {
226   return __gnu_d2h_internal (x, 0);
227 }
228