1const std = @import("std");
2
3const compiler_rt = @import("../compiler_rt.zig");
4const symbol = compiler_rt.symbol;
5
6comptime {
7 if (compiler_rt.want_aeabi) {
8 if (compiler_rt.gnu_f16_abi) {
9 symbol(&__aeabi_h2f, "__gnu_h2f_ieee");
10 } else {
11 symbol(&__aeabi_h2f, "__aeabi_h2f");
12 }
13 } else if (compiler_rt.gnu_f16_abi) {
14 symbol(&__extendhfsf2, "__gnu_h2f_ieee");
15 }
16 symbol(&__extendhfsf2, "__extendhfsf2");
17 symbol(&__extendhfdf2, "__extendhfdf2");
18 symbol(&__extendhfxf2, "__extendhfxf2");
19 if (compiler_rt.want_ppc_abi) {
20 symbol(&__extendhftf2, "__extendhfkf2");
21 } else {
22 symbol(&__extendhftf2, "__extendhftf2");
23 }
24
25 if (compiler_rt.want_aeabi) {
26 symbol(&__aeabi_f2d, "__aeabi_f2d");
27 } else {
28 symbol(&__extendsfdf2, "__extendsfdf2");
29 }
30 symbol(&__extendsfxf2, "__extendsfxf2");
31 if (compiler_rt.want_ppc_abi) {
32 symbol(&__extendsftf2, "__extendsfkf2");
33 } else if (compiler_rt.want_sparc64_abi) {
34 symbol(&_Qp_stoq, "_Qp_stoq");
35 } else if (compiler_rt.want_sparc32_abi) {
36 symbol(&__extendsftf2, "_Q_stoq");
37 } else {
38 symbol(&__extendsftf2, "__extendsftf2");
39 }
40
41 symbol(&__extenddfxf2, "__extenddfxf2");
42 if (compiler_rt.want_ppc_abi) {
43 symbol(&__extenddftf2, "__extenddfkf2");
44 } else if (compiler_rt.want_sparc64_abi) {
45 symbol(&_Qp_dtoq, "_Qp_dtoq");
46 } else if (compiler_rt.want_sparc32_abi) {
47 symbol(&__extenddftf2, "_Q_dtoq");
48 } else {
49 symbol(&__extenddftf2, "__extenddftf2");
50 }
51
52 if (compiler_rt.want_ppc_abi) {
53 symbol(&__extendxftf2, "__extendxfkf2");
54 } else {
55 symbol(&__extendxftf2, "__extendxftf2");
56 }
57}
58
59fn __extendhfsf2(a: compiler_rt.f16Conv(f32).Abi) callconv(.c) compiler_rt.f32.Abi {
60 return compiler_rt.f32.toAbi(f32_floatCast_f16(compiler_rt.f16Conv(f32).fromAbi(a)));
61}
62fn __aeabi_h2f(a: u16) callconv(.{ .arm_aapcs = .{} }) u32 {
63 return @bitCast(f32_floatCast_f16(@bitCast(a)));
64}
65pub fn f32_floatCast_f16(a: f16) f32 {
66 return extendf(f32, f16, a);
67}
68
69fn __extendhfdf2(a: compiler_rt.f16Conv(f64).Abi) callconv(.c) compiler_rt.f64.Abi {
70 return compiler_rt.f64.toAbi(f64_floatCast_f16(compiler_rt.f16Conv(f64).fromAbi(a)));
71}
72pub fn f64_floatCast_f16(a: f16) f64 {
73 return extendf(f64, f16, a);
74}
75
76fn __extendhfxf2(a: compiler_rt.f16Conv(f80).Abi) callconv(.c) compiler_rt.f80.Abi {
77 return compiler_rt.f80.toAbi(f80_floatCast_f16(compiler_rt.f16Conv(f80).fromAbi(a)));
78}
79pub fn f80_floatCast_f16(a: f16) f80 {
80 return extend_f80(f16, a);
81}
82
83fn __extendhftf2(a: compiler_rt.f16Conv(f128).Abi) callconv(.c) compiler_rt.f128.Abi {
84 return compiler_rt.f128.toAbi(f128_floatCast_f16(compiler_rt.f16Conv(f128).fromAbi(a)));
85}
86pub fn f128_floatCast_f16(a: f16) f128 {
87 return extendf(f128, f16, a);
88}
89
90fn __extendsfdf2(a: compiler_rt.f32.Abi) callconv(.c) compiler_rt.f64.Abi {
91 return compiler_rt.f64.toAbi(f64_floatCast_f32(compiler_rt.f32.fromAbi(a)));
92}
93fn __aeabi_f2d(a: f32) callconv(.{ .arm_aapcs = .{} }) f64 {
94 return f64_floatCast_f32(a);
95}
96pub fn f64_floatCast_f32(a: f32) f64 {
97 return extendf(f64, f32, a);
98}
99
100fn __extendsfxf2(a: compiler_rt.f32.Abi) callconv(.c) compiler_rt.f80.Abi {
101 return compiler_rt.f80.toAbi(f80_floatCast_f32(compiler_rt.f32.fromAbi(a)));
102}
103pub fn f80_floatCast_f32(a: f32) f80 {
104 return extend_f80(f32, a);
105}
106
107pub fn __extendsftf2(a: compiler_rt.f32.Abi) callconv(.c) compiler_rt.f128.Abi {
108 return compiler_rt.f128.toAbi(f128_floatCast_f32(compiler_rt.f32.fromAbi(a)));
109}
110fn _Qp_stoq(c: *f128, a: f32) callconv(.c) void {
111 c.* = f128_floatCast_f32(a);
112}
113pub fn f128_floatCast_f32(a: f32) f128 {
114 return extendf(f128, f32, a);
115}
116
117fn __extenddfxf2(a: compiler_rt.f64.Abi) callconv(.c) compiler_rt.f80.Abi {
118 return compiler_rt.f80.toAbi(f80_floatCast_f64(compiler_rt.f64.fromAbi(a)));
119}
120pub fn f80_floatCast_f64(a: f64) f80 {
121 return extend_f80(f64, a);
122}
123
124fn __extenddftf2(a: compiler_rt.f64.Abi) callconv(.c) compiler_rt.f128.Abi {
125 return compiler_rt.f128.toAbi(f128_floatCast_f64(compiler_rt.f64.fromAbi(a)));
126}
127fn _Qp_dtoq(c: *f128, a: f64) callconv(.c) void {
128 c.* = f128_floatCast_f64(a);
129}
130pub fn f128_floatCast_f64(a: f64) f128 {
131 return extendf(f128, f64, a);
132}
133
134fn __extendxftf2(a: compiler_rt.f80.Abi) callconv(.c) compiler_rt.f128.Abi {
135 return compiler_rt.f128.toAbi(f128_floatCast_f80(compiler_rt.f80.fromAbi(a)));
136}
137pub fn f128_floatCast_f80(a: f80) f128 {
138 const src_int_bit: u64 = 0x8000000000000000;
139 const src_sig_mask = ~src_int_bit;
140 const src_sig_bits = std.math.floatMantissaBits(f80) - 1; // -1 for the integer bit
141 const dst_sig_bits = std.math.floatMantissaBits(f128);
142
143 const dst_bits = @bitSizeOf(f128);
144
145 // Break a into a sign and representation of the absolute value
146 var a_rep: std.math.F80 = .fromFloat(a);
147 const sign = a_rep.exp & 0x8000;
148 a_rep.exp &= 0x7FFF;
149 var abs_result: u128 = undefined;
150
151 if (a_rep.exp == 0 and a_rep.fraction == 0) {
152 // zero
153 abs_result = 0;
154 } else if (a_rep.exp == 0x7FFF) {
155 // a is nan or infinite
156 abs_result = @as(u128, a_rep.fraction) << (dst_sig_bits - src_sig_bits);
157 abs_result |= @as(u128, a_rep.exp) << dst_sig_bits;
158 } else if (a_rep.fraction & src_int_bit != 0) {
159 // a is a normal value
160 abs_result = @as(u128, a_rep.fraction & src_sig_mask) << (dst_sig_bits - src_sig_bits);
161 abs_result |= @as(u128, a_rep.exp) << dst_sig_bits;
162 } else {
163 // a is denormal
164 abs_result = @as(u128, a_rep.fraction) << (dst_sig_bits - src_sig_bits);
165 }
166
167 // Apply the signbit to (dst_t)abs(a).
168 const result: u128 = abs_result | @as(u128, sign) << (dst_bits - 16);
169 return @bitCast(result);
170}
171
172inline fn extendf(comptime dst_t: type, comptime src_t: type, f: src_t) dst_t {
173 const src_rep_t = @Int(.unsigned, @typeInfo(src_t).float.bits);
174 const dst_rep_t = @Int(.unsigned, @typeInfo(dst_t).float.bits);
175 const srcSigBits = std.math.floatMantissaBits(src_t);
176 const dstSigBits = std.math.floatMantissaBits(dst_t);
177
178 // Various constants whose values follow from the type parameters.
179 // Any reasonable optimizer will fold and propagate all of these.
180 const srcBits = @bitSizeOf(src_t);
181 const srcExpBits = srcBits - srcSigBits - 1;
182 const srcInfExp = (1 << srcExpBits) - 1;
183 const srcExpBias = srcInfExp >> 1;
184
185 const srcMinNormal = 1 << srcSigBits;
186 const srcInfinity = srcInfExp << srcSigBits;
187 const srcSignMask = 1 << (srcSigBits + srcExpBits);
188 const srcAbsMask = srcSignMask - 1;
189 const srcQNaN = 1 << (srcSigBits - 1);
190 const srcNaNCode = srcQNaN - 1;
191
192 const dstBits = @bitSizeOf(dst_t);
193 const dstExpBits = dstBits - dstSigBits - 1;
194 const dstInfExp = (1 << dstExpBits) - 1;
195 const dstExpBias = dstInfExp >> 1;
196
197 const dstMinNormal: dst_rep_t = @as(dst_rep_t, 1) << dstSigBits;
198
199 const a: src_rep_t = @bitCast(f);
200 // Break a into a sign and representation of the absolute value
201 const aRep: src_rep_t = @bitCast(a);
202 const aAbs: src_rep_t = aRep & srcAbsMask;
203 const sign: src_rep_t = aRep & srcSignMask;
204 var absResult: dst_rep_t = undefined;
205
206 if (aAbs -% srcMinNormal < srcInfinity - srcMinNormal) {
207 // a is a normal number.
208 // Extend to the destination type by shifting the significand and
209 // exponent into the proper position and rebiasing the exponent.
210 absResult = @as(dst_rep_t, aAbs) << (dstSigBits - srcSigBits);
211 absResult += (dstExpBias - srcExpBias) << dstSigBits;
212 } else if (aAbs >= srcInfinity) {
213 // a is NaN or infinity.
214 // Conjure the result by beginning with infinity, then setting the qNaN
215 // bit (if needed) and right-aligning the rest of the trailing NaN
216 // payload field.
217 absResult = dstInfExp << dstSigBits;
218 absResult |= @as(dst_rep_t, aAbs & srcQNaN) << (dstSigBits - srcSigBits);
219 absResult |= @as(dst_rep_t, aAbs & srcNaNCode) << (dstSigBits - srcSigBits);
220 } else if (aAbs != 0) {
221 // a is denormal.
222 // renormalize the significand and clear the leading bit, then insert
223 // the correct adjusted exponent in the destination type.
224 const scale: u32 = @clz(aAbs) - @clz(@as(src_rep_t, srcMinNormal));
225 absResult = @as(dst_rep_t, aAbs) << @intCast(dstSigBits - srcSigBits + scale);
226 absResult ^= dstMinNormal;
227 const resultExponent: u32 = dstExpBias - srcExpBias - scale + 1;
228 absResult |= @as(dst_rep_t, @intCast(resultExponent)) << dstSigBits;
229 } else {
230 // a is zero.
231 absResult = 0;
232 }
233
234 // Apply the signbit to (dst_t)abs(a).
235 const result: dst_rep_t = absResult | @as(dst_rep_t, sign) << (dstBits - srcBits);
236 return @bitCast(result);
237}
238
239inline fn extend_f80(comptime src_t: type, f: src_t) f80 {
240 const src_rep_t = @Int(.unsigned, @typeInfo(src_t).float.bits);
241 const src_sig_bits = std.math.floatMantissaBits(src_t);
242 const dst_int_bit = 0x8000000000000000;
243 const dst_sig_bits = std.math.floatMantissaBits(f80) - 1; // -1 for the integer bit
244
245 const dst_exp_bias = 16383;
246
247 const src_bits = @bitSizeOf(src_t);
248 const src_exp_bits = src_bits - src_sig_bits - 1;
249 const src_inf_exp = (1 << src_exp_bits) - 1;
250 const src_exp_bias = src_inf_exp >> 1;
251
252 const src_min_normal = 1 << src_sig_bits;
253 const src_inf = src_inf_exp << src_sig_bits;
254 const src_sign_mask = 1 << (src_sig_bits + src_exp_bits);
255 const src_abs_mask = src_sign_mask - 1;
256 const src_qnan = 1 << (src_sig_bits - 1);
257 const src_nan_code = src_qnan - 1;
258
259 var dst: std.math.F80 = undefined;
260
261 const a: src_rep_t = @bitCast(f);
262 // Break a into a sign and representation of the absolute value
263 const a_abs = a & src_abs_mask;
264 const sign: u16 = if (a & src_sign_mask != 0) 0x8000 else 0;
265
266 if (a_abs -% src_min_normal < src_inf - src_min_normal) {
267 // a is a normal number.
268 // Extend to the destination type by shifting the significand and
269 // exponent into the proper position and rebiasing the exponent.
270 dst.exp = @intCast(a_abs >> src_sig_bits);
271 dst.exp += dst_exp_bias - src_exp_bias;
272 dst.fraction = @as(u64, a_abs) << (dst_sig_bits - src_sig_bits);
273 dst.fraction |= dst_int_bit; // bit 64 is always set for normal numbers
274 } else if (a_abs >= src_inf) {
275 // a is NaN or infinity.
276 // Conjure the result by beginning with infinity, then setting the qNaN
277 // bit (if needed) and right-aligning the rest of the trailing NaN
278 // payload field.
279 dst.exp = 0x7fff;
280 dst.fraction = dst_int_bit;
281 dst.fraction |= @as(u64, a_abs & src_qnan) << (dst_sig_bits - src_sig_bits);
282 dst.fraction |= @as(u64, a_abs & src_nan_code) << (dst_sig_bits - src_sig_bits);
283 } else if (a_abs != 0) {
284 // a is denormal.
285 // renormalize the significand and clear the leading bit, then insert
286 // the correct adjusted exponent in the destination type.
287 const scale: u16 = @clz(a_abs) - @clz(@as(src_rep_t, src_min_normal));
288
289 dst.fraction = @as(u64, a_abs) << @intCast(dst_sig_bits - src_sig_bits + scale);
290 dst.fraction |= dst_int_bit; // bit 64 is always set for normal numbers
291 dst.exp = @truncate(a_abs >> @intCast(src_sig_bits - scale));
292 dst.exp ^= 1;
293 dst.exp |= dst_exp_bias - src_exp_bias - scale + 1;
294 } else {
295 // a is zero.
296 dst.exp = 0;
297 dst.fraction = 0;
298 }
299
300 dst.exp |= sign;
301 return dst.toFloat();
302}
303
304test {
305 _ = @import("extendf_test.zig");
306}