| author | |
| committer | |
| log | 60c2972c5d976e2dc8bf2432c8ab06edcf5c6d35 |
| tree | 2ed60b8283b58604b406446ed588f97f601b143d |
| parent | 615a98351768598db65fcfc521d3c67eb443eed6 |
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 | } |
| 326 | 326 | ||
| 327 | test "fmod, fmodf" { | 327 | test "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 | |
| 331 | 331 | 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 | |
| 337 | 337 | 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 | |
| 340 | 340 | 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 | |
| 346 | test "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 | } |
| 347 | 364 | ||
| 348 | test { | 365 | test { |
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 zero | 9847 | // 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 possible | 9851 | // * 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" { |
| 687 | 687 | ||
| 688 | try expect(@as(f128, 10384593717069655257060992658440192.0) + 1 == 10384593717069655257060992658440192.0); | 688 | try expect(@as(f128, 10384593717069655257060992658440192.0) + 1 == 10384593717069655257060992658440192.0); |
| 689 | } | 689 | } |
| 690 | |||
| 691 | test "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 | |||
| 697 | test "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 | |||
| 708 | test "comptime_float zero divided by zero produces zero" { | ||
| 709 | try expect((0.0 / 0.0) == 0.0); | ||
| 710 | } |