Search code examples
cerlangerlang-nif

Erlang NIF number return types


I'm experimenting with NIFs and I'm confused about what number types Erlang is working with, because I'm getting some weirdness with my precisions.

Here's an example:

erlang:band(18446744073709551614, 5) == 4

And from inside a NIF which looks something like this:

long long l, r;

enif_get_long(env, argv[0], &l);
enif_get_long(env, argv[1], &r);

return enif_make_long(env, l & r);

I get a 1 as a result.

Is this something to do with the C layer not holding the correct "size" of the number? Or is it that enif_(get|make)_long isn't the correct way to be dealing with a number of this size? Or is it simply that NIFs can't work with numbers this large?


Solution

  • 18446744073709551614 is 2^64 - 2 and therefore cannot fit in a long long, which is most likely a 64 bit signed integer with the range -(2^63) to (2^63)-1. Also, enif_get_long requires a long int, not long long. You should also be getting an error value returned from enif_get_long because of the overflow according to the docs which you are not checking for.

    To work on numbers upto 2^64 - 1 (which includes the number in question), you can use enif_get_uint64.

    This code should work (untested):

    ErlNifUInt64 l, r;
    enif_get_uint64(env, argv[0], &l);
    enif_get_uint64(env, argv[1], &r);
    return enif_make_uint64(env, l & r);
    

    You should also check the return value of enif_get_* to make sure you're not working on uninitialized data.