Basically, don’t follow the pattern that Base switched to in 0.7, it’s just not a good idea. Something like https://github.com/JuliaLang/julia/issues/26552#issuecomment-374958654 would turn the StackOverFlowError into a MethodError that describes exactly what method you need to define, while still doing the right thing for Reals that do have the property that float(x) isa AbstractFloat.
If your f is somehow ‘primitive’, in the sense that it uses operations that don’t have derivative rules defined for them in ForwardDiff.jl/DiffRules.jl, then you have to define an overload for Dual, yes. But if it’s like your example case, I’d just define f(x::Real) = 2x. Could you provide a more realistic example where it’s clear why you’d need the float call?