-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add big.Rational type to std #2102
Conversation
f338a2f
to
0a2e193
Compare
Interesting that the windows build failed. Will look closer tomorrow. |
Hmm I wonder why stack traces aren't working. That error log isn't particularly helpful. |
I opened it up in MSVC debugger and it seems to be an issue with compiler-rt:
stack trace:
|
Can't wait to make some fractals with this :D |
std/math/big/rational.zig
Outdated
// | ||
// r = gcd(x, y) where x, y > 0 | ||
fn gcdLehmer(r: *Int, xa: Int, ya: Int) !void { | ||
debug.assert(xa.positive and ya.positive); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change all debug.assert
's to use the testing
module.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But this isn't in a test
/// This function is intended to be used only in tests. When `ok` is false, the test fails.
/// A message is printed to stderr and then abort is called.
pub fn expect(ok: bool) void {
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, correct. I made this note on the wrong assert. Latest commit is updated to use testing
in appropriate places.
const SignedDoubleLimb = @IntType(true, DoubleLimb.bit_count); | ||
|
||
fn gcd(rma: *Int, x: Int, y: Int) !void { | ||
var r = rma; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assert that rma
is writable here.
|
||
for (str) |c, i| { | ||
switch (state) { | ||
State.Integer => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you like you can try out the new enum literal syntax here:
State.Integer => { | |
.Integer => { |
State.Integer => { | ||
switch (c) { | ||
'.' => { | ||
state = State.Fractional; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it would work here too:
state = State.Fractional; | |
state = .Fractional; |
Failing Int div case for 64-bit limbs (should fail on smaller limbs as well): test "big.int zero-limb underflow (3.3 !positive branch)" {
var a = try Int.initSet(al, 0x60000000000000000000000000000000000000000000000000000000000000000);
var b = try Int.initSet(al, 0x10000000000000000);
var q = try Int.init(al);
var r = try Int.init(al);
try Int.divTrunc(&q, &r, a, b);
var expected = try Int.initSet(al, 0x6000000000000000000000000000000000000000000000000);
testing.expect(q.eq(expected));
testing.expect(r.eqZero());
} Might do a fix in this PR. I'll probably cross-reference against the description in Knuth, since it is more widely used and might be an edge case not handled in the current. |
} | ||
|
||
pub fn isOdd(r: Int) bool { | ||
return r.limbs[0] & 1 != 0; | ||
pub fn isOdd(self: Int) bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this (and following methods) take a pointer to an Int
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why? isOdd
doesn't modify self
and you should only pass a const
pointer when you need the semantics of a pointer. I don't see the need for at pointer here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh. I forget that arguments are const
by default. Which I guess means that there won't be a copy?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right. In my mind I had already decided that it would be cheaper to pass by reference.... from the zen:
Communicate intent precisely.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Communicate intent precisely" also means don't communicate more than you intend to. Don't communicate that it needs to be a reference, if you don't need reference semantics.
r.q.swap(&other.q); | ||
} | ||
|
||
pub fn cmp(a: Rational, b: Rational) !i8 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should take a pointer again?
Should have covered the main issue with the division code. I still plan to fuzz the main operations against gmp and cover any other issues soon. |
@tiehuis do you want some help with the Windows compiler-rt issue? I don't know exactly what's wrong but maybe I can figure it out. |
That would be helpful, thanks. I don't have a Windows setup to test on |
Fixed all the main issues now with There is a remaining issue in the The remaining thing now is just the windows failure. |
I'll look into the window failure today. |
I wonder if this is related to #508. |
Decided to pack the length/sign bits as I had been meaning to do for a while. Int is now 32-bits (down from 40-bits) and Rational is 64 bits (down from 80 bits). Original discussion here: #1081 (comment) |
A constant Int is one which has a value of null for its allocator field. It cannot be resized or have its limbs written. Any attempt made to write to it will be caught with a runtime panic.
The int div code still causes some edge cases to fail right now.
This effectively takes one-bit from the length field and uses it as the sign bit. It reduces the size of an Int from 40 bits to 32 bits on a 64-bit arch. This also reduces std.Rational from 80 bits to 64 bits.
774c200
to
78af62a
Compare
Forgot to update the self-hosted compiler. Fixed and rebased against master. |
Self-explanatory. I've made changes to
Int
to now have the concept of a read-only, constant type. This is currently intended primarily for internal usage but could be used externally. Ideally we should still use a fixed-buffer approach but this is infallible and never fails which is nice. A read-only Int is one which has a null allocator. I've made the allocator null so we can catch invalid writes to these types at runtime and panic if needed.One note I've found when implementing the rational type is that there is an error in the Int division code. Using a Limb size of
u8
oru16
will cause the tests to fail. However, since this is an existing problem in the Int code I've decided to make this PR now anyway. I'll reduce this test-case and make a separate issue for this in the next few days.