authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-07-02 16:03:25-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2018-07-02 16:03:25-04:00
logbd282d6cca226c0b02af193f9c3cf0da5f6ea46d
tree5286470c43ef6c7f6530bf8518b0356e44a6ca3a
parent22b7312460c89f8fc9dad9674776d4ca3b40acd7
parent30cfc0ab2c56ad73dca9b9935731d10010c93b32
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #1176 from bnoordhuis/f16-std

improve std.math f16 support

14 files changed, 191 insertions(+), 2 deletions(-)

std/math/copysign.zig+18
......@@ -4,12 +4,22 @@ const assert = std.debug.assert;
44
55pub fn copysign(comptime T: type, x: T, y: T) T {
66 return switch (T) {
7 f16 => copysign16(x, y),
78 f32 => copysign32(x, y),
89 f64 => copysign64(x, y),
910 else => @compileError("copysign not implemented for " ++ @typeName(T)),
1011 };
1112}
1213
14fn copysign16(x: f16, y: f16) f16 {
15 const ux = @bitCast(u16, x);
16 const uy = @bitCast(u16, y);
17
18 const h1 = ux & (@maxValue(u16) / 2);
19 const h2 = uy & (u16(1) << 15);
20 return @bitCast(f16, h1 | h2);
21}
22
1323fn copysign32(x: f32, y: f32) f32 {
1424 const ux = @bitCast(u32, x);
1525 const uy = @bitCast(u32, y);
......@@ -29,10 +39,18 @@ fn copysign64(x: f64, y: f64) f64 {
2939}
3040
3141test "math.copysign" {
42 assert(copysign(f16, 1.0, 1.0) == copysign16(1.0, 1.0));
3243 assert(copysign(f32, 1.0, 1.0) == copysign32(1.0, 1.0));
3344 assert(copysign(f64, 1.0, 1.0) == copysign64(1.0, 1.0));
3445}
3546
47test "math.copysign16" {
48 assert(copysign16(5.0, 1.0) == 5.0);
49 assert(copysign16(5.0, -1.0) == -5.0);
50 assert(copysign16(-5.0, -1.0) == -5.0);
51 assert(copysign16(-5.0, 1.0) == 5.0);
52}
53
3654test "math.copysign32" {
3755 assert(copysign32(5.0, 1.0) == 5.0);
3856 assert(copysign32(5.0, -1.0) == -5.0);
std/math/fabs.zig+19
......@@ -10,12 +10,19 @@ const assert = std.debug.assert;
1010pub fn fabs(x: var) @typeOf(x) {
1111 const T = @typeOf(x);
1212 return switch (T) {
13 f16 => fabs16(x),
1314 f32 => fabs32(x),
1415 f64 => fabs64(x),
1516 else => @compileError("fabs not implemented for " ++ @typeName(T)),
1617 };
1718}
1819
20fn fabs16(x: f16) f16 {
21 var u = @bitCast(u16, x);
22 u &= 0x7FFF;
23 return @bitCast(f16, u);
24}
25
1926fn fabs32(x: f32) f32 {
2027 var u = @bitCast(u32, x);
2128 u &= 0x7FFFFFFF;
......@@ -29,10 +36,16 @@ fn fabs64(x: f64) f64 {
2936}
3037
3138test "math.fabs" {
39 assert(fabs(f16(1.0)) == fabs16(1.0));
3240 assert(fabs(f32(1.0)) == fabs32(1.0));
3341 assert(fabs(f64(1.0)) == fabs64(1.0));
3442}
3543
44test "math.fabs16" {
45 assert(fabs16(1.0) == 1.0);
46 assert(fabs16(-1.0) == 1.0);
47}
48
3649test "math.fabs32" {
3750 assert(fabs32(1.0) == 1.0);
3851 assert(fabs32(-1.0) == 1.0);
......@@ -43,6 +56,12 @@ test "math.fabs64" {
4356 assert(fabs64(-1.0) == 1.0);
4457}
4558
59test "math.fabs16.special" {
60 assert(math.isPositiveInf(fabs(math.inf(f16))));
61 assert(math.isPositiveInf(fabs(-math.inf(f16))));
62 assert(math.isNan(fabs(math.nan(f16))));
63}
64
4665test "math.fabs32.special" {
4766 assert(math.isPositiveInf(fabs(math.inf(f32))));
4867 assert(math.isPositiveInf(fabs(-math.inf(f32))));
std/math/floor.zig+50
......@@ -12,12 +12,47 @@ const math = std.math;
1212pub fn floor(x: var) @typeOf(x) {
1313 const T = @typeOf(x);
1414 return switch (T) {
15 f16 => floor16(x),
1516 f32 => floor32(x),
1617 f64 => floor64(x),
1718 else => @compileError("floor not implemented for " ++ @typeName(T)),
1819 };
1920}
2021
22fn floor16(x: f16) f16 {
23 var u = @bitCast(u16, x);
24 const e = @intCast(i16, (u >> 10) & 31) - 15;
25 var m: u16 = undefined;
26
27 // TODO: Shouldn't need this explicit check.
28 if (x == 0.0) {
29 return x;
30 }
31
32 if (e >= 10) {
33 return x;
34 }
35
36 if (e >= 0) {
37 m = u16(1023) >> @intCast(u4, e);
38 if (u & m == 0) {
39 return x;
40 }
41 math.forceEval(x + 0x1.0p120);
42 if (u >> 15 != 0) {
43 u += m;
44 }
45 return @bitCast(f16, u & ~m);
46 } else {
47 math.forceEval(x + 0x1.0p120);
48 if (u >> 15 == 0) {
49 return 0.0;
50 } else {
51 return -1.0;
52 }
53 }
54}
55
2156fn floor32(x: f32) f32 {
2257 var u = @bitCast(u32, x);
2358 const e = @intCast(i32, (u >> 23) & 0xFF) - 0x7F;
......@@ -84,10 +119,17 @@ fn floor64(x: f64) f64 {
84119}
85120
86121test "math.floor" {
122 assert(floor(f16(1.3)) == floor16(1.3));
87123 assert(floor(f32(1.3)) == floor32(1.3));
88124 assert(floor(f64(1.3)) == floor64(1.3));
89125}
90126
127test "math.floor16" {
128 assert(floor16(1.3) == 1.0);
129 assert(floor16(-1.3) == -2.0);
130 assert(floor16(0.2) == 0.0);
131}
132
91133test "math.floor32" {
92134 assert(floor32(1.3) == 1.0);
93135 assert(floor32(-1.3) == -2.0);
......@@ -100,6 +142,14 @@ test "math.floor64" {
100142 assert(floor64(0.2) == 0.0);
101143}
102144
145test "math.floor16.special" {
146 assert(floor16(0.0) == 0.0);
147 assert(floor16(-0.0) == -0.0);
148 assert(math.isPositiveInf(floor16(math.inf(f16))));
149 assert(math.isNegativeInf(floor16(-math.inf(f16))));
150 assert(math.isNan(floor16(math.nan(f16))));
151}
152
103153test "math.floor32.special" {
104154 assert(floor32(0.0) == 0.0);
105155 assert(floor32(-0.0) == -0.0);
std/math/index.zig+17
......@@ -19,6 +19,18 @@ pub const f32_max = 3.40282346638528859812e+38;
1919pub const f32_epsilon = 1.1920928955078125e-07;
2020pub const f32_toint = 1.0 / f32_epsilon;
2121
22pub const f16_true_min = 0.000000059604644775390625; // 2**-24
23pub const f16_min = 0.00006103515625; // 2**-14
24pub const f16_max = 65504;
25pub const f16_epsilon = 0.0009765625; // 2**-10
26pub const f16_toint = 1.0 / f16_epsilon;
27
28pub const nan_u16 = u16(0x7C01);
29pub const nan_f16 = @bitCast(f16, nan_u16);
30
31pub const inf_u16 = u16(0x7C00);
32pub const inf_f16 = @bitCast(f16, inf_u16);
33
2234pub const nan_u32 = u32(0x7F800001);
2335pub const nan_f32 = @bitCast(f32, nan_u32);
2436
......@@ -44,6 +56,11 @@ pub fn approxEq(comptime T: type, x: T, y: T, epsilon: T) bool {
4456pub fn forceEval(value: var) void {
4557 const T = @typeOf(value);
4658 switch (T) {
59 f16 => {
60 var x: f16 = undefined;
61 const p = @ptrCast(*volatile f16, &x);
62 p.* = x;
63 },
4764 f32 => {
4865 var x: f32 = undefined;
4966 const p = @ptrCast(*volatile f32, &x);
std/math/inf.zig+1-1
......@@ -1,9 +1,9 @@
11const std = @import("../index.zig");
22const math = std.math;
3const assert = std.debug.assert;
43
54pub fn inf(comptime T: type) T {
65 return switch (T) {
6 f16 => @bitCast(f16, math.inf_u16),
77 f32 => @bitCast(f32, math.inf_u32),
88 f64 => @bitCast(f64, math.inf_u64),
99 else => @compileError("inf not implemented for " ++ @typeName(T)),
std/math/isfinite.zig+8
......@@ -5,6 +5,10 @@ const assert = std.debug.assert;
55pub fn isFinite(x: var) bool {
66 const T = @typeOf(x);
77 switch (T) {
8 f16 => {
9 const bits = @bitCast(u16, x);
10 return bits & 0x7FFF < 0x7C00;
11 },
812 f32 => {
913 const bits = @bitCast(u32, x);
1014 return bits & 0x7FFFFFFF < 0x7F800000;
......@@ -20,10 +24,14 @@ pub fn isFinite(x: var) bool {
2024}
2125
2226test "math.isFinite" {
27 assert(isFinite(f16(0.0)));
28 assert(isFinite(f16(-0.0)));
2329 assert(isFinite(f32(0.0)));
2430 assert(isFinite(f32(-0.0)));
2531 assert(isFinite(f64(0.0)));
2632 assert(isFinite(f64(-0.0)));
33 assert(!isFinite(math.inf(f16)));
34 assert(!isFinite(-math.inf(f16)));
2735 assert(!isFinite(math.inf(f32)));
2836 assert(!isFinite(-math.inf(f32)));
2937 assert(!isFinite(math.inf(f64)));
std/math/isinf.zig+22
......@@ -5,6 +5,10 @@ const assert = std.debug.assert;
55pub fn isInf(x: var) bool {
66 const T = @typeOf(x);
77 switch (T) {
8 f16 => {
9 const bits = @bitCast(u16, x);
10 return bits & 0x7FFF == 0x7C00;
11 },
812 f32 => {
913 const bits = @bitCast(u32, x);
1014 return bits & 0x7FFFFFFF == 0x7F800000;
......@@ -22,6 +26,9 @@ pub fn isInf(x: var) bool {
2226pub fn isPositiveInf(x: var) bool {
2327 const T = @typeOf(x);
2428 switch (T) {
29 f16 => {
30 return @bitCast(u16, x) == 0x7C00;
31 },
2532 f32 => {
2633 return @bitCast(u32, x) == 0x7F800000;
2734 },
......@@ -37,6 +44,9 @@ pub fn isPositiveInf(x: var) bool {
3744pub fn isNegativeInf(x: var) bool {
3845 const T = @typeOf(x);
3946 switch (T) {
47 f16 => {
48 return @bitCast(u16, x) == 0xFC00;
49 },
4050 f32 => {
4151 return @bitCast(u32, x) == 0xFF800000;
4252 },
......@@ -50,10 +60,14 @@ pub fn isNegativeInf(x: var) bool {
5060}
5161
5262test "math.isInf" {
63 assert(!isInf(f16(0.0)));
64 assert(!isInf(f16(-0.0)));
5365 assert(!isInf(f32(0.0)));
5466 assert(!isInf(f32(-0.0)));
5567 assert(!isInf(f64(0.0)));
5668 assert(!isInf(f64(-0.0)));
69 assert(isInf(math.inf(f16)));
70 assert(isInf(-math.inf(f16)));
5771 assert(isInf(math.inf(f32)));
5872 assert(isInf(-math.inf(f32)));
5973 assert(isInf(math.inf(f64)));
......@@ -61,10 +75,14 @@ test "math.isInf" {
6175}
6276
6377test "math.isPositiveInf" {
78 assert(!isPositiveInf(f16(0.0)));
79 assert(!isPositiveInf(f16(-0.0)));
6480 assert(!isPositiveInf(f32(0.0)));
6581 assert(!isPositiveInf(f32(-0.0)));
6682 assert(!isPositiveInf(f64(0.0)));
6783 assert(!isPositiveInf(f64(-0.0)));
84 assert(isPositiveInf(math.inf(f16)));
85 assert(!isPositiveInf(-math.inf(f16)));
6886 assert(isPositiveInf(math.inf(f32)));
6987 assert(!isPositiveInf(-math.inf(f32)));
7088 assert(isPositiveInf(math.inf(f64)));
......@@ -72,10 +90,14 @@ test "math.isPositiveInf" {
7290}
7391
7492test "math.isNegativeInf" {
93 assert(!isNegativeInf(f16(0.0)));
94 assert(!isNegativeInf(f16(-0.0)));
7595 assert(!isNegativeInf(f32(0.0)));
7696 assert(!isNegativeInf(f32(-0.0)));
7797 assert(!isNegativeInf(f64(0.0)));
7898 assert(!isNegativeInf(f64(-0.0)));
99 assert(!isNegativeInf(math.inf(f16)));
100 assert(isNegativeInf(-math.inf(f16)));
79101 assert(!isNegativeInf(math.inf(f32)));
80102 assert(isNegativeInf(-math.inf(f32)));
81103 assert(!isNegativeInf(math.inf(f64)));
std/math/isnan.zig+6
......@@ -5,6 +5,10 @@ const assert = std.debug.assert;
55pub fn isNan(x: var) bool {
66 const T = @typeOf(x);
77 switch (T) {
8 f16 => {
9 const bits = @bitCast(u16, x);
10 return (bits & 0x7fff) > 0x7c00;
11 },
812 f32 => {
913 const bits = @bitCast(u32, x);
1014 return bits & 0x7FFFFFFF > 0x7F800000;
......@@ -26,8 +30,10 @@ pub fn isSignalNan(x: var) bool {
2630}
2731
2832test "math.isNan" {
33 assert(isNan(math.nan(f16)));
2934 assert(isNan(math.nan(f32)));
3035 assert(isNan(math.nan(f64)));
36 assert(!isNan(f16(1.0)));
3137 assert(!isNan(f32(1.0)));
3238 assert(!isNan(f64(1.0)));
3339}
std/math/isnormal.zig+9
......@@ -5,6 +5,10 @@ const assert = std.debug.assert;
55pub fn isNormal(x: var) bool {
66 const T = @typeOf(x);
77 switch (T) {
8 f16 => {
9 const bits = @bitCast(u16, x);
10 return (bits + 1024) & 0x7FFF >= 2048;
11 },
812 f32 => {
913 const bits = @bitCast(u32, x);
1014 return (bits + 0x00800000) & 0x7FFFFFFF >= 0x01000000;
......@@ -20,8 +24,13 @@ pub fn isNormal(x: var) bool {
2024}
2125
2226test "math.isNormal" {
27 assert(!isNormal(math.nan(f16)));
2328 assert(!isNormal(math.nan(f32)));
2429 assert(!isNormal(math.nan(f64)));
30 assert(!isNormal(f16(0)));
31 assert(!isNormal(f32(0)));
32 assert(!isNormal(f64(0)));
33 assert(isNormal(f16(1.0)));
2534 assert(isNormal(f32(1.0)));
2635 assert(isNormal(f64(1.0)));
2736}
std/math/nan.zig+2
......@@ -2,6 +2,7 @@ const math = @import("index.zig");
22
33pub fn nan(comptime T: type) T {
44 return switch (T) {
5 f16 => @bitCast(f16, math.nan_u16),
56 f32 => @bitCast(f32, math.nan_u32),
67 f64 => @bitCast(f64, math.nan_u64),
78 else => @compileError("nan not implemented for " ++ @typeName(T)),
......@@ -12,6 +13,7 @@ pub fn nan(comptime T: type) T {
1213// representation in the future when required.
1314pub fn snan(comptime T: type) T {
1415 return switch (T) {
16 f16 => @bitCast(f16, math.nan_u16),
1517 f32 => @bitCast(f32, math.nan_u32),
1618 f64 => @bitCast(f64, math.nan_u64),
1719 else => @compileError("snan not implemented for " ++ @typeName(T)),
std/math/signbit.zig+12
......@@ -5,12 +5,18 @@ const assert = std.debug.assert;
55pub fn signbit(x: var) bool {
66 const T = @typeOf(x);
77 return switch (T) {
8 f16 => signbit16(x),
89 f32 => signbit32(x),
910 f64 => signbit64(x),
1011 else => @compileError("signbit not implemented for " ++ @typeName(T)),
1112 };
1213}
1314
15fn signbit16(x: f16) bool {
16 const bits = @bitCast(u16, x);
17 return bits >> 15 != 0;
18}
19
1420fn signbit32(x: f32) bool {
1521 const bits = @bitCast(u32, x);
1622 return bits >> 31 != 0;
......@@ -22,10 +28,16 @@ fn signbit64(x: f64) bool {
2228}
2329
2430test "math.signbit" {
31 assert(signbit(f16(4.0)) == signbit16(4.0));
2532 assert(signbit(f32(4.0)) == signbit32(4.0));
2633 assert(signbit(f64(4.0)) == signbit64(4.0));
2734}
2835
36test "math.signbit16" {
37 assert(!signbit16(4.0));
38 assert(signbit16(-3.0));
39}
40
2941test "math.signbit32" {
3042 assert(!signbit32(4.0));
3143 assert(signbit32(-3.0));
std/math/sqrt.zig+23
......@@ -31,10 +31,25 @@ pub fn sqrt(x: var) (if (@typeId(@typeOf(x)) == TypeId.Int) @IntType(false, @typ
3131}
3232
3333test "math.sqrt" {
34 assert(sqrt(f16(0.0)) == @sqrt(f16, 0.0));
3435 assert(sqrt(f32(0.0)) == @sqrt(f32, 0.0));
3536 assert(sqrt(f64(0.0)) == @sqrt(f64, 0.0));
3637}
3738
39test "math.sqrt16" {
40 const epsilon = 0.000001;
41
42 assert(@sqrt(f16, 0.0) == 0.0);
43 assert(math.approxEq(f16, @sqrt(f16, 2.0), 1.414214, epsilon));
44 assert(math.approxEq(f16, @sqrt(f16, 3.6), 1.897367, epsilon));
45 assert(@sqrt(f16, 4.0) == 2.0);
46 assert(math.approxEq(f16, @sqrt(f16, 7.539840), 2.745877, epsilon));
47 assert(math.approxEq(f16, @sqrt(f16, 19.230934), 4.385309, epsilon));
48 assert(@sqrt(f16, 64.0) == 8.0);
49 assert(math.approxEq(f16, @sqrt(f16, 64.1), 8.006248, epsilon));
50 assert(math.approxEq(f16, @sqrt(f16, 8942.230469), 94.563370, epsilon));
51}
52
3853test "math.sqrt32" {
3954 const epsilon = 0.000001;
4055
......@@ -63,6 +78,14 @@ test "math.sqrt64" {
6378 assert(math.approxEq(f64, @sqrt(f64, 8942.230469), 94.563367, epsilon));
6479}
6580
81test "math.sqrt16.special" {
82 assert(math.isPositiveInf(@sqrt(f16, math.inf(f16))));
83 assert(@sqrt(f16, 0.0) == 0.0);
84 assert(@sqrt(f16, -0.0) == -0.0);
85 assert(math.isNan(@sqrt(f16, -1.0)));
86 assert(math.isNan(@sqrt(f16, math.nan(f16))));
87}
88
6689test "math.sqrt32.special" {
6790 assert(math.isPositiveInf(@sqrt(f32, math.inf(f32))));
6891 assert(@sqrt(f32, 0.0) == 0.0);
std/special/builtin.zig+3-1
......@@ -210,7 +210,9 @@ fn generic_fmod(comptime T: type, x: T, y: T) T {
210210}
211211
212212fn isNan(comptime T: type, bits: T) bool {
213 if (T == u32) {
213 if (T == u16) {
214 return (bits & 0x7fff) > 0x7c00;
215 } else if (T == u32) {
214216 return (bits & 0x7fffffff) > 0x7f800000;
215217 } else if (T == u64) {
216218 return (bits & (@maxValue(u64) >> 1)) > (u64(0x7ff) << 52);
std/special/compiler_rt/extendXfYf2_test.zig+1
......@@ -88,6 +88,7 @@ test "extenddftf2" {
8888test "extendhfsf2" {
8989 test__extendhfsf2(0x7e00, 0x7fc00000); // qNaN
9090 test__extendhfsf2(0x7f00, 0x7fe00000); // sNaN
91 test__extendhfsf2(0x7c01, 0x7f802000); // sNaN
9192
9293 test__extendhfsf2(0, 0); // 0
9394 test__extendhfsf2(0x8000, 0x80000000); // -0