Rand() doesn't work with dims as tuple of Uint

I’m trying to create random numbers in a matrix given by another variable that is a UInt32. For example I have something like this:

test_uint::UInt32 = 4
rand(Float64, (test_uint, 10))

And get the following error

ERROR: MethodError: no method matching rand(::Type{Float64}, ::Tuple{UInt32, Int64})

Why is the case? What are the supported types for dims? is it only Int64?

I’m not sure if the API always strictly adheres to it, but there is a type alias Dims for Tuple{Vararg{Int, N}} where N used for AbstractArray dimensions. Some functions like zeros accept dims arguments with more integer types, but even then it gets wrangled into Dims for the output:

julia> typeof(size(zeros(Float64, (UInt32(4), 10)))) # 64-bit system
Tuple{Int64, Int64}

On v1.10.2, all the Base methods for rand appear to annotate dims with ::Integer... or ::Dims. So, rand(Float64, UInt(4), 10) will work, but rand(Float64, (UInt(4), 10)) won’t, you’ll need rand(Float64, (4, 10)).

1 Like

Thanks a lot for the answer! In my opinion this is super confusing and should just be leveled out, also, negative dims makes no real sense so using a UInt should be the preferred choice, no? Maybe there are other reasons for this?

In general, it’s only Int (== Int64 on a 64-bit machine), though a few functions support more general Integer arguments and it’s been slowly expanded over time … in particular, tuples of dimensions still have to be Int. This was discussed way back in 2014: accept more Integer types for dims::Int... arguments by stevengj · Pull Request #7956 · JuliaLang/julia · GitHub (see also issues that link this).

See this comment from 2014 by @StefanKarpinski and this discourse discussion: Why `Int` instead of `UInt` for indexing?

3 Likes

This topic also extends far beyond Julia. Many languages opt for signed integer indices, and even the languages that use unsigned integer indices have a lot of nuance and caveats when you involve arithmetic. I’m linking the inventor of C++ Bjarne Stroustrup and the ISO C++ standards committee convener Herb Sutter suggesting signed integer types, warning of mixing integer types, and griping about such a mix stuck in the standard library. As for your question in particular:

Stroustrup: …Now, when people use unsigned numbers, they usually have a reason. And the reason will be something like, “well it can’t be negative”, or “I need an extra bit”. If you need an extra bit, I am very reluctant to believe you that you really need it, and I don’t think that’s a good reason. When you think you can’t have negative numbers, you will have somebody who initialize your unsigned with −2, and think they get −2, and things like that. It is just highly error-prone…
Sutter: …If you are writing a very large data structure, the only case where you would really care about the unsigned is if you know it’s an array of characters that’s going to be bigger than half of memory. I don’t know of any other case where it matters…

Array elements are often larger than 1 byte, which needs many times fewer integer indices than memory addresses, even for such an array completely filling addressable memory. 64-bit systems were mainstreamed before RAM exceeded the 4GiB limit of 32-bit systems, and we’ll likely move away from 64-bit systems before we ever have 16EiB RAM. We can afford to “waste” half of an integer range on indexing because arrays are just so rarely ≤1 byte per element AND large enough.

3 Likes

Just for the record, note that you can splat the tuple in this case:

test_uint::UInt32 = 4
rand(Float64, (test_uint, 10)...)

or

rand(Float64, test_uint, 10)

works fine.

2 Likes