authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-05-01 15:02:06-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-05-01 15:02:06-07:00
log60c2972c5d976e2dc8bf2432c8ab06edcf5c6d35
tree2ed60b8283b58604b406446ed588f97f601b143d
parent615a98351768598db65fcfc521d3c67eb443eed6

stage2: fix comptime fixed-width float division


3 files changed, 85 insertions(+), 35 deletions(-)

lib/std/special/compiler_rt/fmod.zig+36-19
...@@ -324,25 +324,42 @@ inline fn generic_fmod(comptime T: type, x: T, y: T) T {...@@ -324,25 +324,42 @@ inline fn generic_fmod(comptime T: type, x: T, y: T) T {
324 return @bitCast(T, ux);324 return @bitCast(T, ux);
325}325}
326326
327test "fmod, fmodf" {327test "fmodf" {
328 inline for ([_]type{ f32, f64 }) |T| {328 const nan_val = math.nan(f32);
329 const nan_val = math.nan(T);329 const inf_val = math.inf(f32);
330 const inf_val = math.inf(T);330
331331 try std.testing.expect(math.isNan(fmodf(nan_val, 1.0)));
332 try std.testing.expect(math.isNan(generic_fmod(T, nan_val, 1.0)));332 try std.testing.expect(math.isNan(fmodf(1.0, nan_val)));
333 try std.testing.expect(math.isNan(generic_fmod(T, 1.0, nan_val)));333 try std.testing.expect(math.isNan(fmodf(inf_val, 1.0)));
334 try std.testing.expect(math.isNan(generic_fmod(T, inf_val, 1.0)));334 try std.testing.expect(math.isNan(fmodf(0.0, 0.0)));
335 try std.testing.expect(math.isNan(generic_fmod(T, 0.0, 0.0)));335 try std.testing.expect(math.isNan(fmodf(1.0, 0.0)));
336 try std.testing.expect(math.isNan(generic_fmod(T, 1.0, 0.0)));336
337337 try std.testing.expectEqual(@as(f32, 0.0), fmodf(0.0, 2.0));
338 try std.testing.expectEqual(@as(T, 0.0), generic_fmod(T, 0.0, 2.0));338 try std.testing.expectEqual(@as(f32, -0.0), fmodf(-0.0, 2.0));
339 try std.testing.expectEqual(@as(T, -0.0), generic_fmod(T, -0.0, 2.0));339
340340 try std.testing.expectEqual(@as(f32, -2.0), fmodf(-32.0, 10.0));
341 try std.testing.expectEqual(@as(T, -2.0), generic_fmod(T, -32.0, 10.0));341 try std.testing.expectEqual(@as(f32, -2.0), fmodf(-32.0, -10.0));
342 try std.testing.expectEqual(@as(T, -2.0), generic_fmod(T, -32.0, -10.0));342 try std.testing.expectEqual(@as(f32, 2.0), fmodf(32.0, 10.0));
343 try std.testing.expectEqual(@as(T, 2.0), generic_fmod(T, 32.0, 10.0));343 try std.testing.expectEqual(@as(f32, 2.0), fmodf(32.0, -10.0));
344 try std.testing.expectEqual(@as(T, 2.0), generic_fmod(T, 32.0, -10.0));344}
345 }345
346test "fmod" {
347 const nan_val = math.nan(f64);
348 const inf_val = math.inf(f64);
349
350 try std.testing.expect(math.isNan(fmod(nan_val, 1.0)));
351 try std.testing.expect(math.isNan(fmod(1.0, nan_val)));
352 try std.testing.expect(math.isNan(fmod(inf_val, 1.0)));
353 try std.testing.expect(math.isNan(fmod(0.0, 0.0)));
354 try std.testing.expect(math.isNan(fmod(1.0, 0.0)));
355
356 try std.testing.expectEqual(@as(f64, 0.0), fmod(0.0, 2.0));
357 try std.testing.expectEqual(@as(f64, -0.0), fmod(-0.0, 2.0));
358
359 try std.testing.expectEqual(@as(f64, -2.0), fmod(-32.0, 10.0));
360 try std.testing.expectEqual(@as(f64, -2.0), fmod(-32.0, -10.0));
361 try std.testing.expectEqual(@as(f64, 2.0), fmod(32.0, 10.0));
362 try std.testing.expectEqual(@as(f64, 2.0), fmod(32.0, -10.0));
346}363}
347364
348test {365test {
src/Sema.zig+28-16
...@@ -9847,25 +9847,37 @@ fn analyzeArithmetic(...@@ -9847,25 +9847,37 @@ fn analyzeArithmetic(
9847 // TODO: emit runtime safety for division by zero9847 // TODO: emit runtime safety for division by zero
9848 //9848 //
9849 // For floats:9849 // For floats:
9850 // If the rhs is zero, compile error for division by zero.9850 // If the rhs is zero:
9851 // If the rhs is undefined, compile error because there is a possible9851 // * comptime_float: compile error for division by zero.
9852 // value (zero) for which the division would be illegal behavior.9852 // * other float type:
9853 // * if the lhs is zero: QNaN
9854 // * otherwise: +Inf or -Inf depending on lhs sign
9855 // If the rhs is undefined:
9856 // * comptime_float: compile error because there is a possible
9857 // value (zero) for which the division would be illegal behavior.
9858 // * other float type: result is undefined
9853 // If the lhs is undefined, result is undefined.9859 // If the lhs is undefined, result is undefined.
9854 if (maybe_lhs_val) |lhs_val| {9860 switch (scalar_tag) {
9855 if (!lhs_val.isUndef()) {9861 .Int, .ComptimeInt, .ComptimeFloat => {
9856 if (lhs_val.compareWithZero(.eq)) {9862 if (maybe_lhs_val) |lhs_val| {
9857 return sema.addConstant(resolved_type, Value.zero);9863 if (!lhs_val.isUndef()) {
9864 if (lhs_val.compareWithZero(.eq)) {
9865 return sema.addConstant(resolved_type, Value.zero);
9866 }
9867 }
9858 }9868 }
9859 }9869 if (maybe_rhs_val) |rhs_val| {
9860 }9870 if (rhs_val.isUndef()) {
9861 if (maybe_rhs_val) |rhs_val| {9871 return sema.failWithUseOfUndef(block, rhs_src);
9862 if (rhs_val.isUndef()) {9872 }
9863 return sema.failWithUseOfUndef(block, rhs_src);9873 if (rhs_val.compareWithZero(.eq)) {
9864 }9874 return sema.failWithDivideByZero(block, rhs_src);
9865 if (rhs_val.compareWithZero(.eq)) {9875 }
9866 return sema.failWithDivideByZero(block, rhs_src);9876 }
9867 }9877 },
9878 else => {},
9868 }9879 }
9880
9869 if (maybe_lhs_val) |lhs_val| {9881 if (maybe_lhs_val) |lhs_val| {
9870 if (lhs_val.isUndef()) {9882 if (lhs_val.isUndef()) {
9871 if (lhs_scalar_ty.isSignedInt() and rhs_scalar_ty.isSignedInt()) {9883 if (lhs_scalar_ty.isSignedInt() and rhs_scalar_ty.isSignedInt()) {
test/behavior/floatop.zig+21
...@@ -687,3 +687,24 @@ test "f128 at compile time is lossy" {...@@ -687,3 +687,24 @@ test "f128 at compile time is lossy" {
687687
688 try expect(@as(f128, 10384593717069655257060992658440192.0) + 1 == 10384593717069655257060992658440192.0);688 try expect(@as(f128, 10384593717069655257060992658440192.0) + 1 == 10384593717069655257060992658440192.0);
689}689}
690
691test "comptime fixed-width float zero divided by zero produces NaN" {
692 inline for (.{ f16, f32, f64, f80, f128 }) |F| {
693 try expect(math.isNan(@as(F, 0) / @as(F, 0)));
694 }
695}
696
697test "comptime fixed-width float non-zero divided by zero produces signed Inf" {
698 inline for (.{ f16, f32, f64, f80, f128 }) |F| {
699 const pos = @as(F, 1) / @as(F, 0);
700 const neg = @as(F, -1) / @as(F, 0);
701 try expect(math.isInf(pos));
702 try expect(math.isInf(neg));
703 try expect(pos > 0);
704 try expect(neg < 0);
705 }
706}
707
708test "comptime_float zero divided by zero produces zero" {
709 try expect((0.0 / 0.0) == 0.0);
710}