-
-
Notifications
You must be signed in to change notification settings - Fork 1.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
Additions to Big arithmetics #4653
Conversation
src/big/big_float.cr
Outdated
module Math | ||
def frexp(value : BigFloat) | ||
frac = LibGMP.mpf_get_d_2exp(out exp, value) | ||
{frac, exp} |
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.
Implementation for exact calculation (for example, for exact hashing) should look like
module Math
def frexp(value : BigFloat)
LibGMP.mpf_get_d_2exp(out exp, value)
frac = BigFloat.new { |mpf|
if exp >= 0
LibGMP.mpf_div_2exp(mpf, self, exp)
else
LibGMP.mpf_mul_2exp(mpf, self, -exp)
end
}
{frac, exp}
end
end
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.
Thanks, will do
src/big/big_int.cr
Outdated
|
||
module Math | ||
def frexp(value : BigInt) | ||
frac = LibGMP.get_d_2exp(out exp, value) |
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.
I'd rather fallback to frexp(BigFloat.new(value))
.
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.
Just thinking about -)
I've made BigFloat.new(BigInt) and BigFloat.new(BigRational) as a part of #4675. |
(but i forgot to add I mean: there will be collisions between this PR and #4675. |
It's ok. And to_big_f is crystal way. |
May be we need to copy to_big_f to your PR, because it's usage looks more clear. |
I found |
require "big"
def tst(a, b)
p BigRational.new(a,b).to_big_f
p BigFloat.new(a) / BigFloat.new(b)
end
tst(10000000000000001, 10000000000000000)
|
Yes, it looks like my version of |
I see: compiler ignores my |
Fixed : it was need to Note: if you do BigFloat constructors for every type (like in my PR), then there will be no need to add |
Thanks, @funny-falcon for clarification but this is strange. Anyway I'll try to copy GMP related logic here to simplify any merge etc. |
Hmm, dropping of
|
The ASTNode of `1 <= 2 <= 3` is `to_s`-ed to `(1 <= 2) <= 3`. Right side parenthesis is not required and it causes crystal-lang#4653 error.
@akzhan build is failing because of formatter. |
@RX14 Fixed, thanks. |
src/big/big_float.cr
Outdated
@@ -1,5 +1,6 @@ | |||
require "c/string" | |||
require "./big" | |||
require "./big_rational" # Strange but required for working BigFloat.new(BigRational) on Travis CI. |
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 looks like it didn't help :/
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.
Bad kind of magic was restored not exactly as @funny-falcon proposed. Just updated (сross fingers). Will create issue for this.
@funny-falcon this trick doesn't help :-( |
@Sija @funny-falcon I'll drop constructor(BigRational) due to it's unpredictable usage behavior by current Crystal compiler. Anyway we should prefer |
shouldn't constructor(BigRational) be placed in |
Ok, will try :) |
src/big/big_float.cr
Outdated
end | ||
|
||
def /(other : UInt8 | UInt16 | UInt32) | ||
raise DivisionByZero.new if other == 0 |
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.
How about just self / LibGMP::ULong.new(other)
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's ok too.
Excuse me, I was wrong in previous comment. |
@RX14 looks like ready to approve |
@akzhan here is how it could be reproduced on master (and my branch) quite easy:
|
@RX14 thanks to @funny-falcon - now broken overload issue has been extracted to #4897 |
src/big/big_float.cr
Outdated
@@ -18,7 +18,37 @@ struct BigFloat < Float | |||
end | |||
|
|||
def initialize(num : Number) | |||
LibGMP.mpf_init_set_d(out @mpf, num.to_f64) | |||
case num |
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.
Does this new arrangement where there is no Number
overload trigger the bug? If no we should use overloads 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.
Yes, it is workaround.
Here was comment "XXX fix me when overload will work", but Akzhan removed it.
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.
Now will be extracted as issue and link added
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 does this specific code still break the specs. If it does not (and I don't believe it does, as you removed the Int
overload) the workaround should be removed.
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.
Just tested, extracted overloads works ok.
But it doesn't OK because I don't know why it sometimes didn't work - as exposed by #4897.
Compiler has unpredictable behavior to choose which overload will be chosen :-/
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.
@RX14 - Moreover, added overloads break specs at all. Again.
1) BigFloat new new(BigInt) Failure/Error: bigfloat_on_bigint_value.should eq(bigfloat_of_integer_value) Expected: 123456789012345678901_big_f got: 123456789012345667584_big_f # spec/std/big/big_float_spec.cr:18 2) BigFloat new new(BigRational) Failure/Error: bigfloat_on_bigrational_value.should eq(BigFloat.new(1) / BigFloat.new(3)) Expected: 0.333333333333333333333_big_f got: 0.33333333333333331483_big_f
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.
I hate Crystal compiler overloads for now. They have unpredictable behavior.
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.
The bug is very clearly related to using Number
or Int
overloads (both of which are superclasses of BigInt
). Simply avoid such generic overloads. Your case
statement didn't have them, you don't need them.
There is no need to be "scared" of a bug, there is almost 0 chance that the bug will depend on anything but the list of overloads and their order. The bug seems fairly predictable to me.
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.
I have initialize(num : Number) anyway
@RX14 What can I do with these compiler bugs after returning of overloads? I want to rollback proposed overloads due to its erroneous. by the way - bin/crystal spec/std/big/big_float_spec.cr works ok (failed only std spec) Previous solution works fine in any conditions. No Crystal Voodoo overloads magic. |
@RX14 Travis-CI failed with constructor overloads. Until compiler fixed, code should be reverted to 'case' overload. |
LibGMP.mpf_set_z(self, num.to_big_i) | ||
end | ||
end | ||
|
||
def initialize(num : Number) |
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.
As I suspected, removing this type restriction fixes the issue.
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, def initialize(num)
works.
But it is ridiculous.
Should this method accept anything with #to_f64
method, or it should accept only Number?
If latter, then this restriction could not be removed.
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.
I'd rather merge this with the overloads working and not have to change it later than merge it with the ugly case + comment in initialize. We can reproduce the failure so we can investigate it in parallel.
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.
Just fix overloads by forward declaration of types as @asterite points to.
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.
Finally!
Good time to merge :-) |
Need another review on this before it's merged. |
Prepare hash infrastructor to future change of hashing algrorithm to protect against Hash DoS. Class|Struct should define method `def hash(hasher)` and call `hasher << @ivar` inside. As an option, for speed, and for backward compatibility, `def hash` still could be implemented. It will be used for Hash of matched type. `Thread#hash` and `Signal#hash` is implemented as unseeded cause they are used before `StdHasher @@seed` is initialized. Hash::Hasher is default hasher that uses `hash(hasher)` and it is used as default seeded hasher. Also, number normalization for hashing introduced, ie rule 'equality forces hash equality' is forced (`a == b` => `a.hash == b.hash`). Normalization idea is borrowed from Python implementation. It fixes several issues with BigInt and BigFloat on 32bit platform, but not all issues. Fixes crystal-lang#4578 Fixes crystal-lang#3932 Prerequisite for crystal-lang#4557 Replaces crystal-lang#4581 Correlates with crystal-lang#4653
Prepare hash infrastructor to future change of hashing algrorithm to protect against Hash DoS. Class|Struct should define method `def hash(hasher)` and call `hasher << @ivar` inside. As an option, for speed, and for backward compatibility, `def hash` still could be implemented. It will be used for Hash of matched type. `Thread#hash` and `Signal#hash` is implemented as unseeded cause they are used before `StdHasher @@seed` is initialized. Hash::Hasher is default hasher that uses `hash(hasher)` and it is used as default seeded hasher. Also, number normalization for hashing introduced, ie rule 'equality forces hash equality' is forced (`a == b` => `a.hash == b.hash`). Normalization idea is borrowed from Python implementation. It fixes several issues with BigInt and BigFloat on 32bit platform, but not all issues. Fixes crystal-lang#4578 Fixes crystal-lang#3932 Prerequisite for crystal-lang#4557 Replaces crystal-lang#4581 Correlates with crystal-lang#4653
Prepare hash infrastructor to future change of hashing algrorithm to protect against Hash DoS. Class|Struct should define method `def hash(hasher)` and call `hasher << @ivar` inside. As an option, for speed, and for backward compatibility, `def hash` still could be implemented. It will be used for Hash of matched type. `Thread#hash` and `Signal#hash` is implemented as unseeded cause they are used before `StdHasher @@seed` is initialized. Hash::Hasher is default hasher that uses `hash(hasher)` and it is used as default seeded hasher. Also, number normalization for hashing introduced, ie rule 'equality forces hash equality' is forced (`a == b` => `a.hash == b.hash`). Normalization idea is borrowed from Python implementation. It fixes several issues with BigInt and BigFloat on 32bit platform, but not all issues. Fixes crystal-lang#4578 Fixes crystal-lang#3932 Prerequisite for crystal-lang#4557 Replaces crystal-lang#4581 Correlates with crystal-lang#4653
Prepare hash infrastructor to future change of hashing algrorithm to protect against Hash DoS. Class|Struct should define method `def hash(hasher)` and call `hasher << @ivar` inside. As an option, for speed, and for backward compatibility, `def hash` still could be implemented. It will be used for Hash of matched type. `Thread#hash` and `Signal#hash` is implemented as unseeded cause they are used before `StdHasher @@seed` is initialized. Hash::Hasher is default hasher that uses `hash(hasher)` and it is used as default seeded hasher. Also, number normalization for hashing introduced, ie rule 'equality forces hash equality' is forced (`a == b` => `a.hash == b.hash`). Normalization idea is borrowed from Python implementation. It fixes several issues with BigInt and BigFloat on 32bit platform, but not all issues. Fixes crystal-lang#4578 Fixes crystal-lang#3932 Prerequisite for crystal-lang#4557 Replaces crystal-lang#4581 Correlates with crystal-lang#4653
Prepare hash infrastructor to future change of hashing algrorithm to protect against Hash DoS. Class|Struct should define method `def hash(hasher)` and call `hasher << @ivar` inside. As an option, for speed, and for backward compatibility, `def hash` still could be implemented. It will be used for Hash of matched type. `Thread#hash` and `Signal#hash` is implemented as unseeded cause they are used before `StdHasher @@seed` is initialized. Hash::Hasher is default hasher that uses `hash(hasher)` and it is used as default seeded hasher. Also, number normalization for hashing introduced, ie rule 'equality forces hash equality' is forced (`a == b` => `a.hash == b.hash`). Normalization idea is borrowed from Python implementation. It fixes several issues with BigInt and BigFloat on 32bit platform, but not all issues. Fixes crystal-lang#4578 Fixes crystal-lang#3932 Prerequisite for crystal-lang#4557 Replaces crystal-lang#4581 Correlates with crystal-lang#4653
Follows #4560.
This pull request includes changes required to support number normalization for #4675.