The function binomial(n,k) throws an OverflowError if the standard ::Int64 arguments are large enough that the result overflows an ::Int64. This can be modified to binomial(big(b),k) to promote the output to BigInt, e.g.:
Iβm trying to write a function as generically as possible that internally computes a binomial, and occasionally the inputs are large enough to require a BigInt type. In those cases the output of the overall function winds up turning into BigFloat.
Does anybody have an elegant generic way to compute binomial where the types are promoted to BigInt i.f.f. a BigInt output would be required? A try/catch could work but seems probably suboptimal.
If your functionβs return type depends on its argument values rather than just its inputs, then it isnβt βtype stableβ, which has some performance downsides and is more complex to use.
In the case where the result is small enough to fit in Int64, itβs faster to do try/catch than to always use big. In the overflow case, itβs faster to use big in the first place.
A third option is to convert to BigInt after the trycatch, so the functionβs return value is statically known but it has internal dynamic types.
binomial_trycatch(n,k) =
try
binomial(n,k)
catch e
e isa OverflowError && binomial(big(n), k)
end
binomial_trycatch_then_big(n,k)::BigInt =
try
binomial(n,k)
catch e
e isa OverflowError && binomial(big(n), k)
end
binomial_big(n,k) = binomial(big(n),k)
using BenchmarkTools
# Fits in Int64.
@btime binomial_trycatch(73, 23);
@btime binomial_trycatch_then_big(73, 23);
@btime binomial_big(73, 23);
# Doesn't fit in Int64.
@btime binomial_trycatch(73, 24);
@btime binomial_trycatch_then_big(73, 24);
@btime binomial_big(73, 24);
Not sure from that exactly where the overhead of overflowing is, whether itβs recomputing binomial or catching an error, but I think the only way to cut down on that any further is to define a separate binomial function that breaks the while loop where Base.binomial would throw the error. Then, it resumes the algorithm with BigInt, possibly with a rewrite that picks up where the loop left off instead of starting over.
Ooh, good point. Looks like converting n to Int128 extends the non-erroring input range into sufficiently large numbers with good performance and prevents the need for the wrapping function to return a BigFloat.
@jar1 I marked your earlier post as the solution since it actually answered the original question, but this Int128 tip was the solution I actually needed. Thanks!
The Int128 solution of @Jar1 always returns a Float64 for integer inputs. As pointed out by @Dan this is perfectly fine for @mike.ingold 's specific application because heβs using these coefficients in a floating point calculation. So another, faster way to get a floating point approximation of the binomial is
Edit: After looking over the source of binomial, when called with a non-integer first argument, I see itβs almost the same as above. So a better definition for binomial_float would be