Enforcing numeric types


#1

I’m trying to write a function that enforces numeric types. The eventualy goal is to create a library to support the use of fixed-point.

So i tried the simple example of :slight_smile:

function foo(y::UInt8)::UInt8
    x::UInt8 = 10
    return x+y
end
julia> foo(250)
ERROR: MethodError: no method matching foo(::Int64)

so to solve that problem I simply did:

julia> foo(convert(UInt8, 250))
0x04

Naturally I would like foo(250) to work. So give that this is a multiple dispatch language I figure that i simply can do:

function foo(y)
    foo(convert(UInt8, y))
end

and that does in fact do what I want.
I thought this might actually be a good example for those of use new to Julia.

My question is : Is this the right approach ?

My other question is , if i define foo thusly

function foo(y::UInt8)::UInt8
    x = 10
    return x+y
end

and do

julia> foo(convert(UInt8, 250))
ERROR: InexactError()

I can see why there would be a problem since I’m sure x is defaulting to Int64, but that doesn’t seem like the right error. Or is it ? I expect that the conversion can’t be done without the possibility of overflow ?

Thanks


#2

In the first case, you can simply call foo(UInt8(250)).

The error in your second question is because 260 is larger than typemax(UInt8)=255 and you are asking to convert the return value to a UInt8. If you make the signature foo(y::UInt8) without the ::UInt8, the return value is a Int64 as you expect.

Your example works if you defined

function foo(y::UInt8)::UInt8
    x = UInt8(10)
    x + y
end

and would return 0x04. Since you are summing two UInt8 and Julia does not widens automatically the type, you could have written also function foo(y::UInt8).

One more thing: there is no need to do this

because if you pass a UInt8 it will call the more specific method and if you pass any other type, it will throw an error. If you need to have methods that do different things for different input types, you have to define in the same way as you did for UInt8:

function foo(y::Int) ...
function foo(y::Float64) ...

but you do that only when what you want to do is not captured by a generic method. In your case, because you want to return the same value type as the input, you could simply do

function foo(y)
   x = convert(typeof(y), 10)
   x + y
end

#3

Can you clarify exactly what you want your foo function to do? For instance, what do you want to happen when the user calls it with:

  1. an out-of-range or negative number?
  2. an integer-valued floating-point number like 34.0
  3. a fractional floating-point number like 2.6

The InexactError is thrown when you try to convert an out-of-range number. You can verify this with convert(UInt8, 260).

Also, have you checked out the FixedPointNumbers package?


#4

Hi,

The idea is tha only unsigned 8 bit values should be handed to the routine and i want the values to wrap. so calling foo(250) and getting back 4 is the desired result.
A value <0 or >255 as an input would be an error.

This was just me using a simple example to make sure I understand how everything works.

@ssfrr : thanks for your help. of course I thought to look for a FixedPointNumbers package right after i posted …
@mzaffalon: Thanks very much for the explanations !


#5

Does this do what you want

foo(y) = UInt8(y) + UInt8(10)

?


julia> foo(250)
0x04

julia> foo(-1)
------ InexactError -------------------- Stacktrace (most recent call last)

 [1] — foo(::Int64) at REPL[138]:1

InexactError()

julia> foo(256)
------ InexactError -------------------- Stacktrace (most recent call last)

 [1] — foo(::Int64) at REPL[138]:1

InexactError()

If I have understood you, then I think this is a better approach. Any number thing that can be converted to a UInt8 will be, so don’t type the input, except, maybe you should use:

foo(y::Number) = UInt8(y) + UInt8(10)