Mapping to mixtures of Int64 & Float64

I have a function that can produce either Int64 or Float64 and I would like to map it to an array to be summed:

f(s) = parse(rand([Float64, Int64]), s)
a = rand(string.('0':'9'), rand([0,1,5]))
map(f, a) |> sum

If a is empty this fails because in that case, map produces an array of Any which cannot be summed.

The shortest solution that I can think of is:

map(f, a) |> Vector{Real} |> sum

but it just feels wrong to typecast everything (My application is not performance sensitive, so performance wise this is okay).

The other solutions that I can think of are:

map(f, a) |> (x -> isempty(x) ? Int64[] : x) |> sum

and

map(f, a) |> isempty(x) ? 0 : sum(x)

I’m just curious to know if someone has a better solution.

Not sure if you are focused on the sum, but if so:

sum(f,a,init=0)
2 Likes

Thank you, the sum is important to me, so this is a better solution.

How about sum(float ∘ f, a)?

sum(float ∘ f, String[]) produces a stacktrace. It is important that it can handle the empty array case.

Note that this will be relatively slow in Julia because it is type unstable. Why not just return a Float64 in all cases?

(Contrary to popular misconception, integers are represented exactly by Float64 values, up to \pm 2^{53}, so you don’t generally improve accuracy by using Int to store integers.)

10 Likes

Well the stacktrace actually also tells you what went wrong and how to fix it right on the top:

julia> sum(float∘f, String[])
ERROR: MethodError: reducing over an empty collection is not allowed; consider supplying `init` to the reducer
Stacktrace:
...

so all you need to do is to set init:

julia> sum(float∘f, String[]; init=0.0)
0.0

Thank you, I was not aware of this, and according to this thread +, -, *, / and sqrt all preserve this precision. I will certainly consider this in the future, but I do need to work on being comfortable with this after 30 years of having the ā€œmisconceptionā€.

Thanks for challenging my old habits, it is nice to see a well respected programmer confident in using Float64 to represent integers. I will keep this in mind in the future, but right now I prefer not to change that part of the code.

Yes, I did see this, but I don’t see how that would be better than sum(f, String[], init=0) as proposed by @laborg .

Not much, on my benchmarks it is even a bit slower. But the underlying idea remains interesting: preserve type-stability wherever possible.
For example, init = 0.0 is better than init = 0 at least on papier, because the sum will end up being a Float64 and not an Int, so it’s better to initialize it that way. See also Performance Tips Ā· The Julia Language

1 Like