| author | |
| committer | |
| log | 1e06a74348d11e4eef456a067dc5065c1e8b1ee9 |
| tree | 10c881f0f0d7eb8c1d8fdf459cc739466d4fa500 |
| parent | 2ca26ffde7342f7b676507452df75fbec545f650 |
| parent | bd4421befee2157018dad88b31b684f9a5af73c2 |
| signature |
Floating point is hard my dude10 files changed, 156 insertions(+), 17 deletions(-)
lib/std/special/compiler_rt/extendXfYf2.zig+4| ... | ... | @@ -23,6 +23,10 @@ pub fn __extendhfsf2(a: u16) callconv(.C) f32 { |
| 23 | 23 | return @call(.{ .modifier = .always_inline }, extendXfYf2, .{ f32, f16, a }); |
| 24 | 24 | } |
| 25 | 25 | |
| 26 | pub fn __extendhftf2(a: u16) callconv(.C) f128 { | |
| 27 | return @call(.{ .modifier = .always_inline }, extendXfYf2, .{ f128, f16, a }); | |
| 28 | } | |
| 29 | ||
| 26 | 30 | pub fn __aeabi_h2f(arg: u16) callconv(.AAPCS) f32 { |
| 27 | 31 | @setRuntimeSafety(false); |
| 28 | 32 | return @call(.{ .modifier = .always_inline }, __extendhfsf2, .{arg}); |
lib/std/special/compiler_rt/extendXfYf2_test.zig+48-1| ... | ... | @@ -4,9 +4,10 @@ |
| 4 | 4 | // The MIT license requires this copyright notice to be included in all copies |
| 5 | 5 | // and substantial portions of the software. |
| 6 | 6 | const builtin = @import("builtin"); |
| 7 | const __extenddftf2 = @import("extendXfYf2.zig").__extenddftf2; | |
| 8 | 7 | const __extendhfsf2 = @import("extendXfYf2.zig").__extendhfsf2; |
| 8 | const __extendhftf2 = @import("extendXfYf2.zig").__extendhftf2; | |
| 9 | 9 | const __extendsftf2 = @import("extendXfYf2.zig").__extendsftf2; |
| 10 | const __extenddftf2 = @import("extendXfYf2.zig").__extenddftf2; | |
| 10 | 11 | |
| 11 | 12 | fn test__extenddftf2(a: f64, expectedHi: u64, expectedLo: u64) void { |
| 12 | 13 | const x = __extenddftf2(a); |
| ... | ... | @@ -161,3 +162,49 @@ fn makeNaN32(rand: u32) f32 { |
| 161 | 162 | fn makeInf32() f32 { |
| 162 | 163 | return @bitCast(f32, @as(u32, 0x7f800000)); |
| 163 | 164 | } |
| 165 | ||
| 166 | fn test__extendhftf2(a: u16, expectedHi: u64, expectedLo: u64) void { | |
| 167 | const x = __extendhftf2(a); | |
| 168 | ||
| 169 | const rep = @bitCast(u128, x); | |
| 170 | const hi = @intCast(u64, rep >> 64); | |
| 171 | const lo = @truncate(u64, rep); | |
| 172 | ||
| 173 | if (hi == expectedHi and lo == expectedLo) | |
| 174 | return; | |
| 175 | ||
| 176 | // test other possible NaN representation(signal NaN) | |
| 177 | if (expectedHi == 0x7fff800000000000 and expectedLo == 0x0) { | |
| 178 | if ((hi & 0x7fff000000000000) == 0x7fff000000000000 and | |
| 179 | ((hi & 0xffffffffffff) > 0 or lo > 0)) | |
| 180 | { | |
| 181 | return; | |
| 182 | } | |
| 183 | } | |
| 184 | ||
| 185 | @panic("__extendhftf2 test failure"); | |
| 186 | } | |
| 187 | ||
| 188 | test "extendhftf2" { | |
| 189 | // qNaN | |
| 190 | test__extendhftf2(0x7e00, 0x7fff800000000000, 0x0); | |
| 191 | // NaN | |
| 192 | test__extendhftf2(0x7d00, 0x7fff400000000000, 0x0); | |
| 193 | // inf | |
| 194 | test__extendhftf2(0x7c00, 0x7fff000000000000, 0x0); | |
| 195 | test__extendhftf2(0xfc00, 0xffff000000000000, 0x0); | |
| 196 | // zero | |
| 197 | test__extendhftf2(0x0000, 0x0000000000000000, 0x0); | |
| 198 | test__extendhftf2(0x8000, 0x8000000000000000, 0x0); | |
| 199 | // denormal | |
| 200 | test__extendhftf2(0x0010, 0x3feb000000000000, 0x0); | |
| 201 | test__extendhftf2(0x0001, 0x3fe7000000000000, 0x0); | |
| 202 | test__extendhftf2(0x8001, 0xbfe7000000000000, 0x0); | |
| 203 | ||
| 204 | // pi | |
| 205 | test__extendhftf2(0x4248, 0x4000920000000000, 0x0); | |
| 206 | test__extendhftf2(0xc248, 0xc000920000000000, 0x0); | |
| 207 | ||
| 208 | test__extendhftf2(0x508c, 0x4004230000000000, 0x0); | |
| 209 | test__extendhftf2(0x1bb7, 0x3ff6edc000000000, 0x0); | |
| 210 | } |
lib/std/special/compiler_rt/truncXfYf2.zig+5-1| ... | ... | @@ -13,6 +13,10 @@ pub fn __truncdfhf2(a: f64) callconv(.C) u16 { |
| 13 | 13 | return @bitCast(u16, @call(.{ .modifier = .always_inline }, truncXfYf2, .{ f16, f64, a })); |
| 14 | 14 | } |
| 15 | 15 | |
| 16 | pub fn __trunctfhf2(a: f128) callconv(.C) u16 { | |
| 17 | return @bitCast(u16, @call(.{ .modifier = .always_inline }, truncXfYf2, .{ f16, f128, a })); | |
| 18 | } | |
| 19 | ||
| 16 | 20 | pub fn __trunctfsf2(a: f128) callconv(.C) f32 { |
| 17 | 21 | return @call(.{ .modifier = .always_inline }, truncXfYf2, .{ f32, f128, a }); |
| 18 | 22 | } |
| ... | ... | @@ -122,7 +126,7 @@ fn truncXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t { |
| 122 | 126 | if (shift > srcSigBits) { |
| 123 | 127 | absResult = 0; |
| 124 | 128 | } else { |
| 125 | const sticky: src_rep_t = significand << @intCast(SrcShift, srcBits - shift); | |
| 129 | const sticky: src_rep_t = @boolToInt(significand << @intCast(SrcShift, srcBits - shift) != 0); | |
| 126 | 130 | const denormalizedSignificand: src_rep_t = significand >> @intCast(SrcShift, shift) | sticky; |
| 127 | 131 | absResult = @intCast(dst_rep_t, denormalizedSignificand >> (srcSigBits - dstSigBits)); |
| 128 | 132 | const roundBits: src_rep_t = denormalizedSignificand & roundMask; |
lib/std/special/compiler_rt/truncXfYf2_test.zig+56| ... | ... | @@ -242,3 +242,59 @@ test "truncdfsf2" { |
| 242 | 242 | // huge number becomes inf |
| 243 | 243 | test__truncdfsf2(340282366920938463463374607431768211456.0, 0x7f800000); |
| 244 | 244 | } |
| 245 | ||
| 246 | const __trunctfhf2 = @import("truncXfYf2.zig").__trunctfhf2; | |
| 247 | ||
| 248 | fn test__trunctfhf2(a: f128, expected: u16) void { | |
| 249 | const x = __trunctfhf2(a); | |
| 250 | ||
| 251 | const rep = @bitCast(u16, x); | |
| 252 | if (rep == expected) { | |
| 253 | return; | |
| 254 | } | |
| 255 | ||
| 256 | @import("std").debug.warn("got 0x{x} wanted 0x{x}\n", .{ rep, expected }); | |
| 257 | ||
| 258 | @panic("__trunctfhf2 test failure"); | |
| 259 | } | |
| 260 | ||
| 261 | test "trunctfhf2" { | |
| 262 | // qNaN | |
| 263 | test__trunctfhf2(@bitCast(f128, @as(u128, 0x7fff8000000000000000000000000000)), 0x7e00); | |
| 264 | // NaN | |
| 265 | test__trunctfhf2(@bitCast(f128, @as(u128, 0x7fff0000000000000000000000000001)), 0x7e00); | |
| 266 | // inf | |
| 267 | test__trunctfhf2(@bitCast(f128, @as(u128, 0x7fff0000000000000000000000000000)), 0x7c00); | |
| 268 | test__trunctfhf2(-@bitCast(f128, @as(u128, 0x7fff0000000000000000000000000000)), 0xfc00); | |
| 269 | // zero | |
| 270 | test__trunctfhf2(0.0, 0x0); | |
| 271 | test__trunctfhf2(-0.0, 0x8000); | |
| 272 | ||
| 273 | test__trunctfhf2(3.1415926535, 0x4248); | |
| 274 | test__trunctfhf2(-3.1415926535, 0xc248); | |
| 275 | test__trunctfhf2(0x1.987124876876324p+100, 0x7c00); | |
| 276 | test__trunctfhf2(0x1.987124876876324p+12, 0x6e62); | |
| 277 | test__trunctfhf2(0x1.0p+0, 0x3c00); | |
| 278 | test__trunctfhf2(0x1.0p-14, 0x0400); | |
| 279 | // denormal | |
| 280 | test__trunctfhf2(0x1.0p-20, 0x0010); | |
| 281 | test__trunctfhf2(0x1.0p-24, 0x0001); | |
| 282 | test__trunctfhf2(-0x1.0p-24, 0x8001); | |
| 283 | test__trunctfhf2(0x1.5p-25, 0x0001); | |
| 284 | // and back to zero | |
| 285 | test__trunctfhf2(0x1.0p-25, 0x0000); | |
| 286 | test__trunctfhf2(-0x1.0p-25, 0x8000); | |
| 287 | // max (precise) | |
| 288 | test__trunctfhf2(65504.0, 0x7bff); | |
| 289 | // max (rounded) | |
| 290 | test__trunctfhf2(65519.0, 0x7bff); | |
| 291 | // max (to +inf) | |
| 292 | test__trunctfhf2(65520.0, 0x7c00); | |
| 293 | test__trunctfhf2(65536.0, 0x7c00); | |
| 294 | test__trunctfhf2(-65520.0, 0xfc00); | |
| 295 | ||
| 296 | test__trunctfhf2(0x1.23a2abb4a2ddee355f36789abcdep+5, 0x508f); | |
| 297 | test__trunctfhf2(0x1.e3d3c45bd3abfd98b76a54cc321fp-9, 0x1b8f); | |
| 298 | test__trunctfhf2(0x1.234eebb5faa678f4488693abcdefp+453, 0x7c00); | |
| 299 | test__trunctfhf2(0x1.edcba9bb8c76a5a43dd21f334634p-43, 0x0); | |
| 300 | } |
src/stage1/bigfloat.cpp+3-6| ... | ... | @@ -9,6 +9,7 @@ |
| 9 | 9 | #include "bigint.hpp" |
| 10 | 10 | #include "buffer.hpp" |
| 11 | 11 | #include "softfloat.hpp" |
| 12 | #include "softfloat_ext.hpp" | |
| 12 | 13 | #include "parse_f128.h" |
| 13 | 14 | #include <stdio.h> |
| 14 | 15 | #include <math.h> |
| ... | ... | @@ -60,9 +61,7 @@ void bigfloat_init_bigint(BigFloat *dest, const BigInt *op) { |
| 60 | 61 | |
| 61 | 62 | if (i == 0) { |
| 62 | 63 | if (op->is_negative) { |
| 63 | float128_t zero_f128; | |
| 64 | ui32_to_f128M(0, &zero_f128); | |
| 65 | f128M_sub(&zero_f128, &dest->value, &dest->value); | |
| 64 | f128M_neg(&dest->value, &dest->value); | |
| 66 | 65 | } |
| 67 | 66 | return; |
| 68 | 67 | } |
| ... | ... | @@ -89,9 +88,7 @@ void bigfloat_add(BigFloat *dest, const BigFloat *op1, const BigFloat *op2) { |
| 89 | 88 | } |
| 90 | 89 | |
| 91 | 90 | void bigfloat_negate(BigFloat *dest, const BigFloat *op) { |
| 92 | float128_t zero_f128; | |
| 93 | ui32_to_f128M(0, &zero_f128); | |
| 94 | f128M_sub(&zero_f128, &op->value, &dest->value); | |
| 91 | f128M_neg(&op->value, &dest->value); | |
| 95 | 92 | } |
| 96 | 93 | |
| 97 | 94 | void bigfloat_sub(BigFloat *dest, const BigFloat *op1, const BigFloat *op2) { |
src/stage1/codegen.cpp+4-1| ... | ... | @@ -7436,7 +7436,10 @@ static LLVMValueRef gen_const_val(CodeGen *g, ZigValue *const_val, const char *n |
| 7436 | 7436 | case ZigTypeIdFloat: |
| 7437 | 7437 | switch (type_entry->data.floating.bit_count) { |
| 7438 | 7438 | case 16: |
| 7439 | return LLVMConstReal(get_llvm_type(g, type_entry), zig_f16_to_double(const_val->data.x_f16)); | |
| 7439 | { | |
| 7440 | LLVMValueRef as_int = LLVMConstInt(LLVMInt16Type(), const_val->data.x_f16.v, false); | |
| 7441 | return LLVMConstBitCast(as_int, get_llvm_type(g, type_entry)); | |
| 7442 | } | |
| 7440 | 7443 | case 32: |
| 7441 | 7444 | return LLVMConstReal(get_llvm_type(g, type_entry), const_val->data.x_f32); |
| 7442 | 7445 | case 64: |
src/stage1/ir.cpp+3-8| ... | ... | @@ -11363,11 +11363,8 @@ static void float_negate(ZigValue *out_val, ZigValue *op) { |
| 11363 | 11363 | } else if (op->type->id == ZigTypeIdFloat) { |
| 11364 | 11364 | switch (op->type->data.floating.bit_count) { |
| 11365 | 11365 | case 16: |
| 11366 | { | |
| 11367 | const float16_t zero = zig_double_to_f16(0); | |
| 11368 | out_val->data.x_f16 = f16_sub(zero, op->data.x_f16); | |
| 11369 | return; | |
| 11370 | } | |
| 11366 | out_val->data.x_f16 = f16_neg(op->data.x_f16); | |
| 11367 | return; | |
| 11371 | 11368 | case 32: |
| 11372 | 11369 | out_val->data.x_f32 = -op->data.x_f32; |
| 11373 | 11370 | return; |
| ... | ... | @@ -11375,9 +11372,7 @@ static void float_negate(ZigValue *out_val, ZigValue *op) { |
| 11375 | 11372 | out_val->data.x_f64 = -op->data.x_f64; |
| 11376 | 11373 | return; |
| 11377 | 11374 | case 128: |
| 11378 | float128_t zero_f128; | |
| 11379 | ui32_to_f128M(0, &zero_f128); | |
| 11380 | f128M_sub(&zero_f128, &op->data.x_f128, &out_val->data.x_f128); | |
| 11375 | f128M_neg(&op->data.x_f128, &out_val->data.x_f128); | |
| 11381 | 11376 | return; |
| 11382 | 11377 | default: |
| 11383 | 11378 | zig_unreachable(); |
src/stage1/softfloat_ext.cpp+13| ... | ... | @@ -1,6 +1,8 @@ |
| 1 | 1 | #include "softfloat_ext.hpp" |
| 2 | 2 | |
| 3 | 3 | extern "C" { |
| 4 | #include "platform.h" | |
| 5 | #include "internals.h" | |
| 4 | 6 | #include "softfloat.h" |
| 5 | 7 | } |
| 6 | 8 | |
| ... | ... | @@ -22,4 +24,15 @@ void f128M_trunc(const float128_t *aPtr, float128_t *zPtr) { |
| 22 | 24 | } else { |
| 23 | 25 | f128M_roundToInt(aPtr, softfloat_round_min, false, zPtr); |
| 24 | 26 | } |
| 27 | } | |
| 28 | ||
| 29 | float16_t f16_neg(const float16_t a) { | |
| 30 | union ui16_f16 uZ; | |
| 31 | uZ.ui = a.v ^ (UINT16_C(1) << 15); | |
| 32 | return uZ.f; | |
| 33 | } | |
| 34 | ||
| 35 | void f128M_neg(const float128_t *aPtr, float128_t *zPtr) { | |
| 36 | zPtr->v[indexWord(2,1)] = aPtr->v[indexWord(2,1)] ^ (UINT64_C(1) << 63); | |
| 37 | zPtr->v[indexWord(2,0)] = aPtr->v[indexWord(2,0)]; | |
| 25 | 38 | } |
| \ No newline at end of file |
src/stage1/softfloat_ext.hpp+3| ... | ... | @@ -5,5 +5,8 @@ |
| 5 | 5 | |
| 6 | 6 | void f128M_abs(const float128_t *aPtr, float128_t *zPtr); |
| 7 | 7 | void f128M_trunc(const float128_t *aPtr, float128_t *zPtr); |
| 8 | void f128M_neg(const float128_t *aPtr, float128_t *zPtr); | |
| 9 | ||
| 10 | float16_t f16_neg(const float16_t a); | |
| 8 | 11 | |
| 9 | 12 | #endif |
| \ No newline at end of file |
test/stage1/behavior/math.zig+17| ... | ... | @@ -843,3 +843,20 @@ test "compare undefined literal with comptime_int" { |
| 843 | 843 | x = true; |
| 844 | 844 | expect(x); |
| 845 | 845 | } |
| 846 | ||
| 847 | test "signed zeros are represented properly" { | |
| 848 | const S = struct { | |
| 849 | fn doTheTest() void { | |
| 850 | inline for ([_]type{ f16, f32, f64, f128 }) |T| { | |
| 851 | const ST = std.meta.Int(.unsigned, @typeInfo(T).Float.bits); | |
| 852 | var as_fp_val = -@as(T, 0.0); | |
| 853 | var as_uint_val = @bitCast(ST, as_fp_val); | |
| 854 | // Ensure the sign bit is set. | |
| 855 | expect(as_uint_val >> (@typeInfo(T).Float.bits - 1) == 1); | |
| 856 | } | |
| 857 | } | |
| 858 | }; | |
| 859 | ||
| 860 | S.doTheTest(); | |
| 861 | comptime S.doTheTest(); | |
| 862 | } |