Friday, June 15, 2012

NextAfter in C# without allocations or unsafe code

In C99, there is a standard way to get the next larger or smaller float32: the function nextafterf. In C#, there’s no such function and I had to roll my own version.
There are three ugly possibilities.
  1. use pointer aliasing,
  2. BitConverter.GetBytes,
  3. simulate an union.
The main update loop may not make any allocations, so BitConverter is out. Pointer aliasing is unsafe, so I couldn’t use it either. So the version I ended up with looks like this:
[StructLayout(LayoutKind.Explicit)]
struct FloatIntUnion
{
    [FieldOffset(0)]
    public int i;
    [FieldOffset(0)]
    public float f;
}

//  Returns the next float after x in the direction of y.
float NextAfter(float x, float y)
{
    if (float.IsNaN(x) || float.IsNaN(y)) return x + y;
    if (x == y) return y;  // nextafter(0, -0) = -0

    FloatIntUnion u;
    u.i = 0; u.f = x;  // shut up the compiler

    if (x == 0)
    {
        u.i = 1;
        return y > 0 ? u.f : -u.f;
    }

    if ((x > 0) == (y > x))
        u.i++;
    else
        u.i--;
    return u.f;
}

3 comments:

  1. Thanks a lot, great stuff!

    Are there likely to be any issues when using this technique on mobile platforms like iOS, the varying flavours of Android, Windows Phone, etc?

    (using Mono)

    ReplyDelete
  2. For future seekers like me, coming here from Google:

    It can also be done with BitConverter.DoubleToInt64Bits method which doesn't seem to allocate anything. It is done this way in C.math.NET library, take a look at its source here: https://github.com/MachineCognitis/C.math.NET/blob/master/C.math/math.cs#L1649-L1803

    ReplyDelete
    Replies
    1. Well, seems like actually I might be wrong... In case of Mono and Unity even DoubleToInt64Bits seems to generate some garbage - probably Unity/Mono implementation differs from VC#'s one.

      Delete