aboutsummaryrefslogtreecommitdiff
path: root/std
diff options
context:
space:
mode:
Diffstat (limited to 'std')
-rw-r--r--std/fmt/index.zig8
-rw-r--r--std/fmt/parse_float.zig420
-rw-r--r--std/json.zig8
-rw-r--r--std/math/fabs.zig19
-rw-r--r--std/math/index.zig12
-rw-r--r--std/math/inf.zig7
-rw-r--r--std/math/isinf.zig22
-rw-r--r--std/math/isnan.zig6
-rw-r--r--std/math/nan.zig7
-rw-r--r--std/special/compiler_rt/addXf3.zig191
-rw-r--r--std/special/compiler_rt/addXf3_test.zig85
-rw-r--r--std/special/compiler_rt/index.zig4
-rw-r--r--std/special/compiler_rt/truncXfYf2.zig4
-rw-r--r--std/special/compiler_rt/truncXfYf2_test.zig68
14 files changed, 851 insertions, 10 deletions
diff --git a/std/fmt/index.zig b/std/fmt/index.zig
index b09fe21032..f006dfdbdc 100644
--- a/std/fmt/index.zig
+++ b/std/fmt/index.zig
@@ -831,7 +831,7 @@ pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) ParseUnsigned
return x;
}
-test "parseUnsigned" {
+test "fmt.parseUnsigned" {
testing.expect((try parseUnsigned(u16, "050124", 10)) == 50124);
testing.expect((try parseUnsigned(u16, "65535", 10)) == 65535);
testing.expectError(error.Overflow, parseUnsigned(u16, "65536", 10));
@@ -858,6 +858,12 @@ test "parseUnsigned" {
testing.expectError(error.Overflow, parseUnsigned(u2, "4", 16));
}
+pub const parseFloat = @import("parse_float.zig").parseFloat;
+
+test "fmt.parseFloat" {
+ _ = @import("parse_float.zig");
+}
+
pub fn charToDigit(c: u8, radix: u8) (error{InvalidCharacter}!u8) {
const value = switch (c) {
'0'...'9' => c - '0',
diff --git a/std/fmt/parse_float.zig b/std/fmt/parse_float.zig
new file mode 100644
index 0000000000..de9619efe2
--- /dev/null
+++ b/std/fmt/parse_float.zig
@@ -0,0 +1,420 @@
+// Adapted from https://github.com/grzegorz-kraszewski/stringtofloat.
+
+// MIT License
+//
+// Copyright (c) 2016 Grzegorz Kraszewski
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+
+// Be aware that this implementation has the following limitations:
+//
+// - Is not round-trip accurate for all values
+// - Only supports round-to-zero
+// - Does not handle denormals
+
+const std = @import("../index.zig");
+
+const max_digits = 25;
+
+const f64_plus_zero: u64 = 0x0000000000000000;
+const f64_minus_zero: u64 = 0x8000000000000000;
+const f64_plus_infinity: u64 = 0x7FF0000000000000;
+const f64_minus_infinity: u64 = 0xFFF0000000000000;
+
+const Z96 = struct {
+ d0: u32,
+ d1: u32,
+ d2: u32,
+
+ // d = s >> 1
+ inline fn shiftRight1(d: *Z96, s: Z96) void {
+ d.d0 = (s.d0 >> 1) | ((s.d1 & 1) << 31);
+ d.d1 = (s.d1 >> 1) | ((s.d2 & 1) << 31);
+ d.d2 = s.d2 >> 1;
+ }
+
+ // d = s << 1
+ inline fn shiftLeft1(d: *Z96, s: Z96) void {
+ d.d2 = (s.d2 << 1) | ((s.d1 & (1 << 31)) >> 31);
+ d.d1 = (s.d1 << 1) | ((s.d0 & (1 << 31)) >> 31);
+ d.d0 = s.d0 << 1;
+ }
+
+ // d += s
+ inline fn add(d: *Z96, s: Z96) void {
+ var w = u64(d.d0) + u64(s.d0);
+ d.d0 = @truncate(u32, w);
+
+ w >>= 32;
+ w += u64(d.d1) + u64(s.d1);
+ d.d1 = @truncate(u32, w);
+
+ w >>= 32;
+ w += u64(d.d2) + u64(s.d2);
+ d.d2 = @truncate(u32, w);
+ }
+
+ // d -= s
+ inline fn sub(d: *Z96, s: Z96) void {
+ var w = u64(d.d0) -% u64(s.d0);
+ d.d0 = @truncate(u32, w);
+
+ w >>= 32;
+ w += u64(d.d1) -% u64(s.d1);
+ d.d1 = @truncate(u32, w);
+
+ w >>= 32;
+ w += u64(d.d2) -% u64(s.d2);
+ d.d2 = @truncate(u32, w);
+ }
+};
+
+const FloatRepr = struct {
+ negative: bool,
+ exponent: i32,
+ mantissa: u64,
+};
+
+fn convertRepr(comptime T: type, n: FloatRepr) T {
+ const mask28: u32 = 0xf << 28;
+
+ var s: Z96 = undefined;
+ var q: Z96 = undefined;
+ var r: Z96 = undefined;
+
+ s.d0 = @truncate(u32, n.mantissa);
+ s.d1 = @truncate(u32, n.mantissa >> 32);
+ s.d2 = 0;
+
+ var binary_exponent: u64 = 92;
+ var exp = n.exponent;
+
+ while (exp > 0) : (exp -= 1) {
+ q.shiftLeft1(s); // q = p << 1
+ r.shiftLeft1(q); // r = p << 2
+ s.shiftLeft1(r); // p = p << 3
+ q.add(s); // p = (p << 3) + (p << 1)
+
+ exp -= 1;
+
+ while (s.d2 & mask28 != 0) {
+ q.shiftRight1(s);
+ binary_exponent += 1;
+ s = q;
+ }
+ }
+
+ while (exp < 0) {
+ while (s.d2 & (1 << 31) == 0) {
+ q.shiftLeft1(s);
+ binary_exponent -= 1;
+ s = q;
+ }
+
+ q.d2 = s.d2 / 10;
+ r.d1 = s.d2 % 10;
+ r.d2 = (s.d1 >> 8) | (r.d1 << 24);
+ q.d1 = r.d2 / 10;
+ r.d1 = r.d2 % 10;
+ r.d2 = ((s.d1 & 0xff) << 16) | (s.d0 >> 16) | (r.d1 << 24);
+ r.d0 = r.d2 / 10;
+ r.d1 = r.d2 % 10;
+ q.d1 = (q.d1 << 8) | ((r.d0 & 0x00ff0000) >> 16);
+ q.d0 = r.d0 << 16;
+ r.d2 = (s.d0 *% 0xffff) | (r.d1 << 16);
+ q.d0 |= r.d2 / 10;
+ s = q;
+
+ exp += 1;
+ }
+
+ if (s.d0 != 0 or s.d1 != 0 or s.d2 != 0) {
+ while (s.d2 & mask28 == 0) {
+ q.shiftLeft1(s);
+ binary_exponent -= 1;
+ s = q;
+ }
+ }
+
+ binary_exponent += 1023;
+
+ const repr: u64 = blk: {
+ if (binary_exponent > 2046) {
+ break :blk if (n.negative) f64_minus_infinity else f64_plus_infinity;
+ } else if (binary_exponent < 1) {
+ break :blk if (n.negative) f64_minus_zero else f64_plus_zero;
+ } else if (s.d2 != 0) {
+ const binexs2 = u64(binary_exponent) << 52;
+ const rr = (u64(s.d2 & ~mask28) << 24) | ((u64(s.d1) + 128) >> 8) | binexs2;
+ break :blk if (n.negative) rr | (1 << 63) else rr;
+ } else {
+ break :blk 0;
+ }
+ };
+
+ const f = @bitCast(f64, repr);
+ return @floatCast(T, f);
+}
+
+const State = enum {
+ MaybeSign,
+ LeadingMantissaZeros,
+ LeadingFractionalZeros,
+ MantissaIntegral,
+ MantissaFractional,
+ ExponentSign,
+ LeadingExponentZeros,
+ Exponent,
+};
+
+const ParseResult = enum {
+ Ok,
+ PlusZero,
+ MinusZero,
+ PlusInf,
+ MinusInf,
+};
+
+inline fn isDigit(c: u8) bool {
+ return c >= '0' and c <= '9';
+}
+
+inline fn isSpace(c: u8) bool {
+ return (c >= 0x09 and c <= 0x13) or c == 0x20;
+}
+
+fn parseRepr(s: []const u8, n: *FloatRepr) !ParseResult {
+ var digit_index: usize = 0;
+ var negative = false;
+ var negative_exp = false;
+ var exponent: i32 = 0;
+
+ var state = State.MaybeSign;
+
+ var i: usize = 0;
+ loop: while (i < s.len) {
+ const c = s[i];
+
+ switch (state) {
+ State.MaybeSign => {
+ state = State.LeadingMantissaZeros;
+
+ if (c == '+') {
+ i += 1;
+ } else if (c == '-') {
+ n.negative = true;
+ i += 1;
+ } else if (isDigit(c) or c == '.') {
+ // continue
+ } else {
+ return error.InvalidCharacter;
+ }
+ },
+
+ State.LeadingMantissaZeros => {
+ if (c == '0') {
+ i += 1;
+ } else if (c == '.') {
+ i += 1;
+ state = State.LeadingFractionalZeros;
+ } else {
+ state = State.MantissaIntegral;
+ }
+ },
+
+ State.LeadingFractionalZeros => {
+ if (c == '0') {
+ i += 1;
+ if (n.exponent > std.math.minInt(i32)) {
+ n.exponent -= 1;
+ }
+ } else {
+ state = State.MantissaFractional;
+ }
+ },
+
+ State.MantissaIntegral => {
+ if (isDigit(c)) {
+ if (digit_index < max_digits) {
+ n.mantissa *%= 10;
+ n.mantissa += s[i] - '0';
+ digit_index += 1;
+ } else if (n.exponent < std.math.maxInt(i32)) {
+ n.exponent += 1;
+ }
+
+ i += 1;
+ } else if (c == '.') {
+ i += 1;
+ state = State.MantissaFractional;
+ } else {
+ state = State.MantissaFractional;
+ }
+ },
+
+ State.MantissaFractional => {
+ if (isDigit(c)) {
+ if (digit_index < max_digits) {
+ n.mantissa *%= 10;
+ n.mantissa += c - '0';
+ n.exponent -%= 1;
+ digit_index += 1;
+ }
+
+ i += 1;
+ } else if (c == 'e' or c == 'E') {
+ i += 1;
+ state = State.ExponentSign;
+ } else {
+ state = State.ExponentSign;
+ }
+ },
+
+ State.ExponentSign => {
+ if (c == '+') {
+ i += 1;
+ } else if (c == '-') {
+ negative_exp = true;
+ i += 1;
+ }
+
+ state = State.LeadingExponentZeros;
+ },
+
+ State.LeadingExponentZeros => {
+ if (c == '0') {
+ i += 1;
+ } else {
+ state = State.Exponent;
+ }
+ },
+
+ State.Exponent => {
+ if (isDigit(c)) {
+ if (exponent < std.math.maxInt(i32)) {
+ exponent *= 10;
+ exponent += @intCast(i32, c - '0');
+ }
+
+ i += 1;
+ } else {
+ return error.InvalidCharacter;
+ }
+ },
+ }
+ }
+
+ if (negative_exp) exponent = -exponent;
+ n.exponent += exponent;
+
+ if (n.mantissa == 0) {
+ return if (n.negative) ParseResult.MinusZero else ParseResult.PlusZero;
+ } else if (n.exponent > 309) {
+ return if (n.negative) ParseResult.MinusInf else ParseResult.PlusInf;
+ } else if (n.exponent < -328) {
+ return if (n.negative) ParseResult.MinusZero else ParseResult.PlusZero;
+ }
+
+ return ParseResult.Ok;
+}
+
+inline fn isLower(c: u8) bool {
+ return c -% 'a' < 26;
+}
+
+inline fn toUpper(c: u8) u8 {
+ return if (isLower(c)) (c & 0x5f) else c;
+}
+
+fn caseInEql(a: []const u8, b: []const u8) bool {
+ if (a.len != b.len) return false;
+
+ for (a) |_, i| {
+ if (toUpper(a[i]) != toUpper(b[i])) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+pub fn parseFloat(comptime T: type, s: []const u8) !T {
+ if (s.len == 0) {
+ return error.InvalidCharacter;
+ }
+
+ if (caseInEql(s, "nan")) {
+ return std.math.nan(T);
+ } else if (caseInEql(s, "inf") or caseInEql(s, "+inf")) {
+ return std.math.inf(T);
+ } else if (caseInEql(s, "-inf")) {
+ return -std.math.inf(T);
+ }
+
+ var r = FloatRepr{
+ .negative = false,
+ .exponent = 0,
+ .mantissa = 0,
+ };
+
+ return switch (try parseRepr(s, &r)) {
+ ParseResult.Ok => convertRepr(T, r),
+ ParseResult.PlusZero => 0.0,
+ ParseResult.MinusZero => -T(0.0),
+ ParseResult.PlusInf => std.math.inf(T),
+ ParseResult.MinusInf => -std.math.inf(T),
+ };
+}
+
+test "fmt.parseFloat" {
+ const testing = std.testing;
+ const expect = testing.expect;
+ const expectEqual = testing.expectEqual;
+ const approxEq = std.math.approxEq;
+ const epsilon = 1e-7;
+
+ inline for ([]type{ f16, f32, f64, f128 }) |T| {
+ const Z = @IntType(false, T.bit_count);
+
+ testing.expectError(error.InvalidCharacter, parseFloat(T, ""));
+ testing.expectError(error.InvalidCharacter, parseFloat(T, " 1"));
+ testing.expectError(error.InvalidCharacter, parseFloat(T, "1abc"));
+
+ expectEqual(try parseFloat(T, "0"), 0.0);
+ expectEqual((try parseFloat(T, "0")), 0.0);
+ expectEqual((try parseFloat(T, "+0")), 0.0);
+ expectEqual((try parseFloat(T, "-0")), 0.0);
+
+ expect(approxEq(T, try parseFloat(T, "3.141"), 3.141, epsilon));
+ expect(approxEq(T, try parseFloat(T, "-3.141"), -3.141, epsilon));
+
+ expectEqual((try parseFloat(T, "1e-700")), 0);
+ expectEqual((try parseFloat(T, "1e+700")), std.math.inf(T));
+
+ expectEqual(@bitCast(Z, try parseFloat(T, "nAn")), @bitCast(Z, std.math.nan(T)));
+ expectEqual((try parseFloat(T, "inF")), std.math.inf(T));
+ expectEqual((try parseFloat(T, "-INF")), -std.math.inf(T));
+
+ if (T != f16) {
+ expect(approxEq(T, try parseFloat(T, "123142.1"), 123142.1, epsilon));
+ expect(approxEq(T, try parseFloat(T, "-123142.1124"), T(-123142.1124), epsilon));
+ }
+ }
+}
diff --git a/std/json.zig b/std/json.zig
index d8f28560e5..dd053e7635 100644
--- a/std/json.zig
+++ b/std/json.zig
@@ -1345,7 +1345,7 @@ pub const Parser = struct {
return if (token.number_is_integer)
Value{ .Integer = try std.fmt.parseInt(i64, token.slice(input, i), 10) }
else
- @panic("TODO: fmt.parseFloat not yet implemented");
+ Value{ .Float = try std.fmt.parseFloat(f64, token.slice(input, i)) };
}
};
@@ -1366,7 +1366,8 @@ test "json.parser.dynamic" {
\\ },
\\ "Animated" : false,
\\ "IDs": [116, 943, 234, 38793],
- \\ "ArrayOfObject": [{"n": "m"}]
+ \\ "ArrayOfObject": [{"n": "m"}],
+ \\ "double": 1.3412
\\ }
\\}
;
@@ -1395,4 +1396,7 @@ test "json.parser.dynamic" {
const obj0 = array_of_object.Array.at(0).Object.get("n").?.value;
testing.expect(mem.eql(u8, obj0.String, "m"));
+
+ const double = image.Object.get("double").?.value;
+ testing.expect(double.Float == 1.3412);
}
diff --git a/std/math/fabs.zig b/std/math/fabs.zig
index 9fad5644c3..a605f4f33f 100644
--- a/std/math/fabs.zig
+++ b/std/math/fabs.zig
@@ -14,6 +14,7 @@ pub fn fabs(x: var) @typeOf(x) {
f16 => fabs16(x),
f32 => fabs32(x),
f64 => fabs64(x),
+ f128 => fabs128(x),
else => @compileError("fabs not implemented for " ++ @typeName(T)),
};
}
@@ -36,10 +37,17 @@ fn fabs64(x: f64) f64 {
return @bitCast(f64, u);
}
+fn fabs128(x: f128) f128 {
+ var u = @bitCast(u128, x);
+ u &= maxInt(u128) >> 1;
+ return @bitCast(f128, u);
+}
+
test "math.fabs" {
expect(fabs(f16(1.0)) == fabs16(1.0));
expect(fabs(f32(1.0)) == fabs32(1.0));
expect(fabs(f64(1.0)) == fabs64(1.0));
+ expect(fabs(f128(1.0)) == fabs128(1.0));
}
test "math.fabs16" {
@@ -57,6 +65,11 @@ test "math.fabs64" {
expect(fabs64(-1.0) == 1.0);
}
+test "math.fabs128" {
+ expect(fabs128(1.0) == 1.0);
+ expect(fabs128(-1.0) == 1.0);
+}
+
test "math.fabs16.special" {
expect(math.isPositiveInf(fabs(math.inf(f16))));
expect(math.isPositiveInf(fabs(-math.inf(f16))));
@@ -74,3 +87,9 @@ test "math.fabs64.special" {
expect(math.isPositiveInf(fabs(-math.inf(f64))));
expect(math.isNan(fabs(math.nan(f64))));
}
+
+test "math.fabs128.special" {
+ expect(math.isPositiveInf(fabs(math.inf(f128))));
+ expect(math.isPositiveInf(fabs(-math.inf(f128))));
+ expect(math.isNan(fabs(math.nan(f128))));
+}
diff --git a/std/math/index.zig b/std/math/index.zig
index ccfac03038..20648139b8 100644
--- a/std/math/index.zig
+++ b/std/math/index.zig
@@ -51,6 +51,12 @@ pub const nan_f64 = @bitCast(f64, nan_u64);
pub const inf_u64 = u64(0x7FF << 52);
pub const inf_f64 = @bitCast(f64, inf_u64);
+pub const nan_u128 = u128(0x7fff0000000000000000000000000001);
+pub const nan_f128 = @bitCast(f128, nan_u128);
+
+pub const inf_u128 = u128(0x7fff0000000000000000000000000000);
+pub const inf_f128 = @bitCast(f128, inf_u128);
+
pub const nan = @import("nan.zig").nan;
pub const snan = @import("nan.zig").snan;
pub const inf = @import("inf.zig").inf;
@@ -379,7 +385,7 @@ pub fn IntFittingRange(comptime from: comptime_int, comptime to: comptime_int) t
return u0;
}
const is_signed = from < 0;
- const largest_positive_integer = max(if (from<0) (-from)-1 else from, to); // two's complement
+ const largest_positive_integer = max(if (from < 0) (-from) - 1 else from, to); // two's complement
const base = log2(largest_positive_integer);
const upper = (1 << base) - 1;
var magnitude_bits = if (upper >= largest_positive_integer) base else base + 1;
@@ -752,6 +758,7 @@ test "minInt and maxInt" {
testing.expect(maxInt(u16) == 65535);
testing.expect(maxInt(u32) == 4294967295);
testing.expect(maxInt(u64) == 18446744073709551615);
+ testing.expect(maxInt(u128) == 340282366920938463463374607431768211455);
testing.expect(maxInt(i0) == 0);
testing.expect(maxInt(i1) == 0);
@@ -760,6 +767,7 @@ test "minInt and maxInt" {
testing.expect(maxInt(i32) == 2147483647);
testing.expect(maxInt(i63) == 4611686018427387903);
testing.expect(maxInt(i64) == 9223372036854775807);
+ testing.expect(maxInt(i128) == 170141183460469231731687303715884105727);
testing.expect(minInt(u0) == 0);
testing.expect(minInt(u1) == 0);
@@ -768,6 +776,7 @@ test "minInt and maxInt" {
testing.expect(minInt(u32) == 0);
testing.expect(minInt(u63) == 0);
testing.expect(minInt(u64) == 0);
+ testing.expect(minInt(u128) == 0);
testing.expect(minInt(i0) == 0);
testing.expect(minInt(i1) == -1);
@@ -776,6 +785,7 @@ test "minInt and maxInt" {
testing.expect(minInt(i32) == -2147483648);
testing.expect(minInt(i63) == -4611686018427387904);
testing.expect(minInt(i64) == -9223372036854775808);
+ testing.expect(minInt(i128) == -170141183460469231731687303715884105728);
}
test "max value type" {
diff --git a/std/math/inf.zig b/std/math/inf.zig
index 62f5ef7c0d..fb7a3489c5 100644
--- a/std/math/inf.zig
+++ b/std/math/inf.zig
@@ -3,9 +3,10 @@ const math = std.math;
pub fn inf(comptime T: type) T {
return switch (T) {
- f16 => @bitCast(f16, math.inf_u16),
- f32 => @bitCast(f32, math.inf_u32),
- f64 => @bitCast(f64, math.inf_u64),
+ f16 => math.inf_f16,
+ f32 => math.inf_f32,
+ f64 => math.inf_f64,
+ f128 => math.inf_f128,
else => @compileError("inf not implemented for " ++ @typeName(T)),
};
}
diff --git a/std/math/isinf.zig b/std/math/isinf.zig
index e34e9c5971..b1e3f7795e 100644
--- a/std/math/isinf.zig
+++ b/std/math/isinf.zig
@@ -18,6 +18,10 @@ pub fn isInf(x: var) bool {
const bits = @bitCast(u64, x);
return bits & (maxInt(u64) >> 1) == (0x7FF << 52);
},
+ f128 => {
+ const bits = @bitCast(u128, x);
+ return bits & (maxInt(u128) >> 1) == (0x7FFF << 112);
+ },
else => {
@compileError("isInf not implemented for " ++ @typeName(T));
},
@@ -36,6 +40,9 @@ pub fn isPositiveInf(x: var) bool {
f64 => {
return @bitCast(u64, x) == 0x7FF << 52;
},
+ f128 => {
+ return @bitCast(u128, x) == 0x7FFF << 112;
+ },
else => {
@compileError("isPositiveInf not implemented for " ++ @typeName(T));
},
@@ -54,6 +61,9 @@ pub fn isNegativeInf(x: var) bool {
f64 => {
return @bitCast(u64, x) == 0xFFF << 52;
},
+ f128 => {
+ return @bitCast(u128, x) == 0xFFFF << 112;
+ },
else => {
@compileError("isNegativeInf not implemented for " ++ @typeName(T));
},
@@ -67,12 +77,16 @@ test "math.isInf" {
expect(!isInf(f32(-0.0)));
expect(!isInf(f64(0.0)));
expect(!isInf(f64(-0.0)));
+ expect(!isInf(f128(0.0)));
+ expect(!isInf(f128(-0.0)));
expect(isInf(math.inf(f16)));
expect(isInf(-math.inf(f16)));
expect(isInf(math.inf(f32)));
expect(isInf(-math.inf(f32)));
expect(isInf(math.inf(f64)));
expect(isInf(-math.inf(f64)));
+ expect(isInf(math.inf(f128)));
+ expect(isInf(-math.inf(f128)));
}
test "math.isPositiveInf" {
@@ -82,12 +96,16 @@ test "math.isPositiveInf" {
expect(!isPositiveInf(f32(-0.0)));
expect(!isPositiveInf(f64(0.0)));
expect(!isPositiveInf(f64(-0.0)));
+ expect(!isPositiveInf(f128(0.0)));
+ expect(!isPositiveInf(f128(-0.0)));
expect(isPositiveInf(math.inf(f16)));
expect(!isPositiveInf(-math.inf(f16)));
expect(isPositiveInf(math.inf(f32)));
expect(!isPositiveInf(-math.inf(f32)));
expect(isPositiveInf(math.inf(f64)));
expect(!isPositiveInf(-math.inf(f64)));
+ expect(isPositiveInf(math.inf(f128)));
+ expect(!isPositiveInf(-math.inf(f128)));
}
test "math.isNegativeInf" {
@@ -97,10 +115,14 @@ test "math.isNegativeInf" {
expect(!isNegativeInf(f32(-0.0)));
expect(!isNegativeInf(f64(0.0)));
expect(!isNegativeInf(f64(-0.0)));
+ expect(!isNegativeInf(f128(0.0)));
+ expect(!isNegativeInf(f128(-0.0)));
expect(!isNegativeInf(math.inf(f16)));
expect(isNegativeInf(-math.inf(f16)));
expect(!isNegativeInf(math.inf(f32)));
expect(isNegativeInf(-math.inf(f32)));
expect(!isNegativeInf(math.inf(f64)));
expect(isNegativeInf(-math.inf(f64)));
+ expect(!isNegativeInf(math.inf(f128)));
+ expect(isNegativeInf(-math.inf(f128)));
}
diff --git a/std/math/isnan.zig b/std/math/isnan.zig
index 641da9e620..e8b03a1e34 100644
--- a/std/math/isnan.zig
+++ b/std/math/isnan.zig
@@ -18,6 +18,10 @@ pub fn isNan(x: var) bool {
const bits = @bitCast(u64, x);
return (bits & (maxInt(u64) >> 1)) > (u64(0x7FF) << 52);
},
+ f128 => {
+ const bits = @bitCast(u128, x);
+ return (bits & (maxInt(u128) >> 1)) > (u128(0x7FFF) << 112);
+ },
else => {
@compileError("isNan not implemented for " ++ @typeName(T));
},
@@ -34,7 +38,9 @@ test "math.isNan" {
expect(isNan(math.nan(f16)));
expect(isNan(math.nan(f32)));
expect(isNan(math.nan(f64)));
+ expect(isNan(math.nan(f128)));
expect(!isNan(f16(1.0)));
expect(!isNan(f32(1.0)));
expect(!isNan(f64(1.0)));
+ expect(!isNan(f128(1.0)));
}
diff --git a/std/math/nan.zig b/std/math/nan.zig
index 2cbcbee81b..d3ad43da23 100644
--- a/std/math/nan.zig
+++ b/std/math/nan.zig
@@ -2,9 +2,10 @@ const math = @import("index.zig");
pub fn nan(comptime T: type) T {
return switch (T) {
- f16 => @bitCast(f16, math.nan_u16),
- f32 => @bitCast(f32, math.nan_u32),
- f64 => @bitCast(f64, math.nan_u64),
+ f16 => math.nan_f16,
+ f32 => math.nan_f32,
+ f64 => math.nan_f64,
+ f128 => math.nan_f128,
else => @compileError("nan not implemented for " ++ @typeName(T)),
};
}
diff --git a/std/special/compiler_rt/addXf3.zig b/std/special/compiler_rt/addXf3.zig
new file mode 100644
index 0000000000..09413b2328
--- /dev/null
+++ b/std/special/compiler_rt/addXf3.zig
@@ -0,0 +1,191 @@
+// Ported from:
+//
+// https://github.com/llvm/llvm-project/blob/02d85149a05cb1f6dc49f0ba7a2ceca53718ae17/compiler-rt/lib/builtins/fp_add_impl.inc
+
+const std = @import("std");
+const builtin = @import("builtin");
+const compiler_rt = @import("index.zig");
+
+pub extern fn __addtf3(a: f128, b: f128) f128 {
+ return addXf3(f128, a, b);
+}
+
+pub extern fn __subtf3(a: f128, b: f128) f128 {
+ const neg_b = @bitCast(f128, @bitCast(u128, b) ^ (u128(1) << 127));
+ return addXf3(f128, a, neg_b);
+}
+
+inline fn normalize(comptime T: type, significand: *@IntType(false, T.bit_count)) i32 {
+ const Z = @IntType(false, T.bit_count);
+ const significandBits = std.math.floatMantissaBits(T);
+ const implicitBit = Z(1) << significandBits;
+
+ const shift = @clz(significand.*) - @clz(implicitBit);
+ significand.* <<= @intCast(u7, shift);
+ return 1 - shift;
+}
+
+inline fn addXf3(comptime T: type, a: T, b: T) T {
+ const Z = @IntType(false, T.bit_count);
+
+ const typeWidth = T.bit_count;
+ const significandBits = std.math.floatMantissaBits(T);
+ const exponentBits = std.math.floatExponentBits(T);
+
+ const signBit = (Z(1) << (significandBits + exponentBits));
+ const maxExponent = ((1 << exponentBits) - 1);
+ const exponentBias = (maxExponent >> 1);
+
+ const implicitBit = (Z(1) << significandBits);
+ const quietBit = implicitBit >> 1;
+ const significandMask = implicitBit - 1;
+
+ const absMask = signBit - 1;
+ const exponentMask = absMask ^ significandMask;
+ const qnanRep = exponentMask | quietBit;
+
+ var aRep = @bitCast(Z, a);
+ var bRep = @bitCast(Z, b);
+ const aAbs = aRep & absMask;
+ const bAbs = bRep & absMask;
+
+ const negative = (aRep & signBit) != 0;
+ const exponent = @intCast(i32, aAbs >> significandBits) - exponentBias;
+ const significand = (aAbs & significandMask) | implicitBit;
+
+ const infRep = @bitCast(Z, std.math.inf(T));
+
+ // Detect if a or b is zero, infinity, or NaN.
+ if (aAbs - Z(1) >= infRep - Z(1) or
+ bAbs - Z(1) >= infRep - Z(1))
+ {
+ // NaN + anything = qNaN
+ if (aAbs > infRep) return @bitCast(T, @bitCast(Z, a) | quietBit);
+ // anything + NaN = qNaN
+ if (bAbs > infRep) return @bitCast(T, @bitCast(Z, b) | quietBit);
+
+ if (aAbs == infRep) {
+ // +/-infinity + -/+infinity = qNaN
+ if ((@bitCast(Z, a) ^ @bitCast(Z, b)) == signBit) {
+ return @bitCast(T, qnanRep);
+ }
+ // +/-infinity + anything remaining = +/- infinity
+ else {
+ return a;
+ }
+ }
+
+ // anything remaining + +/-infinity = +/-infinity
+ if (bAbs == infRep) return b;
+
+ // zero + anything = anything
+ if (aAbs == 0) {
+ // but we need to get the sign right for zero + zero
+ if (bAbs == 0) {
+ return @bitCast(T, @bitCast(Z, a) & @bitCast(Z, b));
+ } else {
+ return b;
+ }
+ }
+
+ // anything + zero = anything
+ if (bAbs == 0) return a;
+ }
+
+ // Swap a and b if necessary so that a has the larger absolute value.
+ if (bAbs > aAbs) {
+ const temp = aRep;
+ aRep = bRep;
+ bRep = temp;
+ }
+
+ // Extract the exponent and significand from the (possibly swapped) a and b.
+ var aExponent = @intCast(i32, (aRep >> significandBits) & maxExponent);
+ var bExponent = @intCast(i32, (bRep >> significandBits) & maxExponent);
+ var aSignificand = aRep & significandMask;
+ var bSignificand = bRep & significandMask;
+
+ // Normalize any denormals, and adjust the exponent accordingly.
+ if (aExponent == 0) aExponent = normalize(T, &aSignificand);
+ if (bExponent == 0) bExponent = normalize(T, &bSignificand);
+
+ // The sign of the result is the sign of the larger operand, a. If they
+ // have opposite signs, we are performing a subtraction; otherwise addition.
+ const resultSign = aRep & signBit;
+ const subtraction = (aRep ^ bRep) & signBit != 0;
+
+ // Shift the significands to give us round, guard and sticky, and or in the
+ // implicit significand bit. (If we fell through from the denormal path it
+ // was already set by normalize( ), but setting it twice won't hurt
+ // anything.)
+ aSignificand = (aSignificand | implicitBit) << 3;
+ bSignificand = (bSignificand | implicitBit) << 3;
+
+ // Shift the significand of b by the difference in exponents, with a sticky
+ // bottom bit to get rounding correct.
+ const @"align" = @intCast(Z, aExponent - bExponent);
+ if (@"align" != 0) {
+ if (@"align" < typeWidth) {
+ const sticky = if (bSignificand << @intCast(u7, typeWidth - @"align") != 0) Z(1) else 0;
+ bSignificand = (bSignificand >> @truncate(u7, @"align")) | sticky;
+ } else {
+ bSignificand = 1; // sticky; b is known to be non-zero.
+ }
+ }
+ if (subtraction) {
+ aSignificand -= bSignificand;
+ // If a == -b, return +zero.
+ if (aSignificand == 0) return @bitCast(T, Z(0));
+
+ // If partial cancellation occured, we need to left-shift the result
+ // and adjust the exponent:
+ if (aSignificand < implicitBit << 3) {
+ const shift = @intCast(i32, @clz(aSignificand)) - @intCast(i32, @clz(implicitBit << 3));
+ aSignificand <<= @intCast(u7, shift);
+ aExponent -= shift;
+ }
+ } else { // addition
+ aSignificand += bSignificand;
+
+ // If the addition carried up, we need to right-shift the result and
+ // adjust the exponent:
+ if (aSignificand & (implicitBit << 4) != 0) {
+ const sticky = aSignificand & 1;
+ aSignificand = aSignificand >> 1 | sticky;
+ aExponent += 1;
+ }
+ }
+
+ // If we have overflowed the type, return +/- infinity:
+ if (aExponent >= maxExponent) return @bitCast(T, infRep | resultSign);
+
+ if (aExponent <= 0) {
+ // Result is denormal before rounding; the exponent is zero and we
+ // need to shift the significand.
+ const shift = @intCast(Z, 1 - aExponent);
+ const sticky = if (aSignificand << @intCast(u7, typeWidth - shift) != 0) Z(1) else 0;
+ aSignificand = aSignificand >> @intCast(u7, shift | sticky);
+ aExponent = 0;
+ }
+
+ // Low three bits are round, guard, and sticky.
+ const roundGuardSticky = aSignificand & 0x7;
+
+ // Shift the significand into place, and mask off the implicit bit.
+ var result = (aSignificand >> 3) & significandMask;
+
+ // Insert the exponent and sign.
+ result |= @intCast(Z, aExponent) << significandBits;
+ result |= resultSign;
+
+ // Final rounding. The result may overflow to infinity, but that is the
+ // correct result in that case.
+ if (roundGuardSticky > 0x4) result += 1;
+ if (roundGuardSticky == 0x4) result += result & 1;
+
+ return @bitCast(T, result);
+}
+
+test "import addXf3" {
+ _ = @import("addXf3_test.zig");
+}
diff --git a/std/special/compiler_rt/addXf3_test.zig b/std/special/compiler_rt/addXf3_test.zig
new file mode 100644
index 0000000000..099b737976
--- /dev/null
+++ b/std/special/compiler_rt/addXf3_test.zig
@@ -0,0 +1,85 @@
+// Ported from:
+//
+// https://github.com/llvm/llvm-project/blob/02d85149a05cb1f6dc49f0ba7a2ceca53718ae17/compiler-rt/test/builtins/Unit/addtf3_test.c
+// https://github.com/llvm/llvm-project/blob/02d85149a05cb1f6dc49f0ba7a2ceca53718ae17/compiler-rt/test/builtins/Unit/subtf3_test.c
+
+const qnan128 = @bitCast(f128, u128(0x7fff800000000000) << 64);
+const inf128 = @bitCast(f128, u128(0x7fff000000000000) << 64);
+
+const __addtf3 = @import("addXf3.zig").__addtf3;
+
+fn test__addtf3(a: f128, b: f128, expected_hi: u64, expected_lo: u64) void {
+ const x = __addtf3(a, b);
+
+ const rep = @bitCast(u128, x);
+ const hi = @intCast(u64, rep >> 64);
+ const lo = @truncate(u64, rep);
+
+ if (hi == expected_hi and lo == expected_lo) {
+ return;
+ }
+ // test other possible NaN representation (signal NaN)
+ else if (expected_hi == 0x7fff800000000000 and expected_lo == 0x0) {
+ if ((hi & 0x7fff000000000000) == 0x7fff000000000000 and
+ ((hi & 0xffffffffffff) > 0 or lo > 0))
+ {
+ return;
+ }
+ }
+
+ @panic("__addtf3 test failure");
+}
+
+test "addtf3" {
+ test__addtf3(qnan128, 0x1.23456789abcdefp+5, 0x7fff800000000000, 0x0);
+
+ // NaN + any = NaN
+ test__addtf3(@bitCast(f128, (u128(0x7fff000000000000) << 64) | u128(0x800030000000)), 0x1.23456789abcdefp+5, 0x7fff800000000000, 0x0);
+
+ // inf + inf = inf
+ test__addtf3(inf128, inf128, 0x7fff000000000000, 0x0);
+
+ // inf + any = inf
+ test__addtf3(inf128, 0x1.2335653452436234723489432abcdefp+5, 0x7fff000000000000, 0x0);
+
+ // any + any
+ test__addtf3(0x1.23456734245345543849abcdefp+5, 0x1.edcba52449872455634654321fp-1, 0x40042afc95c8b579, 0x61e58dd6c51eb77c);
+}
+
+const __subtf3 = @import("addXf3.zig").__subtf3;
+
+fn test__subtf3(a: f128, b: f128, expected_hi: u64, expected_lo: u64) void {
+ const x = __subtf3(a, b);
+
+ const rep = @bitCast(u128, x);
+ const hi = @intCast(u64, rep >> 64);
+ const lo = @truncate(u64, rep);
+
+ if (hi == expected_hi and lo == expected_lo) {
+ return;
+ }
+ // test other possible NaN representation (signal NaN)
+ else if (expected_hi == 0x7fff800000000000 and expected_lo == 0x0) {
+ if ((hi & 0x7fff000000000000) == 0x7fff000000000000 and
+ ((hi & 0xffffffffffff) > 0 or lo > 0))
+ {
+ return;
+ }
+ }
+
+ @panic("__subtf3 test failure");
+}
+
+test "subtf3" {
+ // qNaN - any = qNaN
+ test__subtf3(qnan128, 0x1.23456789abcdefp+5, 0x7fff800000000000, 0x0);
+
+ // NaN + any = NaN
+ test__subtf3(@bitCast(f128, (u128(0x7fff000000000000) << 64) | u128(0x800030000000)), 0x1.23456789abcdefp+5, 0x7fff800000000000, 0x0);
+
+ // inf - any = inf
+ test__subtf3(inf128, 0x1.23456789abcdefp+5, 0x7fff000000000000, 0x0);
+
+ // any + any
+ test__subtf3(0x1.234567829a3bcdef5678ade36734p+5, 0x1.ee9d7c52354a6936ab8d7654321fp-1, 0x40041b8af1915166, 0xa44a7bca780a166c);
+}
diff --git a/std/special/compiler_rt/index.zig b/std/special/compiler_rt/index.zig
index 3df94db589..6715df1805 100644
--- a/std/special/compiler_rt/index.zig
+++ b/std/special/compiler_rt/index.zig
@@ -21,6 +21,9 @@ comptime {
@export("__unordtf2", @import("comparetf2.zig").__unordtf2, linkage);
+ @export("__addtf3", @import("addXf3.zig").__addtf3, linkage);
+ @export("__subtf3", @import("addXf3.zig").__subtf3, linkage);
+
@export("__floattitf", @import("floattitf.zig").__floattitf, linkage);
@export("__floattidf", @import("floattidf.zig").__floattidf, linkage);
@export("__floattisf", @import("floattisf.zig").__floattisf, linkage);
@@ -37,6 +40,7 @@ comptime {
@export("__extendhfsf2", @import("extendXfYf2.zig").__extendhfsf2, linkage);
@export("__truncsfhf2", @import("truncXfYf2.zig").__truncsfhf2, linkage);
+ @export("__truncdfhf2", @import("truncXfYf2.zig").__truncdfhf2, linkage);
@export("__trunctfdf2", @import("truncXfYf2.zig").__trunctfdf2, linkage);
@export("__trunctfsf2", @import("truncXfYf2.zig").__trunctfsf2, linkage);
diff --git a/std/special/compiler_rt/truncXfYf2.zig b/std/special/compiler_rt/truncXfYf2.zig
index 5cb2f61568..b385090a93 100644
--- a/std/special/compiler_rt/truncXfYf2.zig
+++ b/std/special/compiler_rt/truncXfYf2.zig
@@ -4,6 +4,10 @@ pub extern fn __truncsfhf2(a: f32) u16 {
return @bitCast(u16, truncXfYf2(f16, f32, a));
}
+pub extern fn __truncdfhf2(a: f64) u16 {
+ return @bitCast(u16, truncXfYf2(f16, f64, a));
+}
+
pub extern fn __trunctfsf2(a: f128) f32 {
return truncXfYf2(f32, f128, a);
}
diff --git a/std/special/compiler_rt/truncXfYf2_test.zig b/std/special/compiler_rt/truncXfYf2_test.zig
index c4bf2db733..429372c3f1 100644
--- a/std/special/compiler_rt/truncXfYf2_test.zig
+++ b/std/special/compiler_rt/truncXfYf2_test.zig
@@ -63,6 +63,74 @@ test "truncsfhf2" {
test__truncsfhf2(0x33000000, 0x0000); // 0x1.0p-25 -> zero
}
+const __truncdfhf2 = @import("truncXfYf2.zig").__truncdfhf2;
+
+fn test__truncdfhf2(a: f64, expected: u16) void {
+ const rep = @bitCast(u16, __truncdfhf2(a));
+
+ if (rep == expected) {
+ return;
+ }
+ // test other possible NaN representation(signal NaN)
+ else if (expected == 0x7e00) {
+ if ((rep & 0x7c00) == 0x7c00 and (rep & 0x3ff) > 0) {
+ return;
+ }
+ }
+
+ @panic("__truncdfhf2 test failure");
+}
+
+fn test__truncdfhf2_raw(a: u64, expected: u16) void {
+ const actual = __truncdfhf2(@bitCast(f64, a));
+
+ if (actual == expected) {
+ return;
+ }
+
+ @panic("__truncdfhf2 test failure");
+}
+
+test "truncdfhf2" {
+ test__truncdfhf2_raw(0x7ff8000000000000, 0x7e00); // qNaN
+ test__truncdfhf2_raw(0x7ff0000000008000, 0x7e00); // NaN
+
+ test__truncdfhf2_raw(0x7ff0000000000000, 0x7c00); //inf
+ test__truncdfhf2_raw(0xfff0000000000000, 0xfc00); // -inf
+
+ test__truncdfhf2(0.0, 0x0); // zero
+ test__truncdfhf2_raw(0x80000000 << 32, 0x8000); // -zero
+
+ test__truncdfhf2(3.1415926535, 0x4248);
+ test__truncdfhf2(-3.1415926535, 0xc248);
+
+ test__truncdfhf2(0x1.987124876876324p+1000, 0x7c00);
+ test__truncdfhf2(0x1.987124876876324p+12, 0x6e62);
+ test__truncdfhf2(0x1.0p+0, 0x3c00);
+ test__truncdfhf2(0x1.0p-14, 0x0400);
+
+ // denormal
+ test__truncdfhf2(0x1.0p-20, 0x0010);
+ test__truncdfhf2(0x1.0p-24, 0x0001);
+ test__truncdfhf2(-0x1.0p-24, 0x8001);
+ test__truncdfhf2(0x1.5p-25, 0x0001);
+
+ // and back to zero
+ test__truncdfhf2(0x1.0p-25, 0x0000);
+ test__truncdfhf2(-0x1.0p-25, 0x8000);
+
+ // max (precise)
+ test__truncdfhf2(65504.0, 0x7bff);
+
+ // max (rounded)
+ test__truncdfhf2(65519.0, 0x7bff);
+
+ // max (to +inf)
+ test__truncdfhf2(65520.0, 0x7c00);
+ test__truncdfhf2(-65520.0, 0xfc00);
+ test__truncdfhf2(65536.0, 0x7c00);
+}
+
const __trunctfsf2 = @import("truncXfYf2.zig").__trunctfsf2;
fn test__trunctfsf2(a: f128, expected: u32) void {