ziglang / zig

General-purpose programming language and toolchain for maintaining robust, optimal, and reusable software.
https://ziglang.org
MIT License
34.07k stars 2.49k forks source link

std.math.big.Rational produces different results in non-safe build modes #21069

Open scheibo opened 1 month ago

scheibo commented 1 month ago

Zig Version

0.14.0-dev.994+9f46abf59

Steps to Reproduce and Observed Behavior

const std = @import("std");

fn update(r: *std.math.big.Rational, p: u8, q: u8) !void {
    var s = try std.math.big.Rational.init(r.p.allocator);
    defer s.deinit();
    try s.setRatio(p, q);
    try r.mul(r.*, s);
}

test update {
    var r = try std.math.big.Rational.init(std.testing.allocator);
    defer r.deinit();
    var s = try std.math.big.Rational.init(std.testing.allocator);
    defer s.deinit();

    try r.setInt(1);

    try update(&r, 1, 2);
    for (0..2) |_| {
        try update(&r, 1, 2);
        try update(&r, 1, 2);
        try update(&r, 3, 4);
        for (0..10) |_| {
            try update(&r, 9, 10);
            try update(&r, 1, 15);
            try update(&r, 1, 24);
            try update(&r, 9, 10);
        }
    }

    const ZEROS = std.math.pow(u310, 10, 31);
    const n = 36472996377170786403;
    const d: u310 = 146267071761884981524886149560653378220687632500762654519853056 * ZEROS;
    try s.setRatio(n, d);
    try std.testing.expect((try s.order(r)) == .eq);
}
$ zig test -OReleaseSafe test.zig
All 1 tests passed.
$ zig test -OReleaseFast test.zig
1/1 test.decltest.update...FAIL (TestUnexpectedResult)
0 passed; 0 skipped; 1 failed.
error: the following test command failed with exit code 1:
/Users/kjs/.cache/zig/o/d29b5fd88b270c8a1f7854ef13eb81ec/test --seed=0xdcdde35b

Expected Behavior

The results should be the same in both Debug/ReleaseSafe and ReleaseFast/ReleaseSmall - if there is undefined behavior involved in the answers being different then ReleaseSafe should catch it.

pfgithub commented 1 month ago

Simpler reproduction:

const std = @import("std");

fn mul(a: *std.math.big.int.Managed, b: std.math.big.int.Managed, c: std.math.big.int.Managed) !void {
    try a.mul(&b, &c);
}

test "bigint error" {
    var a = try std.math.big.int.Managed.init(std.testing.allocator);
    defer a.deinit();
    var b = try std.math.big.int.Managed.init(std.testing.allocator);
    defer b.deinit();

    try a.set(536870912000000000000000000000000000000);
    try b.set(10);
    try mul(&a, a, b);

    try b.set(5368709120000000000000000000000000000000);
    try std.testing.expect(a.order(b) == .eq);
}

Succeeds on ReleaseFast/ReleaseSmall, fails on Debug/ReleaseSafe

pfgithub commented 1 month ago

Issues: