| 1 | const std = @import("std"); |
| 2 | |
| 3 | const compiler_rt = @import("../compiler_rt.zig"); |
| 4 | const symbol = compiler_rt.symbol; |
| 5 | |
| 6 | comptime { |
| 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 | |
| 59 | fn __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 | } |
| 62 | fn __aeabi_h2f(a: u16) callconv(.{ .arm_aapcs = .{} }) u32 { |
| 63 | return @bitCast(f32_floatCast_f16(@bitCast(a))); |
| 64 | } |
| 65 | pub fn f32_floatCast_f16(a: f16) f32 { |
| 66 | return extendf(f32, f16, a); |
| 67 | } |
| 68 | |
| 69 | fn __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 | } |
| 72 | pub fn f64_floatCast_f16(a: f16) f64 { |
| 73 | return extendf(f64, f16, a); |
| 74 | } |
| 75 | |
| 76 | fn __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 | } |
| 79 | pub fn f80_floatCast_f16(a: f16) f80 { |
| 80 | return extend_f80(f16, a); |
| 81 | } |
| 82 | |
| 83 | fn __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 | } |
| 86 | pub fn f128_floatCast_f16(a: f16) f128 { |
| 87 | return extendf(f128, f16, a); |
| 88 | } |
| 89 | |
| 90 | fn __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 | } |
| 93 | fn __aeabi_f2d(a: f32) callconv(.{ .arm_aapcs = .{} }) f64 { |
| 94 | return f64_floatCast_f32(a); |
| 95 | } |
| 96 | pub fn f64_floatCast_f32(a: f32) f64 { |
| 97 | return extendf(f64, f32, a); |
| 98 | } |
| 99 | |
| 100 | fn __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 | } |
| 103 | pub fn f80_floatCast_f32(a: f32) f80 { |
| 104 | return extend_f80(f32, a); |
| 105 | } |
| 106 | |
| 107 | pub 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 | } |
| 110 | fn _Qp_stoq(c: *f128, a: f32) callconv(.c) void { |
| 111 | c.* = f128_floatCast_f32(a); |
| 112 | } |
| 113 | pub fn f128_floatCast_f32(a: f32) f128 { |
| 114 | return extendf(f128, f32, a); |
| 115 | } |
| 116 | |
| 117 | fn __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 | } |
| 120 | pub fn f80_floatCast_f64(a: f64) f80 { |
| 121 | return extend_f80(f64, a); |
| 122 | } |
| 123 | |
| 124 | fn __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 | } |
| 127 | fn _Qp_dtoq(c: *f128, a: f64) callconv(.c) void { |
| 128 | c.* = f128_floatCast_f64(a); |
| 129 | } |
| 130 | pub fn f128_floatCast_f64(a: f64) f128 { |
| 131 | return extendf(f128, f64, a); |
| 132 | } |
| 133 | |
| 134 | fn __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 | } |
| 137 | pub 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 | |
| 172 | inline 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 | |
| 239 | inline 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 | |
| 304 | test { |
| 305 | _ = @import("extendf_test.zig"); |
| 306 | } |