Commit 60c2972c5d
Changed files (3)
lib/std/special/compiler_rt/fmod.zig
@@ -324,25 +324,42 @@ inline fn generic_fmod(comptime T: type, x: T, y: T) T {
return @bitCast(T, ux);
}
-test "fmod, fmodf" {
- inline for ([_]type{ f32, f64 }) |T| {
- const nan_val = math.nan(T);
- const inf_val = math.inf(T);
-
- try std.testing.expect(math.isNan(generic_fmod(T, nan_val, 1.0)));
- try std.testing.expect(math.isNan(generic_fmod(T, 1.0, nan_val)));
- try std.testing.expect(math.isNan(generic_fmod(T, inf_val, 1.0)));
- try std.testing.expect(math.isNan(generic_fmod(T, 0.0, 0.0)));
- try std.testing.expect(math.isNan(generic_fmod(T, 1.0, 0.0)));
-
- try std.testing.expectEqual(@as(T, 0.0), generic_fmod(T, 0.0, 2.0));
- try std.testing.expectEqual(@as(T, -0.0), generic_fmod(T, -0.0, 2.0));
-
- try std.testing.expectEqual(@as(T, -2.0), generic_fmod(T, -32.0, 10.0));
- try std.testing.expectEqual(@as(T, -2.0), generic_fmod(T, -32.0, -10.0));
- try std.testing.expectEqual(@as(T, 2.0), generic_fmod(T, 32.0, 10.0));
- try std.testing.expectEqual(@as(T, 2.0), generic_fmod(T, 32.0, -10.0));
- }
+test "fmodf" {
+ const nan_val = math.nan(f32);
+ const inf_val = math.inf(f32);
+
+ try std.testing.expect(math.isNan(fmodf(nan_val, 1.0)));
+ try std.testing.expect(math.isNan(fmodf(1.0, nan_val)));
+ try std.testing.expect(math.isNan(fmodf(inf_val, 1.0)));
+ try std.testing.expect(math.isNan(fmodf(0.0, 0.0)));
+ try std.testing.expect(math.isNan(fmodf(1.0, 0.0)));
+
+ try std.testing.expectEqual(@as(f32, 0.0), fmodf(0.0, 2.0));
+ try std.testing.expectEqual(@as(f32, -0.0), fmodf(-0.0, 2.0));
+
+ try std.testing.expectEqual(@as(f32, -2.0), fmodf(-32.0, 10.0));
+ try std.testing.expectEqual(@as(f32, -2.0), fmodf(-32.0, -10.0));
+ try std.testing.expectEqual(@as(f32, 2.0), fmodf(32.0, 10.0));
+ try std.testing.expectEqual(@as(f32, 2.0), fmodf(32.0, -10.0));
+}
+
+test "fmod" {
+ const nan_val = math.nan(f64);
+ const inf_val = math.inf(f64);
+
+ try std.testing.expect(math.isNan(fmod(nan_val, 1.0)));
+ try std.testing.expect(math.isNan(fmod(1.0, nan_val)));
+ try std.testing.expect(math.isNan(fmod(inf_val, 1.0)));
+ try std.testing.expect(math.isNan(fmod(0.0, 0.0)));
+ try std.testing.expect(math.isNan(fmod(1.0, 0.0)));
+
+ try std.testing.expectEqual(@as(f64, 0.0), fmod(0.0, 2.0));
+ try std.testing.expectEqual(@as(f64, -0.0), fmod(-0.0, 2.0));
+
+ try std.testing.expectEqual(@as(f64, -2.0), fmod(-32.0, 10.0));
+ try std.testing.expectEqual(@as(f64, -2.0), fmod(-32.0, -10.0));
+ try std.testing.expectEqual(@as(f64, 2.0), fmod(32.0, 10.0));
+ try std.testing.expectEqual(@as(f64, 2.0), fmod(32.0, -10.0));
}
test {
src/Sema.zig
@@ -9847,25 +9847,37 @@ fn analyzeArithmetic(
// TODO: emit runtime safety for division by zero
//
// For floats:
- // If the rhs is zero, compile error for division by zero.
- // If the rhs is undefined, compile error because there is a possible
- // value (zero) for which the division would be illegal behavior.
+ // If the rhs is zero:
+ // * comptime_float: compile error for division by zero.
+ // * other float type:
+ // * if the lhs is zero: QNaN
+ // * otherwise: +Inf or -Inf depending on lhs sign
+ // If the rhs is undefined:
+ // * comptime_float: compile error because there is a possible
+ // value (zero) for which the division would be illegal behavior.
+ // * other float type: result is undefined
// If the lhs is undefined, result is undefined.
- if (maybe_lhs_val) |lhs_val| {
- if (!lhs_val.isUndef()) {
- if (lhs_val.compareWithZero(.eq)) {
- return sema.addConstant(resolved_type, Value.zero);
+ switch (scalar_tag) {
+ .Int, .ComptimeInt, .ComptimeFloat => {
+ if (maybe_lhs_val) |lhs_val| {
+ if (!lhs_val.isUndef()) {
+ if (lhs_val.compareWithZero(.eq)) {
+ return sema.addConstant(resolved_type, Value.zero);
+ }
+ }
}
- }
- }
- if (maybe_rhs_val) |rhs_val| {
- if (rhs_val.isUndef()) {
- return sema.failWithUseOfUndef(block, rhs_src);
- }
- if (rhs_val.compareWithZero(.eq)) {
- return sema.failWithDivideByZero(block, rhs_src);
- }
+ if (maybe_rhs_val) |rhs_val| {
+ if (rhs_val.isUndef()) {
+ return sema.failWithUseOfUndef(block, rhs_src);
+ }
+ if (rhs_val.compareWithZero(.eq)) {
+ return sema.failWithDivideByZero(block, rhs_src);
+ }
+ }
+ },
+ else => {},
}
+
if (maybe_lhs_val) |lhs_val| {
if (lhs_val.isUndef()) {
if (lhs_scalar_ty.isSignedInt() and rhs_scalar_ty.isSignedInt()) {
test/behavior/floatop.zig
@@ -687,3 +687,24 @@ test "f128 at compile time is lossy" {
try expect(@as(f128, 10384593717069655257060992658440192.0) + 1 == 10384593717069655257060992658440192.0);
}
+
+test "comptime fixed-width float zero divided by zero produces NaN" {
+ inline for (.{ f16, f32, f64, f80, f128 }) |F| {
+ try expect(math.isNan(@as(F, 0) / @as(F, 0)));
+ }
+}
+
+test "comptime fixed-width float non-zero divided by zero produces signed Inf" {
+ inline for (.{ f16, f32, f64, f80, f128 }) |F| {
+ const pos = @as(F, 1) / @as(F, 0);
+ const neg = @as(F, -1) / @as(F, 0);
+ try expect(math.isInf(pos));
+ try expect(math.isInf(neg));
+ try expect(pos > 0);
+ try expect(neg < 0);
+ }
+}
+
+test "comptime_float zero divided by zero produces zero" {
+ try expect((0.0 / 0.0) == 0.0);
+}