sostock / halfintegers.jl Goto Github PK
View Code? Open in Web Editor NEWA Julia package for half-integer numbers
License: Other
A Julia package for half-integer numbers
License: Other
I don’t think it would be wise to make standard arithmetic operations error on
HalfInteger
s. After all, you probably do want to use them in calculations, and division of twoInt
s does also not throw an error.
That's a good point, I forgot that there is a precedent for this with integer division. With that in mind, I think it's fine if the division of Half
s is consistent with that and also results in a float.
But it still feels weird that multiplication would leave the space. And it does that only occasionally (i.e. not if one factor is an integer). So it means that you really have to mentally keep track of types in your expressions.
Rational would be slightly better since you would not lose precision at least, so it might be a good compromise. I still think an argument can be made that if you do arithmetic with half-integers, you don't want to end up with floating point numbers accidentally.
Originally posted by @mortenpi in #2 (comment)
The README states that using Half{SafeInt}
will protect against overflows/underflows. Therefore, I think, we should check (as part of the test suite) that it actually does, even though I don’t see how it couldn’t.
Such that +(::OddHalfInt, ::OddHalfInt)
and -(::OddHalfInt, ::OddHalfInt)
would lead to an Int
result in either case. Implementation wise, these may wrap HalfInt
numbers with an additional check upon construction.
After all, gcd and lcm are well-defined for rational numbers
Hmm.. it is not entirely clear what the definition is actually. LCM and GCD both reduce down to divisibility, and for me, the way to generalize is, following Wikipedia, via integral domains.
Based on that, currently from the code lcm(3/2, 2) = 6
, but AFAICT it should be 3
, since 3/2 | 3
(3/2 * 2 = 3
) and 2 | 3
(2 * 3/2 = 3
).
As for rationals, all rational numbers are common multiples of any pair of rational numbers, since every rational (other than 0) divides every other rational? There is a related issue: JuliaLang/julia#27039
Originally posted by @mortenpi in #2 (comment)
FWIW, there does seem to be a small benefit to the custom hash
function:
julia> using WignerSymbols, HalfIntegers, BenchmarkTools
julia> x = rand(-1000:1000, 10000);
julia> xfloat = x/2;
julia> xrat = x.//2;
julia> xhalf = half.(x);
julia> xwig = WignerSymbols.HalfInteger.(x, 2);
julia> y = hash.(x);
julia> @benchmark map!(hash, $y, $x)
BenchmarkTools.Trial:
memory estimate: 0 bytes
allocs estimate: 0
--------------
minimum time: 26.254 μs (0.00% GC)
median time: 27.258 μs (0.00% GC)
mean time: 28.118 μs (0.00% GC)
maximum time: 142.009 μs (0.00% GC)
--------------
samples: 10000
evals/sample: 1
julia> @benchmark map!(hash, $y, $xfloat)
BenchmarkTools.Trial:
memory estimate: 0 bytes
allocs estimate: 0
--------------
minimum time: 36.866 μs (0.00% GC)
median time: 38.995 μs (0.00% GC)
mean time: 39.603 μs (0.00% GC)
maximum time: 132.436 μs (0.00% GC)
--------------
samples: 10000
evals/sample: 1
julia> @benchmark map!(hash, $y, $xrat)
BenchmarkTools.Trial:
memory estimate: 0 bytes
allocs estimate: 0
--------------
minimum time: 96.417 μs (0.00% GC)
median time: 103.466 μs (0.00% GC)
mean time: 106.371 μs (0.00% GC)
maximum time: 300.126 μs (0.00% GC)
--------------
samples: 10000
evals/sample: 1
julia> @benchmark map!(hash, $y, $xhalf)
BenchmarkTools.Trial:
memory estimate: 0 bytes
allocs estimate: 0
--------------
minimum time: 175.342 μs (0.00% GC)
median time: 177.345 μs (0.00% GC)
mean time: 184.564 μs (0.00% GC)
maximum time: 602.389 μs (0.00% GC)
--------------
samples: 10000
evals/sample: 1
julia> @benchmark map!(hash, $y, $xwig)
BenchmarkTools.Trial:
memory estimate: 0 bytes
allocs estimate: 0
--------------
minimum time: 93.089 μs (0.00% GC)
median time: 94.630 μs (0.00% GC)
mean time: 98.162 μs (0.00% GC)
maximum time: 249.477 μs (0.00% GC)
--------------
samples: 10000
evals/sample: 1
So the custom hash
performs as the one for rational, which however is still much slower than the one for Float64
. The far better solution thus seems to be to just convert to Float64
:
julia> @benchmark map!(z->hash(float(z)), $y, $xhalf)
BenchmarkTools.Trial:
memory estimate: 0 bytes
allocs estimate: 0
--------------
minimum time: 43.134 μs (0.00% GC)
median time: 44.856 μs (0.00% GC)
mean time: 46.925 μs (0.00% GC)
maximum time: 163.442 μs (0.00% GC)
--------------
samples: 10000
evals/sample: 1
Originally posted by @Jutho in #2 (comment)
Sorry for the delay, but it also looks great to me! I really like the half
function. In WignerSymols you have to do HalfInteger(1, 2)
, but that gets old quickly. half
seems like a perfect compromise between brevity and clarity.
I looked through the code and here a few observations / questions:
I am wondering whether it is wise to allow the arithmetic to leave the half-integer space (I'm thinking about multiplication and division between HalfIntegers). I think it would be better to error in those situations, to make sure that you don't accidentally write code that should do everything with Halfs, but ends up using floats in the intermediate calculations.
I find defining lcm
and gcd
for HalfIntegers a bit strange. Do you actually have a use case for these definitions?
Base.decompose
appears to be unexported and undocumented, so I am not sure if it is a good idea to rely on it. FWIW, I don't actually understand what it's even doing.
I see you have some doctests. Do you think it would be worth setting up Documenter?
I don't think the @eval
s are necessary in the tests?
The only thing I see that we have in WignerSymbols that we don't have here is an implementation of Base.hash
. @Jutho, would it be worth bringing it over?
We also define one
and zero
there, but those are not really necessary -- the general method from Base which falls back to the constructor works nicely.
Due to a change in hash
behavior in Julia 1.10, some things do not work anymore:
Hashing: Test Failed at /home/sebastian/Development/HalfIntegers.jl/test/runtests.jl:1496
Expression: #= /home/sebastian/Development/HalfIntegers.jl/test/runtests.jl:1496 =# @inferred(hash(HalfInt128(5 / 2))) === hash(5 // 2)
Evaluated: 0xb795033f6f2a0674 === 0x16b7f40fdbb2c57d
Hashing: Test Failed at /home/sebastian/Development/HalfIntegers.jl/test/runtests.jl:1496
Expression: #= /home/sebastian/Development/HalfIntegers.jl/test/runtests.jl:1496 =# @inferred(hash(HalfUInt128(5 / 2))) === hash(5 // 2)
Evaluated: 0xb795033f6f2a0674 === 0x16b7f40fdbb2c57d
Hashing: Test Failed at /home/sebastian/Development/HalfIntegers.jl/test/runtests.jl:1496
Expression: #= /home/sebastian/Development/HalfIntegers.jl/test/runtests.jl:1496 =# @inferred(hash(BigHalfInt(5 / 2))) === hash(5 // 2)
Evaluated: 0xb795033f6f2a0674 === 0x16b7f40fdbb2c57d
Hashing: Test Failed at /home/sebastian/Development/HalfIntegers.jl/test/runtests.jl:1499
Expression: #= /home/sebastian/Development/HalfIntegers.jl/test/runtests.jl:1499 =# @inferred(hash(half(typemax(Int64)))) === hash(typemax(Int64) // 2)
Evaluated: 0x720825ff5d41ca6f === 0x0fc531c308ea6ce8
Hashing: Test Failed at /home/sebastian/Development/HalfIntegers.jl/test/runtests.jl:1499
Expression: #= /home/sebastian/Development/HalfIntegers.jl/test/runtests.jl:1499 =# @inferred(hash(half(typemax(UInt64)))) === hash(typemax(UInt64) // 2)
Evaluated: 0x30d54b97d39feaa7 === 0xf0300825c92981a4
Edit: Some of the failures are due to a bugs in the new implementation, some are due to an intended change in behavior.
This issue is used to trigger TagBot; feel free to unsubscribe.
If you haven't already, you should update your TagBot.yml
to include issue comment triggers.
Please see this post on Discourse for instructions and more details.
If you'd like for me to do this for you, comment TagBot fix
on this issue.
I'll open a PR within a few hours, please be patient!
I just noticed this package on JuliaHub, and it occurred to me that it appears be related to
https://github.com/JuliaMath/FixedPointNumbers.jl
julia> a = Fixed{Int32, 1}(1.5)
1.5Q30f1
julia> a.i
3
Might be worth pointing out a connection in the README
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.