std::isnormal in C#

Soonts 07/11/2018. 2 answers, 347 views
c# floating-point

Is this correct? Is this portable to .NET core and 32-bit ARM Linux? Is there a faster way, maybe something in the .NET framework? Is there a way to implement float32 version without using dynamic memory i.e. without byte arrays?

/// <summary>Determines if the given floating point number is normal, i.e. is neither zero, subnormal, infinite, nor NaN.</summary>
public static bool isNormal( this double v )
{
    long i64 = BitConverter.DoubleToInt64Bits( v );
    long expMask = 0x7ff0000000000000L;
    long expBits = i64 & expMask;
    return expBits != 0 && expBits != expMask;
}

/// <summary>Determines if the given floating point number is normal, i.e. is neither zero, subnormal, infinite, nor NaN.</summary>
public static bool isNormal( this float v )
{
    byte[] bytes = BitConverter.GetBytes( v );
    uint i32 = BitConverter.ToUInt32( bytes, 0 );
    uint expMask = 0x7f800000;
    uint expBits = i32 & expMask;
    return expBits != 0 && expBits != expMask;
}

2 Answers


Adriano Repetti 07/13/2018.

Code is formally correct, just change the name to IsNormal() (PascalCase) to follow standard .NET naming conventions.

However your second question "Is this portable to .NET core and 32-bit ARM Linux..." is little bit more tricky. In normal conditions you should almost never care about host endianness but floating point numbers have exceptions on ARM (and possibly some other architectures which, however, don't run .NET Core). Specifically:

  • ARM EABI does not store floating point numbers using the natural-endianness but a mixed-endian convention. You can't compare BitConverter.GetBytes(v) result with an int because they're surely encoded differently.
  • CirrusLogic Maverick (formally EP9312) is an ARM9 System-on-chip with a MaverickCrunch math cooprocessor where floating point numbers are always little-endian. You can't compare BitConverter.GetBytes(v) result with an int because they may be encoded differently.

Honestly, regardless the CPU, I am not sure about Linux images compiled with software floating point support then (if you need to support them) you have to try (or find related documentation).

The good news is that you probably won't ever need to deal with them. The bad news is that, if you really need to, compiling for AnyCPU you have to detect endianness at run-time (and ARM CPUs may be configured...)


Henrik Hansen 07/13/2018.

You ask for another way to do this - using the .net framework (I have no experience with Linux, so don't hang me if it doesn't work there).

1)

public static bool IsNormal1(float v)
{
  return !float.IsInfinity(v) && !float.IsNaN(v) && Math.Abs(v) >= 1.175494351E-38;  
}

The magic number 1.175494351E-38 is discussed here.


2) If you are willing to go unsafe you could do this:

const int floatExpMask = 0x7F800000;
public unsafe static bool IsNormal2(float f)
{
  return
    (*(int*)(&f) & 0x7FFFFFFF) < floatExpMask // Infinity and NaN
    && (*(int*)(&f) & floatExpMask) != 0; // Subnormal and zero
}

Related questions

Hot questions

Language

Popular Tags