Horner rule

metaprogramming

#1
 macro horner(x, p...)
     ex = p[end]
     for i = length(p)-1:-1:1
         ex = :($(p[i]) + $x * $ex)
     end
     ex
 end

I was looking at this https://github.com/JuliaLang/julia/pull/2987/commits/9c24795ac2918df7b8cba9bb129db8c535d321e8
and just want to know what

  1. : infront of the bracket is for
  2. $ is supposed to do?
    I mean in principle I could have also written it without : and $, but maybe they have some nice features ?

#2

See https://docs.julialang.org/en/stable/manual/metaprogramming/#Interpolation-1


#3

https://docs.julialang.org/en/latest/manual/metaprogramming/

would be a good start. And please quote your code (```).


#4

I dont see a difference. Did the quoting work for you?

Maybe not belonging to the topic, but why do people like to write long numbers like
0.10501_31152_37334_38116e-3

Does it nothing but a better overview of the number?

Thx for the answers btw!


#5

Yes, just for readability. (You should keep one thread to one topic though).


#6

This uses metaprogramming to “unroll” the expression. Use @macroexpand to see the result.


#7

julia> @macroexpand(ans)
ERROR: UndefVarError: @macroexpand not defined

???


#8

Please quote your code – this has been requested repeatedly by multiple people.


#9

I did…but it does not work.

I used blockquote only for the code.


#10

#11

@macroexpand works since Julia 0.6. You feed it the macro code directly.
It is strongly recommended to upgrade to Julia 0.6.

julia> @macroexpand Base.Math.@horner(5, 1, 2, 3, 4)
quote
    #19#t = 5
    (Base.Math.muladd)(#19#t, (Base.Math.muladd)(#19#t, (Base.Math.muladd)(#19#t, 4, 3), 2), 1)
end

#12

Sorry, but I don’t quite get what you mean:
While Base.Math.@horner(5, 1, 2, 3, 4)
just runs the macro and gives me 586
pretyping @macroexpand then tells me
(Base.Math.muladd)
What does this tell me?


#13

That’s a problem with the syntax highlighting. It’s giving you
(Base.Math.muladd)(#19#t, (Base.Math.muladd)(#19#t, (Base.Math.muladd)(#19#t, 4, 3), 2), 1)
which is the code that the macro is producing.


#14
 macro horner(x, p...)
     ex = p[end]
     for i = length(p)-1:-1:1
         ex = :($(p[i]) + $x * $ex)
     end
     ex
 end

Wouldnt I expect it to show me this code? Maybe with numbers inserted?

What does #19#t stand for? (I guess the variable 5, but why is it called this way?)


#15

It would really help if you read the documentation first, working through it carefully, then asked questions to fill in the holes.


#16

I am reading, but maybe you could show me the one crucial line telling me in https://docs.julialang.org/en/stable/manual/metaprogramming/#Interpolation-1 what the major advantage is doing it this way and not the “normal”

It just says it’s powerful…ok
but why? I dont yet see it…


#17

What would the “normal” way be? Try that, and compare it to what the macro does, e.g. in terms of speed.


#18
macro horner01(x, p...)
    ex = p[end]
    for i = length(p)-1:-1:1
        ex = :($(p[i]) + $x * $ex)
    end
    ex
end

function horner02(x, p::Array)
    ex = p[end]
    for i = length(p)-1:-1:1
        ex = ((p[i]) + x * ex)
    end
    ex
end

setprecision(10^8)
x=big(9528)/10000

@time @horner01(x,4,3,2,1);
@time horner02(x,[4,3,2,1]);

So here it is not faster…Why should it be faster?

BTW: if I change function to macro it does not like this p::Array, why?

Also it is telling me

WARNING: Method definition @horner01(ANY<:Any, Any…) in module Main at C:\Users\Diger\Documents\Julia\various math problems\compare horner.jl:2 overwritten at C:\Users\Diger\Documents\Julia\various math problems\compare horner.jl:2.
WARNING: Method definition horner02(Any, Array{Int64, 1}) in module Main at C:\Users\Diger\Documents\Julia\various math problems\compare horner.jl:10 overwritten at C:\Users\Diger\Documents\Julia\various math problems\compare horner.jl:10.

even though I renamed the functions?!


#19
julia> using BenchmarkTools

julia> f(x) = Base.Math.@horner(x, 4, 3, 2, 1)
f (generic function with 1 method)

julia> @btime f(0.2)
  1.847 ns (0 allocations: 0 bytes)
4.688

julia> v = [4., 3., 2., 1.]
4-element Array{Float64,1}:
 4.0
 3.0
 2.0
 1.0

julia> function horner02(x, p::Array)
           ex = p[end]
           for i = length(p)-1:-1:1
               ex = ((p[i]) + x * ex)
           end
           ex
       end
horner02 (generic function with 1 method)

julia> @btime horner02(0.2, $v)
  12.850 ns (0 allocations: 0 bytes)
4.688

#20

I’m actually not really worried about 1ns or even 1000ns, but when I goes in the seconds range I’m…
So repeating your first function

f(x) = Base.Math.@horner(x, 4, 3, 2, 1);
@time f(x);

where x is the bigfloat I defined before.
This gives me sth like 4.2s as does the other.

Why do you prefer working with some external benchmarking package, rather than using what comes with julia?