Julia: using (U)Int32 on 64-bit machines very awkward.

Created on 22 Nov 2018  ·  17Comments  ·  Source: JuliaLang/julia

I've read #9162 and understand the rational.

The awkwardness stems mostly from integer literals being 64 bit. For high performance real-time, memory limited or embedded systems you often do want to work with 32-bit integers. Games development is an example use-case. Additionally this type of code would tend to use a lot of integer literals. I've been doing things like x + UInt32(1) or defining constants like const three = UInt32(3) ugly. My code has UInt32 casts everywhere! At least with floats I can write 3.0f0. 0x00003 is not so nice.

I'd be happiest if integer literals could demote to match the other type(s) in a given operator, but I guess then your going to need a new type for literals or something. I can see this being complicated and fraught with peril.

Second happiest would be a more convenient way of entering 32-bit integer literals.

julia> typeof(UInt32(1) + 1)
Int64

Also why is that not UInt64? Seems inconsistent.

julia> typeof(UInt64(1) + 1)
UInt64

Most helpful comment

julia> struct U32 end

julia> Base.:*(n::Number, ::U32) = UInt32(n)

julia> const u = U32()
U32()

julia> 5u
0x00000005

heh. (don't name your variables u).

All 17 comments

Just wanted to add that this conversion often leads to type instability in your performance conscious code if you aren't very careful.

The rule is that when integer arguments are of different types the result type is:

  • the type of the larger if the argument types are different sizes;
  • the unsigned type if they argument types are the same size.

This went through many iterations (#9162 is just the first of many). This simple rule is the end result of a lot of experimentation and we're fairly happy with it. We're also not able to change this until Julia 2.0. No rule works as desired in all circumstances; you're hitting one of the places where this rule is inconvenient. You may want to use typed local variables, e.g.:

function f()
    i::UInt32 = 123
    i += 1
    return i
end

Any assignment to i in this function body will be converted to UInt32.

how hard would it be to admit a simpler syntax for integer literals? C++ has 'l', 'L', 'u', 'U',[lLuU][lLuU] suffixes similar to 'f'. Although they all mean things bigger than 32 bits. 's' for signed int and 'u' for unsigned int?

Typed local variables help only a little.

The numerical programming community may be happy with this. However, I'd like to see Julia used in Game Development, so I wouldn't' have to code in C++ if I ever when back :P. Most people there would get really frustrated with the 64-bit literals. It's a pretty long stretch to get any game developer to accept a garbage collected language though, so I'd say this is a low priority. In my current job I can eat the ( in the limit ) 2x memory hit and no one will really notice.

Still1u === UInt32(1) seems reasonable to me.

how hard would it be to admit a simpler syntax for integer literals?

It's breaking since 1u is a valid syntax.

oh right. 2x = 2*x. '_u'? Or another letter if 'u' is taken or undesirable. C++ has a precedent for this kind of syntax. 1_u
https://en.cppreference.com/w/cpp/language/user_literal

or 1u0 could work. it's like 1f0

Those are currently valid syntaxes too. In fact, any suffix are valid, (including both 1u0 and 1_u). Note that I'm not saying it's impossible, I'm just saying it's breaking. Adding 1f0 would have been as breaking if it wasn't previously implemented.

Right. So 2.0 then :)

I'll close this, seems like a serious hassle for little gain right now.

julia> struct U32 end

julia> Base.:*(n::Number, ::U32) = UInt32(n)

julia> const u = U32()
U32()

julia> 5u
0x00000005

heh. (don't name your variables u).

Would it be possible to
a) find a clever way to condense 0x00003, i.e. using prefix instead of postfix
b) a macro to change literals
c) something like setprecision
d) some Cassette magic
?

@Orbots: you seem not to have noticed my suggestion of using typed local variables. Does that not address the problem?

@StefanKarpinski it partially addresses the problem of conveniently creating 32-bit literals. Does it address the problem of working with 32-bit ints being awkward in general? Not really.

julia> foo(x::UInt32) = x*x
foo (generic function with 1 method)

julia> foo(1)
ERROR: MethodError: no method matching foo(::Int64)  
Closest candidates are:
  foo(::UInt32) at REPL[1]:1  # **a little awkward** but error message is informative, so not too bad
Stacktrace:
 [1] top-level scope at none:0

julia> let
           two::UInt32 = 2  # this is ok
           foo( two + two )
         end
0x00000010   # **awkward**

julia> two::UInt32 = 2   
#**awkward**
ERROR: syntax: type declarations on global variables are not yet supported

julia> let
            two::UInt32 = 2
            foo( two +  3 ) 
   end
ERROR: MethodError: no method matching foo(::Int64)
 # oops, forgot to declare three... **a little awkward** but not horrible.

@KristofferC Looks promising. A little scary though :)
@StefanKarpinski this fixes one my awkward complaints:

import Base.show
show( io::IO, x::UInt32 ) = show(io, Int(x))
julia> two = UInt32(2)
2

Julia is flexible enough that I reckon I could work with 32 bit ints in a fairly natural manner via a custom package.

@Orbots, your example seems to be a matter of the function being too constrained in what argument types it accepts. This definition works fine:

julia> foo(x::Integer) = UInt32(x)^2
foo (generic function with 1 method)

julia> foo(1)
0x00000001

There are many other ways this can be handled as well.

@StefanKarpinski

There are many other ways this can be handled as well.

Exactly. UInt32 could be nicer to work with, but with minor effort it can be thanks to Julia's extremely extensible nature.

I don't want to get into this too much further right now. In the interest of "getting stuff done" I've opted to just use the most natural types Julia supports, Int and Float64 ( although I serialize to UInt32 and Float32 for sending over a socket). Another thing was that the performance of UInt32 vs Int was worse when I did a quick comparison. So I'd need to figure out what's going on there ( probably type stability in my code ). Anyways, that would be a separate issue if indeed I found a performance problem with UInt32, which I won't investigate right now.

Thanks for all all the suggestions.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

manor picture manor  ·  3Comments

arshpreetsingh picture arshpreetsingh  ·  3Comments

tkoolen picture tkoolen  ·  3Comments

omus picture omus  ·  3Comments

StefanKarpinski picture StefanKarpinski  ·  3Comments