Sema: improved comptime % syntax

* comptime known 0 as a numerator returns comptime 0 independent of
   denominator.
 * negative numerator and denominator are allowed when the remainder is
   zero because that means the modulus would be also zero.
 * organize math behavior tests
This commit is contained in:
Andrew Kelley 2022-01-29 14:25:43 -07:00
parent 5e60ee4127
commit f8e418c47d
4 changed files with 467 additions and 422 deletions

View File

@ -8674,7 +8674,6 @@ fn analyzeArithmetic(
// For integers:
// Either operand being undef is a compile error because there exists
// a possible value (TODO what is it?) that would invoke illegal behavior.
// TODO: can lhs zero be handled better?
// TODO: can lhs undef be handled better?
//
// For floats:
@ -8690,8 +8689,8 @@ fn analyzeArithmetic(
if (lhs_val.isUndef()) {
return sema.failWithUseOfUndef(block, lhs_src);
}
if (lhs_val.compareWithZero(.lt)) {
return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty);
if (lhs_val.compareWithZero(.eq)) {
return sema.addConstant(scalar_type, Value.zero);
}
} else if (lhs_ty.isSignedInt()) {
return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty);
@ -8703,14 +8702,24 @@ fn analyzeArithmetic(
if (rhs_val.compareWithZero(.eq)) {
return sema.failWithDivideByZero(block, rhs_src);
}
if (rhs_val.compareWithZero(.lt)) {
return sema.failWithModRemNegative(block, rhs_src, lhs_ty, rhs_ty);
}
if (maybe_lhs_val) |lhs_val| {
return sema.addConstant(
scalar_type,
try lhs_val.intRem(rhs_val, sema.arena),
);
const rem_result = try lhs_val.intRem(rhs_val, sema.arena);
// If this answer could possibly be different by doing `intMod`,
// we must emit a compile error. Otherwise, it's OK.
if (rhs_val.compareWithZero(.lt) != lhs_val.compareWithZero(.lt) and
!rem_result.compareWithZero(.eq))
{
const bad_src = if (lhs_val.compareWithZero(.lt))
lhs_src
else
rhs_src;
return sema.failWithModRemNegative(block, bad_src, lhs_ty, rhs_ty);
}
if (lhs_val.compareWithZero(.lt)) {
// Negative
return sema.addConstant(scalar_type, Value.zero);
}
return sema.addConstant(scalar_type, rem_result);
}
break :rs .{ .src = lhs_src, .air_tag = .rem };
} else if (rhs_ty.isSignedInt()) {

View File

@ -153,7 +153,6 @@ test {
_ = @import("behavior/floatop_stage1.zig");
_ = @import("behavior/fn_delegation.zig");
_ = @import("behavior/ir_block_deps.zig");
_ = @import("behavior/math_stage1.zig");
_ = @import("behavior/misc.zig");
_ = @import("behavior/muladd.zig");
_ = @import("behavior/null_stage1.zig");

View File

@ -1,3 +1,4 @@
const builtin = @import("builtin");
const std = @import("std");
const expect = std.testing.expect;
const expectEqual = std.testing.expectEqual;
@ -5,6 +6,7 @@ const expectEqualSlices = std.testing.expectEqualSlices;
const maxInt = std.math.maxInt;
const minInt = std.math.minInt;
const mem = std.mem;
const has_f80_rt = builtin.cpu.arch == .x86_64;
test "assignment operators" {
var i: u32 = 0;
@ -612,3 +614,449 @@ test "overflow arithmetic with u0 values" {
try expect(!@shlWithOverflow(u0, 0, 0, &result));
try expect(result == 0);
}
test "allow signed integer division/remainder when values are comptime known and positive or exact" {
try expect(5 / 3 == 1);
try expect(-5 / -3 == 1);
try expect(-6 / 3 == -2);
try expect(5 % 3 == 2);
try expect(-6 % 3 == 0);
if (builtin.zig_backend != .stage1) {
var undef: i32 = undefined;
if (0 % undef != 0) {
@compileError("0 as numerator should return comptime zero independent of denominator");
}
}
}
test "quad hex float literal parsing accurate" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
const a: f128 = 0x1.1111222233334444555566667777p+0;
// implied 1 is dropped, with an exponent of 0 (0x3fff) after biasing.
const expected: u128 = 0x3fff1111222233334444555566667777;
try expect(@bitCast(u128, a) == expected);
// non-normalized
const b: f128 = 0x11.111222233334444555566667777p-4;
try expect(@bitCast(u128, b) == expected);
const S = struct {
fn doTheTest() !void {
{
var f: f128 = 0x1.2eab345678439abcdefea56782346p+5;
try expect(@bitCast(u128, f) == 0x40042eab345678439abcdefea5678234);
}
{
var f: f128 = 0x1.edcb34a235253948765432134674fp-1;
try expect(@bitCast(u128, f) == 0x3ffeedcb34a235253948765432134674);
}
{
var f: f128 = 0x1.353e45674d89abacc3a2ebf3ff4ffp-50;
try expect(@bitCast(u128, f) == 0x3fcd353e45674d89abacc3a2ebf3ff50);
}
{
var f: f128 = 0x1.ed8764648369535adf4be3214567fp-9;
try expect(@bitCast(u128, f) == 0x3ff6ed8764648369535adf4be3214568);
}
const exp2ft = [_]f64{
0x1.6a09e667f3bcdp-1,
0x1.7a11473eb0187p-1,
0x1.8ace5422aa0dbp-1,
0x1.9c49182a3f090p-1,
0x1.ae89f995ad3adp-1,
0x1.c199bdd85529cp-1,
0x1.d5818dcfba487p-1,
0x1.ea4afa2a490dap-1,
0x1.0000000000000p+0,
0x1.0b5586cf9890fp+0,
0x1.172b83c7d517bp+0,
0x1.2387a6e756238p+0,
0x1.306fe0a31b715p+0,
0x1.3dea64c123422p+0,
0x1.4bfdad5362a27p+0,
0x1.5ab07dd485429p+0,
0x1.8p23,
0x1.62e430p-1,
0x1.ebfbe0p-3,
0x1.c6b348p-5,
0x1.3b2c9cp-7,
0x1.0p127,
-0x1.0p-149,
};
const answers = [_]u64{
0x3fe6a09e667f3bcd,
0x3fe7a11473eb0187,
0x3fe8ace5422aa0db,
0x3fe9c49182a3f090,
0x3feae89f995ad3ad,
0x3fec199bdd85529c,
0x3fed5818dcfba487,
0x3feea4afa2a490da,
0x3ff0000000000000,
0x3ff0b5586cf9890f,
0x3ff172b83c7d517b,
0x3ff2387a6e756238,
0x3ff306fe0a31b715,
0x3ff3dea64c123422,
0x3ff4bfdad5362a27,
0x3ff5ab07dd485429,
0x4168000000000000,
0x3fe62e4300000000,
0x3fcebfbe00000000,
0x3fac6b3480000000,
0x3f83b2c9c0000000,
0x47e0000000000000,
0xb6a0000000000000,
};
for (exp2ft) |x, i| {
try expect(@bitCast(u64, x) == answers[i]);
}
}
};
try S.doTheTest();
comptime try S.doTheTest();
}
test "truncating shift left" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
try testShlTrunc(maxInt(u16));
comptime try testShlTrunc(maxInt(u16));
}
fn testShlTrunc(x: u16) !void {
const shifted = x << 1;
try expect(shifted == 65534);
}
test "exact shift left" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
try testShlExact(0b00110101);
comptime try testShlExact(0b00110101);
}
fn testShlExact(x: u8) !void {
const shifted = @shlExact(x, 2);
try expect(shifted == 0b11010100);
}
test "exact shift right" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
try testShrExact(0b10110100);
comptime try testShrExact(0b10110100);
}
fn testShrExact(x: u8) !void {
const shifted = @shrExact(x, 2);
try expect(shifted == 0b00101101);
}
test "shift left/right on u0 operand" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
const S = struct {
fn doTheTest() !void {
var x: u0 = 0;
var y: u0 = 0;
try expectEqual(@as(u0, 0), x << 0);
try expectEqual(@as(u0, 0), x >> 0);
try expectEqual(@as(u0, 0), x << y);
try expectEqual(@as(u0, 0), x >> y);
try expectEqual(@as(u0, 0), @shlExact(x, 0));
try expectEqual(@as(u0, 0), @shrExact(x, 0));
try expectEqual(@as(u0, 0), @shlExact(x, y));
try expectEqual(@as(u0, 0), @shrExact(x, y));
}
};
try S.doTheTest();
comptime try S.doTheTest();
}
test "comptime float rem int" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
comptime {
var x = @as(f32, 1) % 2;
try expect(x == 1.0);
}
}
test "remainder division" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
comptime try remdiv(f16);
comptime try remdiv(f32);
comptime try remdiv(f64);
comptime try remdiv(f128);
try remdiv(f16);
try remdiv(f64);
try remdiv(f128);
}
fn remdiv(comptime T: type) !void {
try expect(@as(T, 1) == @as(T, 1) % @as(T, 2));
try expect(@as(T, 1) == @as(T, 7) % @as(T, 3));
}
test "@sqrt" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
try testSqrt(f64, 12.0);
comptime try testSqrt(f64, 12.0);
try testSqrt(f32, 13.0);
comptime try testSqrt(f32, 13.0);
try testSqrt(f16, 13.0);
comptime try testSqrt(f16, 13.0);
const x = 14.0;
const y = x * x;
const z = @sqrt(y);
comptime try expect(z == x);
}
fn testSqrt(comptime T: type, x: T) !void {
try expect(@sqrt(x * x) == x);
}
test "@fabs" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
try testFabs(f128, 12.0);
comptime try testFabs(f128, 12.0);
if (has_f80_rt) try testFabs(f80, 12.0);
// comptime try testFabs(f80, 12.0);
try testFabs(f64, 12.0);
comptime try testFabs(f64, 12.0);
try testFabs(f32, 12.0);
comptime try testFabs(f32, 12.0);
try testFabs(f16, 12.0);
comptime try testFabs(f16, 12.0);
const x = 14.0;
const y = -x;
const z = @fabs(y);
comptime try expectEqual(x, z);
}
fn testFabs(comptime T: type, x: T) !void {
const y = -x;
const z = @fabs(y);
try expectEqual(x, z);
}
test "@floor" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
// FIXME: Generates a floorl function call
// testFloor(f128, 12.0);
comptime try testFloor(f128, 12.0);
// try testFloor(f80, 12.0);
comptime try testFloor(f80, 12.0);
try testFloor(f64, 12.0);
comptime try testFloor(f64, 12.0);
try testFloor(f32, 12.0);
comptime try testFloor(f32, 12.0);
try testFloor(f16, 12.0);
comptime try testFloor(f16, 12.0);
const x = 14.0;
const y = x + 0.7;
const z = @floor(y);
comptime try expectEqual(x, z);
}
fn testFloor(comptime T: type, x: T) !void {
const y = x + 0.6;
const z = @floor(y);
try expectEqual(x, z);
}
test "@ceil" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
// FIXME: Generates a ceill function call
//testCeil(f128, 12.0);
comptime try testCeil(f128, 12.0);
// try testCeil(f80, 12.0);
comptime try testCeil(f80, 12.0);
try testCeil(f64, 12.0);
comptime try testCeil(f64, 12.0);
try testCeil(f32, 12.0);
comptime try testCeil(f32, 12.0);
try testCeil(f16, 12.0);
comptime try testCeil(f16, 12.0);
const x = 14.0;
const y = x - 0.7;
const z = @ceil(y);
comptime try expectEqual(x, z);
}
fn testCeil(comptime T: type, x: T) !void {
const y = x - 0.8;
const z = @ceil(y);
try expectEqual(x, z);
}
test "@trunc" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
// FIXME: Generates a truncl function call
//testTrunc(f128, 12.0);
comptime try testTrunc(f128, 12.0);
// try testTrunc(f80, 12.0);
// comptime try testTrunc(f80, 12.0);
comptime {
const x: f80 = 12.0;
const y = x + 0.8;
const z = @trunc(y);
try expectEqual(x, z);
}
try testTrunc(f64, 12.0);
comptime try testTrunc(f64, 12.0);
try testTrunc(f32, 12.0);
comptime try testTrunc(f32, 12.0);
try testTrunc(f16, 12.0);
comptime try testTrunc(f16, 12.0);
const x = 14.0;
const y = x + 0.7;
const z = @trunc(y);
comptime try expectEqual(x, z);
}
fn testTrunc(comptime T: type, x: T) !void {
{
const y = x + 0.8;
const z = @trunc(y);
try expectEqual(x, z);
}
{
const y = -x - 0.8;
const z = @trunc(y);
try expectEqual(-x, z);
}
}
test "@round" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
// FIXME: Generates a roundl function call
//testRound(f128, 12.0);
comptime try testRound(f128, 12.0);
// try testRound(f80, 12.0);
comptime try testRound(f80, 12.0);
try testRound(f64, 12.0);
comptime try testRound(f64, 12.0);
try testRound(f32, 12.0);
comptime try testRound(f32, 12.0);
try testRound(f16, 12.0);
comptime try testRound(f16, 12.0);
const x = 14.0;
const y = x + 0.4;
const z = @round(y);
comptime try expectEqual(x, z);
}
fn testRound(comptime T: type, x: T) !void {
const y = x - 0.5;
const z = @round(y);
try expectEqual(x, z);
}
test "vector integer addition" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
const S = struct {
fn doTheTest() !void {
var a: std.meta.Vector(4, i32) = [_]i32{ 1, 2, 3, 4 };
var b: std.meta.Vector(4, i32) = [_]i32{ 5, 6, 7, 8 };
var result = a + b;
var result_array: [4]i32 = result;
const expected = [_]i32{ 6, 8, 10, 12 };
try expectEqualSlices(i32, &expected, &result_array);
}
};
try S.doTheTest();
comptime try S.doTheTest();
}
test "NaN comparison" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
try testNanEqNan(f16);
try testNanEqNan(f32);
try testNanEqNan(f64);
try testNanEqNan(f128);
if (has_f80_rt) try testNanEqNan(f80);
comptime try testNanEqNan(f16);
comptime try testNanEqNan(f32);
comptime try testNanEqNan(f64);
comptime try testNanEqNan(f128);
// comptime try testNanEqNan(f80);
}
fn testNanEqNan(comptime F: type) !void {
var nan1 = std.math.nan(F);
var nan2 = std.math.nan(F);
try expect(nan1 != nan2);
try expect(!(nan1 == nan2));
try expect(!(nan1 > nan2));
try expect(!(nan1 >= nan2));
try expect(!(nan1 < nan2));
try expect(!(nan1 <= nan2));
}
test "vector comparison" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
const S = struct {
fn doTheTest() !void {
var a: std.meta.Vector(6, i32) = [_]i32{ 1, 3, -1, 5, 7, 9 };
var b: std.meta.Vector(6, i32) = [_]i32{ -1, 3, 0, 6, 10, -10 };
try expect(mem.eql(bool, &@as([6]bool, a < b), &[_]bool{ false, false, true, true, true, false }));
try expect(mem.eql(bool, &@as([6]bool, a <= b), &[_]bool{ false, true, true, true, true, false }));
try expect(mem.eql(bool, &@as([6]bool, a == b), &[_]bool{ false, true, false, false, false, false }));
try expect(mem.eql(bool, &@as([6]bool, a != b), &[_]bool{ true, false, true, true, true, true }));
try expect(mem.eql(bool, &@as([6]bool, a > b), &[_]bool{ true, false, false, false, false, true }));
try expect(mem.eql(bool, &@as([6]bool, a >= b), &[_]bool{ true, true, false, false, false, true }));
}
};
try S.doTheTest();
comptime try S.doTheTest();
}
test "compare undefined literal with comptime_int" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
var x = undefined == 1;
// x is now undefined with type bool
x = true;
try expect(x);
}
test "signed zeros are represented properly" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
const S = struct {
fn doTheTest() !void {
inline for ([_]type{ f16, f32, f64, f128 }) |T| {
const ST = std.meta.Int(.unsigned, @typeInfo(T).Float.bits);
var as_fp_val = -@as(T, 0.0);
var as_uint_val = @bitCast(ST, as_fp_val);
// Ensure the sign bit is set.
try expect(as_uint_val >> (@typeInfo(T).Float.bits - 1) == 1);
}
}
};
try S.doTheTest();
comptime try S.doTheTest();
}

View File

@ -1,411 +0,0 @@
const std = @import("std");
const expect = std.testing.expect;
const expectEqual = std.testing.expectEqual;
const expectEqualSlices = std.testing.expectEqualSlices;
const maxInt = std.math.maxInt;
const minInt = std.math.minInt;
const mem = std.mem;
const has_f80_rt = @import("builtin").cpu.arch == .x86_64;
test "allow signed integer division/remainder when values are comptime known and positive or exact" {
try expect(5 / 3 == 1);
try expect(-5 / -3 == 1);
try expect(-6 / 3 == -2);
try expect(5 % 3 == 2);
try expect(-6 % 3 == 0);
}
test "quad hex float literal parsing accurate" {
const a: f128 = 0x1.1111222233334444555566667777p+0;
// implied 1 is dropped, with an exponent of 0 (0x3fff) after biasing.
const expected: u128 = 0x3fff1111222233334444555566667777;
try expect(@bitCast(u128, a) == expected);
// non-normalized
const b: f128 = 0x11.111222233334444555566667777p-4;
try expect(@bitCast(u128, b) == expected);
const S = struct {
fn doTheTest() !void {
{
var f: f128 = 0x1.2eab345678439abcdefea56782346p+5;
try expect(@bitCast(u128, f) == 0x40042eab345678439abcdefea5678234);
}
{
var f: f128 = 0x1.edcb34a235253948765432134674fp-1;
try expect(@bitCast(u128, f) == 0x3ffeedcb34a235253948765432134674);
}
{
var f: f128 = 0x1.353e45674d89abacc3a2ebf3ff4ffp-50;
try expect(@bitCast(u128, f) == 0x3fcd353e45674d89abacc3a2ebf3ff50);
}
{
var f: f128 = 0x1.ed8764648369535adf4be3214567fp-9;
try expect(@bitCast(u128, f) == 0x3ff6ed8764648369535adf4be3214568);
}
const exp2ft = [_]f64{
0x1.6a09e667f3bcdp-1,
0x1.7a11473eb0187p-1,
0x1.8ace5422aa0dbp-1,
0x1.9c49182a3f090p-1,
0x1.ae89f995ad3adp-1,
0x1.c199bdd85529cp-1,
0x1.d5818dcfba487p-1,
0x1.ea4afa2a490dap-1,
0x1.0000000000000p+0,
0x1.0b5586cf9890fp+0,
0x1.172b83c7d517bp+0,
0x1.2387a6e756238p+0,
0x1.306fe0a31b715p+0,
0x1.3dea64c123422p+0,
0x1.4bfdad5362a27p+0,
0x1.5ab07dd485429p+0,
0x1.8p23,
0x1.62e430p-1,
0x1.ebfbe0p-3,
0x1.c6b348p-5,
0x1.3b2c9cp-7,
0x1.0p127,
-0x1.0p-149,
};
const answers = [_]u64{
0x3fe6a09e667f3bcd,
0x3fe7a11473eb0187,
0x3fe8ace5422aa0db,
0x3fe9c49182a3f090,
0x3feae89f995ad3ad,
0x3fec199bdd85529c,
0x3fed5818dcfba487,
0x3feea4afa2a490da,
0x3ff0000000000000,
0x3ff0b5586cf9890f,
0x3ff172b83c7d517b,
0x3ff2387a6e756238,
0x3ff306fe0a31b715,
0x3ff3dea64c123422,
0x3ff4bfdad5362a27,
0x3ff5ab07dd485429,
0x4168000000000000,
0x3fe62e4300000000,
0x3fcebfbe00000000,
0x3fac6b3480000000,
0x3f83b2c9c0000000,
0x47e0000000000000,
0xb6a0000000000000,
};
for (exp2ft) |x, i| {
try expect(@bitCast(u64, x) == answers[i]);
}
}
};
try S.doTheTest();
comptime try S.doTheTest();
}
test "truncating shift left" {
try testShlTrunc(maxInt(u16));
comptime try testShlTrunc(maxInt(u16));
}
fn testShlTrunc(x: u16) !void {
const shifted = x << 1;
try expect(shifted == 65534);
}
test "exact shift left" {
try testShlExact(0b00110101);
comptime try testShlExact(0b00110101);
}
fn testShlExact(x: u8) !void {
const shifted = @shlExact(x, 2);
try expect(shifted == 0b11010100);
}
test "exact shift right" {
try testShrExact(0b10110100);
comptime try testShrExact(0b10110100);
}
fn testShrExact(x: u8) !void {
const shifted = @shrExact(x, 2);
try expect(shifted == 0b00101101);
}
test "shift left/right on u0 operand" {
const S = struct {
fn doTheTest() !void {
var x: u0 = 0;
var y: u0 = 0;
try expectEqual(@as(u0, 0), x << 0);
try expectEqual(@as(u0, 0), x >> 0);
try expectEqual(@as(u0, 0), x << y);
try expectEqual(@as(u0, 0), x >> y);
try expectEqual(@as(u0, 0), @shlExact(x, 0));
try expectEqual(@as(u0, 0), @shrExact(x, 0));
try expectEqual(@as(u0, 0), @shlExact(x, y));
try expectEqual(@as(u0, 0), @shrExact(x, y));
}
};
try S.doTheTest();
comptime try S.doTheTest();
}
test "comptime float rem int" {
comptime {
var x = @as(f32, 1) % 2;
try expect(x == 1.0);
}
}
test "remainder division" {
comptime try remdiv(f16);
comptime try remdiv(f32);
comptime try remdiv(f64);
comptime try remdiv(f128);
try remdiv(f16);
try remdiv(f64);
try remdiv(f128);
}
fn remdiv(comptime T: type) !void {
try expect(@as(T, 1) == @as(T, 1) % @as(T, 2));
try expect(@as(T, 1) == @as(T, 7) % @as(T, 3));
}
test "@sqrt" {
try testSqrt(f64, 12.0);
comptime try testSqrt(f64, 12.0);
try testSqrt(f32, 13.0);
comptime try testSqrt(f32, 13.0);
try testSqrt(f16, 13.0);
comptime try testSqrt(f16, 13.0);
const x = 14.0;
const y = x * x;
const z = @sqrt(y);
comptime try expect(z == x);
}
fn testSqrt(comptime T: type, x: T) !void {
try expect(@sqrt(x * x) == x);
}
test "@fabs" {
try testFabs(f128, 12.0);
comptime try testFabs(f128, 12.0);
if (has_f80_rt) try testFabs(f80, 12.0);
// comptime try testFabs(f80, 12.0);
try testFabs(f64, 12.0);
comptime try testFabs(f64, 12.0);
try testFabs(f32, 12.0);
comptime try testFabs(f32, 12.0);
try testFabs(f16, 12.0);
comptime try testFabs(f16, 12.0);
const x = 14.0;
const y = -x;
const z = @fabs(y);
comptime try expectEqual(x, z);
}
fn testFabs(comptime T: type, x: T) !void {
const y = -x;
const z = @fabs(y);
try expectEqual(x, z);
}
test "@floor" {
// FIXME: Generates a floorl function call
// testFloor(f128, 12.0);
comptime try testFloor(f128, 12.0);
// try testFloor(f80, 12.0);
comptime try testFloor(f80, 12.0);
try testFloor(f64, 12.0);
comptime try testFloor(f64, 12.0);
try testFloor(f32, 12.0);
comptime try testFloor(f32, 12.0);
try testFloor(f16, 12.0);
comptime try testFloor(f16, 12.0);
const x = 14.0;
const y = x + 0.7;
const z = @floor(y);
comptime try expectEqual(x, z);
}
fn testFloor(comptime T: type, x: T) !void {
const y = x + 0.6;
const z = @floor(y);
try expectEqual(x, z);
}
test "@ceil" {
// FIXME: Generates a ceill function call
//testCeil(f128, 12.0);
comptime try testCeil(f128, 12.0);
// try testCeil(f80, 12.0);
comptime try testCeil(f80, 12.0);
try testCeil(f64, 12.0);
comptime try testCeil(f64, 12.0);
try testCeil(f32, 12.0);
comptime try testCeil(f32, 12.0);
try testCeil(f16, 12.0);
comptime try testCeil(f16, 12.0);
const x = 14.0;
const y = x - 0.7;
const z = @ceil(y);
comptime try expectEqual(x, z);
}
fn testCeil(comptime T: type, x: T) !void {
const y = x - 0.8;
const z = @ceil(y);
try expectEqual(x, z);
}
test "@trunc" {
// FIXME: Generates a truncl function call
//testTrunc(f128, 12.0);
comptime try testTrunc(f128, 12.0);
// try testTrunc(f80, 12.0);
// comptime try testTrunc(f80, 12.0);
comptime {
const x: f80 = 12.0;
const y = x + 0.8;
const z = @trunc(y);
try expectEqual(x, z);
}
try testTrunc(f64, 12.0);
comptime try testTrunc(f64, 12.0);
try testTrunc(f32, 12.0);
comptime try testTrunc(f32, 12.0);
try testTrunc(f16, 12.0);
comptime try testTrunc(f16, 12.0);
const x = 14.0;
const y = x + 0.7;
const z = @trunc(y);
comptime try expectEqual(x, z);
}
fn testTrunc(comptime T: type, x: T) !void {
{
const y = x + 0.8;
const z = @trunc(y);
try expectEqual(x, z);
}
{
const y = -x - 0.8;
const z = @trunc(y);
try expectEqual(-x, z);
}
}
test "@round" {
// FIXME: Generates a roundl function call
//testRound(f128, 12.0);
comptime try testRound(f128, 12.0);
// try testRound(f80, 12.0);
comptime try testRound(f80, 12.0);
try testRound(f64, 12.0);
comptime try testRound(f64, 12.0);
try testRound(f32, 12.0);
comptime try testRound(f32, 12.0);
try testRound(f16, 12.0);
comptime try testRound(f16, 12.0);
const x = 14.0;
const y = x + 0.4;
const z = @round(y);
comptime try expectEqual(x, z);
}
fn testRound(comptime T: type, x: T) !void {
const y = x - 0.5;
const z = @round(y);
try expectEqual(x, z);
}
test "vector integer addition" {
const S = struct {
fn doTheTest() !void {
var a: std.meta.Vector(4, i32) = [_]i32{ 1, 2, 3, 4 };
var b: std.meta.Vector(4, i32) = [_]i32{ 5, 6, 7, 8 };
var result = a + b;
var result_array: [4]i32 = result;
const expected = [_]i32{ 6, 8, 10, 12 };
try expectEqualSlices(i32, &expected, &result_array);
}
};
try S.doTheTest();
comptime try S.doTheTest();
}
test "NaN comparison" {
try testNanEqNan(f16);
try testNanEqNan(f32);
try testNanEqNan(f64);
try testNanEqNan(f128);
if (has_f80_rt) try testNanEqNan(f80);
comptime try testNanEqNan(f16);
comptime try testNanEqNan(f32);
comptime try testNanEqNan(f64);
comptime try testNanEqNan(f128);
// comptime try testNanEqNan(f80);
}
fn testNanEqNan(comptime F: type) !void {
var nan1 = std.math.nan(F);
var nan2 = std.math.nan(F);
try expect(nan1 != nan2);
try expect(!(nan1 == nan2));
try expect(!(nan1 > nan2));
try expect(!(nan1 >= nan2));
try expect(!(nan1 < nan2));
try expect(!(nan1 <= nan2));
}
test "vector comparison" {
const S = struct {
fn doTheTest() !void {
var a: std.meta.Vector(6, i32) = [_]i32{ 1, 3, -1, 5, 7, 9 };
var b: std.meta.Vector(6, i32) = [_]i32{ -1, 3, 0, 6, 10, -10 };
try expect(mem.eql(bool, &@as([6]bool, a < b), &[_]bool{ false, false, true, true, true, false }));
try expect(mem.eql(bool, &@as([6]bool, a <= b), &[_]bool{ false, true, true, true, true, false }));
try expect(mem.eql(bool, &@as([6]bool, a == b), &[_]bool{ false, true, false, false, false, false }));
try expect(mem.eql(bool, &@as([6]bool, a != b), &[_]bool{ true, false, true, true, true, true }));
try expect(mem.eql(bool, &@as([6]bool, a > b), &[_]bool{ true, false, false, false, false, true }));
try expect(mem.eql(bool, &@as([6]bool, a >= b), &[_]bool{ true, true, false, false, false, true }));
}
};
try S.doTheTest();
comptime try S.doTheTest();
}
test "compare undefined literal with comptime_int" {
var x = undefined == 1;
// x is now undefined with type bool
x = true;
try expect(x);
}
test "signed zeros are represented properly" {
const S = struct {
fn doTheTest() !void {
inline for ([_]type{ f16, f32, f64, f128 }) |T| {
const ST = std.meta.Int(.unsigned, @typeInfo(T).Float.bits);
var as_fp_val = -@as(T, 0.0);
var as_uint_val = @bitCast(ST, as_fp_val);
// Ensure the sign bit is set.
try expect(as_uint_val >> (@typeInfo(T).Float.bits - 1) == 1);
}
}
};
try S.doTheTest();
comptime try S.doTheTest();
}