Let us agree to disagree There is no magic here and there are multiple ways to solve this issue.
First of all, as you have said the reason, why the bignums
function is not working, related to the fact that sqrt(big(2))
is calculated before internal calculations are being made, and it is calculated with default precision, which is equal 256. So the easiest way to overcome this issue is to calculate this function with the necessary precision and here one can use higher order functions
julia> @assert join(bignums(setprecision(() -> sqrt(big(2)), 1000), 2, 1000)) == join(@numbers(sqrt(big(2)), 2, 1000))
julia> [join(bignums(setprecision(() -> sqrt(big(2)), u), 2, u)) for u in [10, 20, 30, 40, 1000]
]
5-element Vector{String}:
"10110101000"
"101101010000010011110"
"1011010100000100111100110011010"
"10110101000001001111001100110011111110100"
"10110101000001001111001100110011111110011101111001100100100001000101100101111101100010011011001101110101010010101011111010011111000111010110111101100000101110101000100100111011101010000100110011101101000101111010110010000101100000110011001110011001000101010100101011111100100000110000010000111010101110001010001011000011101010001011000111111110011011111101110010000011110110110011100100001111011101001010100001011110010000111001110001111011010010100111100000000100100001110011011000111101111110100010011101101000110100100010000000101110100001110100001010101111000111110100111001010011000001011001110001100000000100011011110000110011011110111100101010110001101111001001000100010110100010000100010110001010010001100000101010111100011100100010111101111100010011100011001111000110110101011010100010100011100010111011011111101001110111001100101100101010011000110100001100110001111100111100100001001101111101010010111100010010000011111000001101101110010110000010111011101010101001001010000010001001100100000"
Not very convenient? Yes. Impossible? No.
One can make it somewhat easier by applying the same idea of high order function to bignums
itself.
function bignums2(f, b, n = 100)
setprecision(round(Integer, n)) do
reverse(digits(floor(BigInt, big(2)^n*f()), base = b))
end
end
julia> @assert join(bignums2(() -> sqrt(big(2)), 2, 1000)) == join(@numbers(sqrt(big(2)), 2, 1000))
julia> [join(bignums2(() -> sqrt(big(2)), 2, u)) for u in [10, 20, 30, 40, 1000]]
5-element Vector{String}:
"10110101000"
"101101010000010011110"
"1011010100000100111100110011010"
"10110101000001001111001100110011111110100"
"10110101000001001111001100110011111110011101111001100100100001000101100101111101100010011011001101110101010010101011111010011111000111010110111101100000101110101000100100111011101010000100110011101101000101111010110010000101100000110011001110011001000101010100101011111100100000110000010000111010101110001010001011000011101010001011000111111110011011111101110010000011110110110011100100001111011101001010100001011110010000111001110001111011010010100111100000000100100001110011011000111101111110100010011101101000110100100010000000101110100001110100001010101111000111110100111001010011000001011001110001100000000100011011110000110011011110111100101010110001101111001001000100010110100010000100010110001010010001100000101010111100011100100010111101111100010011100011001111000110110101011010100010100011100010111011011111101001110111001100101100101010011000110100001100110001111100111100100001001101111101010010111100010010000011111000001101101110010110000010111011101010101001001010000010001001100100000"
If your numbers a result of some function, then it is better to follow this approach instead of falling down to metaprogramming. I mean in real life code, instead of () -> sqrt(big(2))
you can use actual function, which produces some BigInt
values.
Now, if it is absolutely necessary to use a macro, then one can do something like this
macro numbers2(x, b, n = 100)
return quote
setprecision(round(Integer, $(esc(n)))) do
reverse(digits(floor(BigInt, big(2)^$(esc(n))*$(esc(x))), base = $(esc(b))))
end
end
end
julia> @assert join(@numbers2(sqrt(big(2)), 2, 1000)) == join(@numbers(sqrt(big(2)), 2, 1000))
julia> [join(@numbers2(sqrt(big(2)), 2, u)) for u in [10, 20, 30, 40, 1000]]
5-element Vector{String}:
"10110101000"
"101101010000010011110"
"1011010100000100111100110011010"
"10110101000001001111001100110011111110100"
"10110101000001001111001100110011111110011101111001100100100001000101100101111101100010011011001101110101010010101011111010011111000111010110111101100000101110101000100100111011101010000100110011101101000101111010110010000101100000110011001110011001000101010100101011111100100000110000010000111010101110001010001011000011101010001011000111111110011011111101110010000011110110110011100100001111011101001010100001011110010000111001110001111011010010100111100000000100100001110011011000111101111110100010011101101000110100100010000000101110100001110100001010101111000111110100111001010011000001011001110001100000000100011011110000110011011110111100101010110001101111001001000100010110100010000100010110001010010001100000101010111100011100100010111101111100010011100011001111000110110101011010100010100011100010111011011111101001110111001100101100101010011000110100001100110001111100111100100001001101111101010010111100010010000011111000001101101110010110000010111011101010101001001010000010001001100100000"
First of all, it returns quote
instead of eval
, so macro is doing what it should do, i.e. transforming code instead of calculating in compile time. Secondly, I use macro hygene to generate proper code. You can use @macroexpand
to verify the result.
julia> @macroexpand [join(@numbers2(sqrt(big(2)), 2, u)) for u in [10, 20, 30, 40, 1000]]
:([join(begin
#= REPL[25]:3 =#
Main.setprecision(Main.round(Main.Integer, u)) do
#= REPL[25]:4 =#
Main.reverse(Main.digits(Main.floor(Main.BigInt, Main.big(2) ^ u * sqrt(big(2))),
base = 2))
end
end) for u = [10, 20, 30, 40, 1000]])
You can compare this code with the macro without hygene
macro numbers3(x, b, n = 100)
return quote
setprecision(round(Integer, $n)) do
reverse(digits(floor(BigInt, big(2)^$n*$x), base = $b))
end
end
end
julia> @macroexpand [join(@numbers3(sqrt(big(2)), 2, u)) for u in [10, 20, 30, 40, 1000]]
:([join(begin
#= REPL[40]:3 =#
Main.setprecision(Main.round(Main.Integer, Main.u)) do
#= REPL[40]:4 =#
Main.reverse(Main.digits(Main.floor(Main.BigInt, Main.big(2) ^ Main.u * Main.sqrt(
Main.big(2))), base = 2))
end
end) for u = [10, 20, 30, 40, 1000]])
and from this code, it is easy to understand why Julia throw error.