authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-04-20 13:28:03-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-04-20 13:28:03-04:00
log1e06a74348d11e4eef456a067dc5065c1e8b1ee9
tree10c881f0f0d7eb8c1d8fdf459cc739466d4fa500
parent2ca26ffde7342f7b676507452df75fbec545f650
parentbd4421befee2157018dad88b31b684f9a5af73c2
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #8542 from LemonBoy/floating-point-is-hard-my-dude

Floating point is hard my dude

10 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,6 +23,10 @@ pub fn __extendhfsf2(a: u16) callconv(.C) f32 {
23 return @call(.{ .modifier = .always_inline }, extendXfYf2, .{ f32, f16, a });23 return @call(.{ .modifier = .always_inline }, extendXfYf2, .{ f32, f16, a });
24}24}
2525
26pub fn __extendhftf2(a: u16) callconv(.C) f128 {
27 return @call(.{ .modifier = .always_inline }, extendXfYf2, .{ f128, f16, a });
28}
29
26pub fn __aeabi_h2f(arg: u16) callconv(.AAPCS) f32 {30pub fn __aeabi_h2f(arg: u16) callconv(.AAPCS) f32 {
27 @setRuntimeSafety(false);31 @setRuntimeSafety(false);
28 return @call(.{ .modifier = .always_inline }, __extendhfsf2, .{arg});32 return @call(.{ .modifier = .always_inline }, __extendhfsf2, .{arg});
lib/std/special/compiler_rt/extendXfYf2_test.zig+48-1
...@@ -4,9 +4,10 @@...@@ -4,9 +4,10 @@
4// The MIT license requires this copyright notice to be included in all copies4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.5// and substantial portions of the software.
6const builtin = @import("builtin");6const builtin = @import("builtin");
7const __extenddftf2 = @import("extendXfYf2.zig").__extenddftf2;
8const __extendhfsf2 = @import("extendXfYf2.zig").__extendhfsf2;7const __extendhfsf2 = @import("extendXfYf2.zig").__extendhfsf2;
8const __extendhftf2 = @import("extendXfYf2.zig").__extendhftf2;
9const __extendsftf2 = @import("extendXfYf2.zig").__extendsftf2;9const __extendsftf2 = @import("extendXfYf2.zig").__extendsftf2;
10const __extenddftf2 = @import("extendXfYf2.zig").__extenddftf2;
1011
11fn test__extenddftf2(a: f64, expectedHi: u64, expectedLo: u64) void {12fn test__extenddftf2(a: f64, expectedHi: u64, expectedLo: u64) void {
12 const x = __extenddftf2(a);13 const x = __extenddftf2(a);
...@@ -161,3 +162,49 @@ fn makeNaN32(rand: u32) f32 {...@@ -161,3 +162,49 @@ fn makeNaN32(rand: u32) f32 {
161fn makeInf32() f32 {162fn makeInf32() f32 {
162 return @bitCast(f32, @as(u32, 0x7f800000));163 return @bitCast(f32, @as(u32, 0x7f800000));
163}164}
165
166fn 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
188test "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,6 +13,10 @@ pub fn __truncdfhf2(a: f64) callconv(.C) u16 {
13 return @bitCast(u16, @call(.{ .modifier = .always_inline }, truncXfYf2, .{ f16, f64, a }));13 return @bitCast(u16, @call(.{ .modifier = .always_inline }, truncXfYf2, .{ f16, f64, a }));
14}14}
1515
16pub fn __trunctfhf2(a: f128) callconv(.C) u16 {
17 return @bitCast(u16, @call(.{ .modifier = .always_inline }, truncXfYf2, .{ f16, f128, a }));
18}
19
16pub fn __trunctfsf2(a: f128) callconv(.C) f32 {20pub fn __trunctfsf2(a: f128) callconv(.C) f32 {
17 return @call(.{ .modifier = .always_inline }, truncXfYf2, .{ f32, f128, a });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,7 +126,7 @@ fn truncXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t {
122 if (shift > srcSigBits) {126 if (shift > srcSigBits) {
123 absResult = 0;127 absResult = 0;
124 } else {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 const denormalizedSignificand: src_rep_t = significand >> @intCast(SrcShift, shift) | sticky;130 const denormalizedSignificand: src_rep_t = significand >> @intCast(SrcShift, shift) | sticky;
127 absResult = @intCast(dst_rep_t, denormalizedSignificand >> (srcSigBits - dstSigBits));131 absResult = @intCast(dst_rep_t, denormalizedSignificand >> (srcSigBits - dstSigBits));
128 const roundBits: src_rep_t = denormalizedSignificand & roundMask;132 const roundBits: src_rep_t = denormalizedSignificand & roundMask;
lib/std/special/compiler_rt/truncXfYf2_test.zig+56
...@@ -242,3 +242,59 @@ test "truncdfsf2" {...@@ -242,3 +242,59 @@ test "truncdfsf2" {
242 // huge number becomes inf242 // huge number becomes inf
243 test__truncdfsf2(340282366920938463463374607431768211456.0, 0x7f800000);243 test__truncdfsf2(340282366920938463463374607431768211456.0, 0x7f800000);
244}244}
245
246const __trunctfhf2 = @import("truncXfYf2.zig").__trunctfhf2;
247
248fn 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
261test "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,6 +9,7 @@
9#include "bigint.hpp"9#include "bigint.hpp"
10#include "buffer.hpp"10#include "buffer.hpp"
11#include "softfloat.hpp"11#include "softfloat.hpp"
12#include "softfloat_ext.hpp"
12#include "parse_f128.h"13#include "parse_f128.h"
13#include <stdio.h>14#include <stdio.h>
14#include <math.h>15#include <math.h>
...@@ -60,9 +61,7 @@ void bigfloat_init_bigint(BigFloat *dest, const BigInt *op) {...@@ -60,9 +61,7 @@ void bigfloat_init_bigint(BigFloat *dest, const BigInt *op) {
6061
61 if (i == 0) {62 if (i == 0) {
62 if (op->is_negative) {63 if (op->is_negative) {
63 float128_t zero_f128;64 f128M_neg(&dest->value, &dest->value);
64 ui32_to_f128M(0, &zero_f128);
65 f128M_sub(&zero_f128, &dest->value, &dest->value);
66 }65 }
67 return;66 return;
68 }67 }
...@@ -89,9 +88,7 @@ void bigfloat_add(BigFloat *dest, const BigFloat *op1, const BigFloat *op2) {...@@ -89,9 +88,7 @@ void bigfloat_add(BigFloat *dest, const BigFloat *op1, const BigFloat *op2) {
89}88}
9089
91void bigfloat_negate(BigFloat *dest, const BigFloat *op) {90void bigfloat_negate(BigFloat *dest, const BigFloat *op) {
92 float128_t zero_f128;91 f128M_neg(&op->value, &dest->value);
93 ui32_to_f128M(0, &zero_f128);
94 f128M_sub(&zero_f128, &op->value, &dest->value);
95}92}
9693
97void bigfloat_sub(BigFloat *dest, const BigFloat *op1, const BigFloat *op2) {94void 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,7 +7436,10 @@ static LLVMValueRef gen_const_val(CodeGen *g, ZigValue *const_val, const char *n
7436 case ZigTypeIdFloat:7436 case ZigTypeIdFloat:
7437 switch (type_entry->data.floating.bit_count) {7437 switch (type_entry->data.floating.bit_count) {
7438 case 16: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 case 32:7443 case 32:
7441 return LLVMConstReal(get_llvm_type(g, type_entry), const_val->data.x_f32);7444 return LLVMConstReal(get_llvm_type(g, type_entry), const_val->data.x_f32);
7442 case 64:7445 case 64:
src/stage1/ir.cpp+3-8
...@@ -11363,11 +11363,8 @@ static void float_negate(ZigValue *out_val, ZigValue *op) {...@@ -11363,11 +11363,8 @@ static void float_negate(ZigValue *out_val, ZigValue *op) {
11363 } else if (op->type->id == ZigTypeIdFloat) {11363 } else if (op->type->id == ZigTypeIdFloat) {
11364 switch (op->type->data.floating.bit_count) {11364 switch (op->type->data.floating.bit_count) {
11365 case 16:11365 case 16:
11366 {11366 out_val->data.x_f16 = f16_neg(op->data.x_f16);
11367 const float16_t zero = zig_double_to_f16(0);11367 return;
11368 out_val->data.x_f16 = f16_sub(zero, op->data.x_f16);
11369 return;
11370 }
11371 case 32:11368 case 32:
11372 out_val->data.x_f32 = -op->data.x_f32;11369 out_val->data.x_f32 = -op->data.x_f32;
11373 return;11370 return;
...@@ -11375,9 +11372,7 @@ static void float_negate(ZigValue *out_val, ZigValue *op) {...@@ -11375,9 +11372,7 @@ static void float_negate(ZigValue *out_val, ZigValue *op) {
11375 out_val->data.x_f64 = -op->data.x_f64;11372 out_val->data.x_f64 = -op->data.x_f64;
11376 return;11373 return;
11377 case 128:11374 case 128:
11378 float128_t zero_f128;11375 f128M_neg(&op->data.x_f128, &out_val->data.x_f128);
11379 ui32_to_f128M(0, &zero_f128);
11380 f128M_sub(&zero_f128, &op->data.x_f128, &out_val->data.x_f128);
11381 return;11376 return;
11382 default:11377 default:
11383 zig_unreachable();11378 zig_unreachable();
src/stage1/softfloat_ext.cpp+13
...@@ -1,6 +1,8 @@...@@ -1,6 +1,8 @@
1#include "softfloat_ext.hpp"1#include "softfloat_ext.hpp"
22
3extern "C" {3extern "C" {
4 #include "platform.h"
5 #include "internals.h"
4 #include "softfloat.h"6 #include "softfloat.h"
5}7}
68
...@@ -22,4 +24,15 @@ void f128M_trunc(const float128_t *aPtr, float128_t *zPtr) {...@@ -22,4 +24,15 @@ void f128M_trunc(const float128_t *aPtr, float128_t *zPtr) {
22 } else {24 } else {
23 f128M_roundToInt(aPtr, softfloat_round_min, false, zPtr);25 f128M_roundToInt(aPtr, softfloat_round_min, false, zPtr);
24 } 26 }
27}
28
29float16_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
35void 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 +5,8 @@
55
6void f128M_abs(const float128_t *aPtr, float128_t *zPtr);6void f128M_abs(const float128_t *aPtr, float128_t *zPtr);
7void f128M_trunc(const float128_t *aPtr, float128_t *zPtr);7void f128M_trunc(const float128_t *aPtr, float128_t *zPtr);
8void f128M_neg(const float128_t *aPtr, float128_t *zPtr);
9
10float16_t f16_neg(const float16_t a);
811
9#endif12#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,3 +843,20 @@ test "compare undefined literal with comptime_int" {
843 x = true;843 x = true;
844 expect(x);844 expect(x);
845}845}
846
847test "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}