master eb09e0e3e96f cached
16 files
110.4 KB
30.5k tokens
67 symbols
1 requests
Download .txt
Repository: Kimbatt/soft-float-starter-pack
Branch: master
Commit: eb09e0e3e96f
Files: 16
Total size: 110.4 KB

Directory structure:
gitextract_fgkxyhc3/

├── .gitignore
├── LICENSE
├── README.md
├── SoftFloat/
│   ├── .gitignore
│   ├── SoftFloat.csproj
│   ├── libm/
│   │   ├── Arithmetic.cs
│   │   ├── LICENSE.txt
│   │   ├── Transcendental.cs
│   │   └── Trigonometry.cs
│   └── sfloat.cs
├── SoftFloat.sln
└── SoftFloatTest/
    ├── .gitignore
    ├── PCG.cs
    ├── Program.cs
    ├── SoftFloatTest.csproj
    └── Tests.cs

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
.vs/


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2021 Kimbatt

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
# Soft float starter pack
Software implementation of floating point numbers and operations.  
Soft floats can be used for deterministic calculations, e.g. for physics simulation.  
They will give the same results every time, on every platform, on every processor.  
  
This repository uses the work of:
- [SoftFloat](https://github.com/CodesInChaos/SoftFloat), which implements basic soft float functionality
- [libm](https://github.com/rust-lang/libm), which implements various operations for floating point numbers, including square root, trigonometric functions, transcendental functions, etc. (ported to C#)

## How to use
The `sfloat` type is the main type that you'll need to use for soft float calculations.  
  
The `sfloat` type can be constructed in three ways:
- Explicit cast from float:
```csharp
sfloat a = (sfloat)1.0f;
sfloat b = (sfloat)(-123.456f);
sfloat c = (sfloat)float.PositiveInfinity;
sfloat d = (sfloat)float.NaN;
```
This cast is basically free, since the internal representations are identical.
- Explicit cast from int:
```csharp
sfloat a = (sfloat)1;
sfloat b = (sfloat)(-123);
sfloat c = (sfloat)int.MaxValue;
```
- Create from raw byte representation
```csharp
sfloat a = sfloat.FromRaw(0x00000000); // == 0
sfloat b = sfloat.FromRaw(0x3f800000); // == 1
sfloat c = sfloat.FromRaw(0xc2f6e979); // == -123.456
sfloat d = sfloat.FromRaw(0x7f800000); // == Infinity
```
This cast is also basically free, it's just the byte representation of the value.

The rest of the operations work just like with floats (addition, multiplication, etc.).  
Note that you should always use a float literal (or a variable that was assigned a float literal before) for explicit casts from floats, since any operation done on floats can be non-deterministic.
```csharp
// OK
float a = 1.0f;
sfloat b = (sfloat)a + (sfloat)123.456f;


// NOT OK
float a = 1.0f;
sfloat b = (sfloat)(a + 123.456f); // <-- float addition here, which may be non-deterministic
```

### Using libm

You can use libm just like a regular mathematics library:
```csharp
sfloat x = (sfloat)2.0f;
sfloat squareRoot = libm.sqrtf(x);

sfloat c = libm.cosf((sfloat)3.1415f);

sfloat e = libm.expf((sfloat)1.0f);
```
All functions have the f suffix, which comes from the rust libm implementation. You can rename them if you want to.


================================================
FILE: SoftFloat/.gitignore
================================================
/bin
/obj


================================================
FILE: SoftFloat/SoftFloat.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Library</OutputType>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>

  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
  </PropertyGroup>

</Project>


================================================
FILE: SoftFloat/libm/Arithmetic.cs
================================================

namespace SoftFloat
{
    public static partial class libm
    {
        /// <summary>
        /// Returns the remainder and the quotient when dividing x by y, so that x == y * quotient + remainder
        /// </summary>
        public static void remquof(sfloat x, sfloat y, out sfloat remainder, out int quotient)
        {
            uint ux = x.RawValue;
            uint uy = y.RawValue;
            int ex = (int)((ux >> 23) & 0xff);
            int ey = (int)((uy >> 23) & 0xff);
            bool sx = (ux >> 31) != 0;
            bool sy = (uy >> 31) != 0;
            uint q;
            uint i;
            var uxi = ux;

            if ((uy << 1) == 0 || y.IsNaN() || ex == 0xff)
            {
                sfloat m = (x * y);
                remainder = m / m;
                quotient = 0;
                return;
            }

            if ((ux << 1) == 0)
            {
                remainder = x;
                quotient = 0;
                return;
            }

            /* normalize x and y */
            if (ex == 0)
            {
                i = uxi << 9;
                while ((i >> 31) == 0)
                {
                    ex -= 1;
                    i <<= 1;
                }

                uxi <<= -ex + 1;
            }
            else
            {
                uxi &= (~0u) >> 9;
                uxi |= 1 << 23;
            }

            if (ey == 0)
            {
                i = uy << 9;
                while ((i >> 31) == 0)
                {
                    ey -= 1;
                    i <<= 1;
                }

                uy <<= -ey + 1;
            }
            else
            {
                uy &= (~0u) >> 9;
                uy |= 1 << 23;
            }

            q = 0;
            if (ex + 1 != ey)
            {
                if (ex < ey)
                {
                    remainder = x;
                    quotient = 0;
                    return;
                }

                /* x mod y */
                while (ex > ey)
                {
                    i = uxi - uy;
                    if ((i >> 31) == 0)
                    {
                        uxi = i;
                        q += 1;
                    }

                    uxi <<= 1;
                    q <<= 1;
                    ex -= 1;
                }

                i = uxi - uy;
                if ((i >> 31) == 0)
                {
                    uxi = i;
                    q += 1;
                }

                if (uxi == 0)
                {
                    ex = -30;
                }
                else
                {
                    while ((uxi >> 23) == 0)
                    {
                        uxi <<= 1;
                        ex -= 1;
                    }
                }
            }

            /* scale result and decide between |x| and |x|-|y| */
            if (ex > 0)
            {
                uxi -= 1 << 23;
                uxi |= ((uint)ex) << 23;
            }
            else
            {
                uxi >>= -ex + 1;
            }

            x = sfloat.FromRaw(uxi);
            if (sy)
            {
                y = -y;
            }

            if ((ex == ey || (ex + 1 == ey && ((sfloat)2.0f * x > y || ((sfloat)2.0f * x == y && (q % 2) != 0)))) && x > y)
            {
                x -= y;
                q += 1;
            }

            q &= 0x7fffffff;
            int quo = sx ^ sy ? -(int)q : (int)q;
            remainder = sx ? -x : x;
            quotient = quo;
        }

        /// <summary>
        /// Returns the remainder when dividing x by y
        /// </summary>
        public static sfloat remainderf(sfloat x, sfloat y)
        {
            remquof(x, y, out sfloat remainder, out _);
            return remainder;
        }

        /// <summary>
        /// Returns x modulo y
        /// </summary>
        public static sfloat fmodf(sfloat x, sfloat y)
        {
            uint uxi = x.RawValue;
            uint uyi = y.RawValue;
            int ex = (int)(uxi >> 23 & 0xff);
            int ey = (int)(uyi >> 23 & 0xff);
            uint sx = uxi & 0x80000000;
            uint i;

            if (uyi << 1 == 0 || y.IsNaN() || ex == 0xff)
            {
                return (x * y) / (x * y);
            }

            if (uxi << 1 <= uyi << 1)
            {
                if (uxi << 1 == uyi << 1)
                {
                    //return 0.0 * x;
                    return sfloat.Zero;
                }

                return x;
            }

            /* normalize x and y */
            if (ex == 0)
            {
                i = uxi << 9;
                while (i >> 31 == 0)
                {
                    ex -= 1;
                    i <<= 1;
                }

                uxi <<= -ex + 1;
            }
            else
            {
                uxi &= uint.MaxValue >> 9;
                uxi |= 1 << 23;
            }

            if (ey == 0)
            {
                i = uyi << 9;
                while (i >> 31 == 0)
                {
                    ey -= 1;
                    i <<= 1;
                }

                uyi <<= -ey + 1;
            }
            else
            {
                uyi &= uint.MaxValue >> 9;
                uyi |= 1 << 23;
            }

            /* x mod y */
            while (ex > ey)
            {
                i = uxi - uyi;
                if (i >> 31 == 0)
                {
                    if (i == 0)
                    {
                        //return 0.0 * x;
                        return sfloat.Zero;
                    }

                    uxi = i;
                }

                uxi <<= 1;

                ex -= 1;
            }

            i = uxi - uyi;
            if (i >> 31 == 0)
            {
                if (i == 0)
                {
                    //return 0.0 * x;
                    return sfloat.Zero;
                }

                uxi = i;
            }

            while (uxi >> 23 == 0)
            {
                uxi <<= 1;
                ex -= 1;
            }

            /* scale result up */
            if (ex > 0)
            {
                uxi -= 1 << 23;
                uxi |= ((uint)ex) << 23;
            }
            else
            {
                uxi >>= -ex + 1;
            }

            uxi |= sx;
            return sfloat.FromRaw(uxi);
        }

        /// <summary>
        /// Rounds x to the nearest integer
        /// </summary>
        public static sfloat roundf(sfloat x)
        {
            sfloat TOINT = (sfloat)8388608.0f;

            uint i = x.RawValue;
            uint e = i >> 23 & 0xff;
            sfloat y;

            if (e >= 0x7f + 23)
            {
                return x;
            }

            if (e < 0x7f - 1)
            {
                //force_eval!(x + TOINT);
                //return 0.0 * x;
                return sfloat.Zero;
            }

            if (i >> 31 != 0)
            {
                x = -x;
            }

            y = x + TOINT - TOINT - x;

            if (y > (sfloat)0.5f)
            {
                y = y + x - sfloat.One;
            }
            else if (y <= (sfloat)(-0.5f))
            {
                y = y + x + sfloat.One;
            }
            else
            {
                y += x;
            }

            return i >> 31 != 0 ? -y : y;
        }

        /// <summary>
        /// Rounds x down to the nearest integer
        /// </summary>
        public static sfloat floorf(sfloat x)
        {
            uint ui = x.RawValue;
            int e = (((int)(ui >> 23)) & 0xff) - 0x7f;

            if (e >= 23)
            {
                return x;
            }

            if (e >= 0)
            {
                uint m = 0x007fffffu >> e;
                if ((ui & m) == 0)
                {
                    return x;
                }
                if (ui >> 31 != 0)
                {
                    ui += m;
                }
                ui &= ~m;
            }
            else
            {
                if (ui >> 31 == 0)
                {
                    ui = 0;
                }
                else if (ui << 1 != 0)
                {
                    return (sfloat)(-1.0f);
                }
            }

            return sfloat.FromRaw(ui);
        }

        /// <summary>
        /// Rounds x up to the nearest integer
        /// </summary>
        public static sfloat ceilf(sfloat x)
        {
            uint ui = x.RawValue;
            int e = (int)(((ui >> 23) & 0xff) - (0x7f));

            if (e >= 23)
            {
                return x;
            }

            if (e >= 0)
            {
                uint m = 0x007fffffu >> e;
                if ((ui & m) == 0)
                {
                    return x;
                }
                if (ui >> 31 == 0)
                {
                    ui += m;
                }
                ui &= ~m;
            }
            else
            {
                if (ui >> 31 != 0)
                {
                    return (sfloat)(-0.0f);
                }
                else if (ui << 1 != 0)
                {
                    return sfloat.One;
                }
            }

            return sfloat.FromRaw(ui);
        }

        /// <summary>
        /// Truncates x, removing its fractional parts
        /// </summary>
        public static sfloat truncf(sfloat x)
        {
            uint i = x.RawValue;
            int e = (int)(i >> 23 & 0xff) - 0x7f + 9;
            uint m;

            if (e >= 23 + 9)
            {
                return x;
            }

            if (e < 9)
            {
                e = 1;
            }

            m = unchecked((uint)-1) >> e;
            if ((i & m) == 0)
            {
                return x;
            }

            i &= ~m;
            return sfloat.FromRaw(i);
        }

        /// <summary>
        /// Returns the square root of x
        /// </summary>
        public static sfloat sqrtf(sfloat x)
        {
            int sign = unchecked((int)0x80000000);
            int ix;
            int s;
            int q;
            int m;
            int t;
            int i;
            uint r;

            ix = (int)x.RawValue;

            /* take care of Inf and NaN */
            if (((uint)ix & 0x7f800000) == 0x7f800000)
            {
                //return x * x + x; /* sqrt(NaN)=NaN, sqrt(+inf)=+inf, sqrt(-inf)=sNaN */
                if (x.IsNaN() || x.IsNegativeInfinity())
                {
                    return sfloat.NaN;
                }
                else // if (x.IsPositiveInfinity())
                {
                    return sfloat.PositiveInfinity;
                }
            }

            /* take care of zero */
            if (ix <= 0)
            {
                if ((ix & ~sign) == 0)
                {
                    return x; /* sqrt(+-0) = +-0 */
                }

                if (ix < 0)
                {
                    //return (x - x) / (x - x); /* sqrt(-ve) = sNaN */
                    return sfloat.NaN;
                }
            }

            /* normalize x */
            m = ix >> 23;
            if (m == 0)
            {
                /* subnormal x */
                i = 0;
                while ((ix & 0x00800000) == 0)
                {
                    ix <<= 1;
                    i += 1;
                }

                m -= i - 1;
            }

            m -= 127; /* unbias exponent */
            ix = (ix & 0x007fffff) | 0x00800000;
            if ((m & 1) == 1)
            {
                /* odd m, double x to make it even */
                ix += ix;
            }

            m >>= 1; /* m = [m/2] */

            /* generate sqrt(x) bit by bit */
            ix += ix;
            q = 0;
            s = 0;
            r = 0x01000000; /* r = moving bit from right to left */

            while (r != 0)
            {
                t = s + (int)r;
                if (t <= ix)
                {
                    s = t + (int)r;
                    ix -= t;
                    q += (int)r;
                }

                ix += ix;
                r >>= 1;
            }

            /* use floating add to find out rounding direction */
            if (ix != 0)
            {
                q += q & 1;
            }

            ix = (q >> 1) + 0x3f000000;
            ix += m << 23;
            return sfloat.FromRaw((uint)ix);
        }
    }
}


================================================
FILE: SoftFloat/libm/LICENSE.txt
================================================
Copyright (c) 2018 Jorge Aparicio

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.


================================================
FILE: SoftFloat/libm/Transcendental.cs
================================================

namespace SoftFloat
{
    public static partial class libm
    {
        private static sfloat scalbnf(sfloat x, int n)
        {
            sfloat x1p127 = sfloat.FromRaw(0x7f000000); // 0x1p127f === 2 ^ 127
            sfloat x1p_126 = sfloat.FromRaw(0x800000); // 0x1p-126f === 2 ^ -126
            sfloat x1p24 = sfloat.FromRaw(0x4b800000); // 0x1p24f === 2 ^ 24

            if (n > 127)
            {
                x *= x1p127;
                n -= 127;
                if (n > 127)
                {
                    x *= x1p127;
                    n -= 127;
                    if (n > 127)
                    {
                        n = 127;
                    }
                }
            }
            else if (n < -126)
            {
                x *= x1p_126 * x1p24;
                n += 126 - 24;
                if (n < -126)
                {
                    x *= x1p_126 * x1p24;
                    n += 126 - 24;
                    if (n < -126)
                    {
                        n = -126;
                    }
                }
            }

            return x * sfloat.FromRaw(((uint)(0x7f + n)) << 23);
        }

        /// <summary>
        /// Returns e raised to the power x (e ~= 2.71828182845904523536)
        /// </summary>
        public static sfloat expf(sfloat x)
        {
            const uint LN2_HI_U32 = 0x3f317200; // 6.9314575195e-01
            const uint LN2_LO_U32 = 0x35bfbe8e; // 1.4286067653e-06
            const uint INV_LN2_U32 = 0x3fb8aa3b; // 1.4426950216e+00

            const uint P1_U32 = 0x3e2aaa8f; // 1.6666625440e-1 /*  0xaaaa8f.0p-26 */
            const uint P2_U32 = 0xbb355215; // -2.7667332906e-3 /* -0xb55215.0p-32 */

            sfloat x1p127 = sfloat.FromRaw(0x7f000000); // 0x1p127f === 2 ^ 127
            sfloat x1p_126 = sfloat.FromRaw(0x800000); // 0x1p-126f === 2 ^ -126  /*original 0x1p-149f    ??????????? */
            uint hx = x.RawValue;
            int sign = (int)(hx >> 31); /* sign bit of x */
            bool signb = sign != 0;
            hx &= 0x7fffffff; /* high word of |x| */

            /* special cases */
            if (hx >= 0x42aeac50)
            {
                /* if |x| >= -87.33655f or NaN */
                if (hx > 0x7f800000)
                {
                    /* NaN */
                    return x;
                }

                if (hx >= 0x42b17218 && !signb)
                {
                    /* x >= 88.722839f */
                    /* overflow */
                    x *= x1p127;
                    return x;
                }

                if (signb)
                {
                    /* underflow */
                    //force_eval!(-x1p_126 / x);

                    if (hx >= 0x42cff1b5)
                    {
                        /* x <= -103.972084f */
                        return sfloat.Zero;
                    }
                }
            }

            /* argument reduction */
            int k;
            sfloat hi;
            sfloat lo;
            if (hx > 0x3eb17218)
            {
                /* if |x| > 0.5 ln2 */
                if (hx > 0x3f851592)
                {
                    /* if |x| > 1.5 ln2 */
                    k = (int)(sfloat.FromRaw(INV_LN2_U32) * x + (signb ? (sfloat)0.5f : (sfloat)(-0.5f)));
                }
                else
                {
                    k = 1 - sign - sign;
                }

                sfloat kf = (sfloat)k;
                hi = x - kf * sfloat.FromRaw(LN2_HI_U32); /* k*ln2hi is exact here */
                lo = kf * sfloat.FromRaw(LN2_LO_U32);
                x = hi - lo;
            }
            else if (hx > 0x39000000)
            {
                /* |x| > 2**-14 */
                k = 0;
                hi = x;
                lo = sfloat.Zero;
            }
            else
            {
                /* raise inexact */
                //force_eval!(x1p127 + x);
                return sfloat.One + x;
            }

            /* x is now in primary range */
            sfloat xx = x * x;
            sfloat c = x - xx * (sfloat.FromRaw(P1_U32) + xx * sfloat.FromRaw(P2_U32));
            sfloat y = sfloat.One + (x * c / ((sfloat)2.0f - c) - lo + hi);
            return k == 0 ? y : scalbnf(y, k);
        }

        /// <summary>
        /// Returns the natural logarithm (base e) of x
        /// </summary>
        public static sfloat logf(sfloat x)
        {
            const uint LN2_HI_U32 = 0x3f317180; // 6.9313812256e-01
            const uint LN2_LO_U32 = 0x3717f7d1; // 9.0580006145e-06

            /* |(log(1+s)-log(1-s))/s - Lg(s)| < 2**-34.24 (~[-4.95e-11, 4.97e-11]). */
            const uint LG1_U32 = 0x3f2aaaaa; // 0.66666662693 /*  0xaaaaaa.0p-24*/
            const uint LG2_U32 = 0x3eccce13; // 0.40000972152 /*  0xccce13.0p-25 */
            const uint LG3_U32 = 0x3e91e9ee; // 0.28498786688 /*  0x91e9ee.0p-25 */
            const uint LG4_U32 = 0x3e789e26; // 0.24279078841 /*  0xf89e26.0p-26 */

            uint ix = x.RawValue;
            int k = 0;

            if ((ix < 0x00800000) || ((ix >> 31) != 0))
            {
                /* x < 2**-126  */
                if (ix << 1 == 0)
                {
                    //return -1. / (x * x); /* log(+-0)=-inf */
                    return sfloat.NegativeInfinity;
                }

                if ((ix >> 31) != 0)
                {
                    //return (x - x) / 0.; /* log(-#) = NaN */
                    return sfloat.NaN;
                }

                /* subnormal number, scale up x */
                sfloat x1p25 = sfloat.FromRaw(0x4c000000); // 0x1p25f === 2 ^ 25
                k -= 25;
                x *= x1p25;
                ix = x.RawValue;
            }
            else if (ix >= 0x7f800000)
            {
                return x;
            }
            else if (ix == 0x3f800000)
            {
                return sfloat.Zero;
            }

            /* reduce x into [sqrt(2)/2, sqrt(2)] */
            ix += 0x3f800000 - 0x3f3504f3;
            k += ((int)(ix >> 23)) - 0x7f;
            ix = (ix & 0x007fffff) + 0x3f3504f3;
            x = sfloat.FromRaw(ix);

            sfloat f = x - sfloat.One;
            sfloat s = f / ((sfloat)2.0f + f);
            sfloat z = s * s;
            sfloat w = z * z;
            sfloat t1 = w * (sfloat.FromRaw(LG2_U32) + w * sfloat.FromRaw(LG4_U32));
            sfloat t2 = z * (sfloat.FromRaw(LG1_U32) + w * sfloat.FromRaw(LG3_U32));
            sfloat r = t2 + t1;
            sfloat hfsq = (sfloat)0.5f * f * f;
            sfloat dk = (sfloat)k;

            return s * (hfsq + r) + dk * sfloat.FromRaw(LN2_LO_U32) - hfsq + f + dk * sfloat.FromRaw(LN2_HI_U32);
        }

        /// <summary>
        /// Returns the base 2 logarithm of x
        /// </summary>
        public static sfloat log2f(sfloat x)
        {
            const uint IVLN2HI_U32 = 0x3fb8b000; // 1.4428710938e+00
            const uint IVLN2LO_U32 = 0xb9389ad4; // -1.7605285393e-04

            /* |(log(1+s)-log(1-s))/s - Lg(s)| < 2**-34.24 (~[-4.95e-11, 4.97e-11]). */
            const uint LG1_U32 = 0x3f2aaaaa; // 0.66666662693 /*  0xaaaaaa.0p-24*/
            const uint LG2_U32 = 0x3eccce13; // 0.40000972152 /*  0xccce13.0p-25 */
            const uint LG3_U32 = 0x3e91e9ee; // 0.28498786688 /*  0x91e9ee.0p-25 */
            const uint LG4_U32 = 0x3e789e26; // 0.24279078841 /*  0xf89e26.0p-26 */

            sfloat x1p25f = sfloat.FromRaw(0x4c000000); // 0x1p25f === 2 ^ 25

            uint ui = x.RawValue;
            sfloat hfsq;
            sfloat f;
            sfloat s;
            sfloat z;
            sfloat r;
            sfloat w;
            sfloat t1;
            sfloat t2;
            sfloat hi;
            sfloat lo;
            uint ix;
            int k;

            ix = ui;
            k = 0;
            if (ix < 0x00800000 || (ix >> 31) > 0)
            {
                /* x < 2**-126  */
                if (ix << 1 == 0)
                {
                    //return -1. / (x * x); /* log(+-0)=-inf */
                    return sfloat.NegativeInfinity;
                }

                if ((ix >> 31) > 0)
                {
                    //return (x - x) / 0.0; /* log(-#) = NaN */
                    return sfloat.NaN;
                }

                /* subnormal number, scale up x */
                k -= 25;
                x *= x1p25f;
                ui = x.RawValue;
                ix = ui;
            }
            else if (ix >= 0x7f800000)
            {
                return x;
            }
            else if (ix == 0x3f800000)
            {
                return sfloat.Zero;
            }

            /* reduce x into [sqrt(2)/2, sqrt(2)] */
            ix += 0x3f800000 - 0x3f3504f3;
            k += (int)(ix >> 23) - 0x7f;
            ix = (ix & 0x007fffff) + 0x3f3504f3;
            ui = ix;
            x = sfloat.FromRaw(ui);

            f = x - sfloat.One;
            s = f / ((sfloat)2.0f + f);
            z = s * s;
            w = z * z;
            t1 = w * (sfloat.FromRaw(LG2_U32) + w * sfloat.FromRaw(LG4_U32));
            t2 = z * (sfloat.FromRaw(LG1_U32) + w * sfloat.FromRaw(LG3_U32));
            r = t2 + t1;
            hfsq = (sfloat)0.5f * f * f;

            hi = f - hfsq;
            ui = hi.RawValue;
            ui &= 0xfffff000;
            hi = sfloat.FromRaw(ui);
            lo = f - hi - hfsq + s * (hfsq + r);
            return (lo + hi) * sfloat.FromRaw(IVLN2LO_U32) + lo * sfloat.FromRaw(IVLN2HI_U32) + hi * sfloat.FromRaw(IVLN2HI_U32) + (sfloat)k;
        }

        /// <summary>
        /// Returns x raised to the power y
        /// </summary>
        public static sfloat powf(sfloat x, sfloat y)
        {
            const uint BP_0_U32 = 0x3f800000; /* 1.0 */
            const uint BP_1_U32 = 0x3fc00000; /* 1.5 */
            const uint DP_H_0_U32 = 0x00000000; /* 0.0 */
            const uint DP_H_1_U32 = 0x3f15c000; /* 5.84960938e-01 */
            const uint DP_L_0_U32 = 0x00000000; /* 0.0 */
            const uint DP_L_1_U32 = 0x35d1cfdc; /* 1.56322085e-06 */
            const uint TWO24_U32 = 0x4b800000; /* 16777216.0 */
            const uint HUGE_U32 = 0x7149f2ca; /* 1.0e30 */
            const uint TINY_U32 = 0x0da24260; /* 1.0e-30 */
            const uint L1_U32 = 0x3f19999a; /* 6.0000002384e-01 */
            const uint L2_U32 = 0x3edb6db7; /* 4.2857143283e-01 */
            const uint L3_U32 = 0x3eaaaaab; /* 3.3333334327e-01 */
            const uint L4_U32 = 0x3e8ba305; /* 2.7272811532e-01 */
            const uint L5_U32 = 0x3e6c3255; /* 2.3066075146e-01 */
            const uint L6_U32 = 0x3e53f142; /* 2.0697501302e-01 */
            const uint P1_U32 = 0x3e2aaaab; /* 1.6666667163e-01 */
            const uint P2_U32 = 0xbb360b61; /* -2.7777778450e-03 */
            const uint P3_U32 = 0x388ab355; /* 6.6137559770e-05 */
            const uint P4_U32 = 0xb5ddea0e; /* -1.6533901999e-06 */
            const uint P5_U32 = 0x3331bb4c; /* 4.1381369442e-08 */
            const uint LG2_U32 = 0x3f317218; /* 6.9314718246e-01 */
            const uint LG2_H_U32 = 0x3f317200; /* 6.93145752e-01 */
            const uint LG2_L_U32 = 0x35bfbe8c; /* 1.42860654e-06 */
            const uint OVT_U32 = 0x3338aa3c; /* 4.2995665694e-08 =-(128-log2(ovfl+.5ulp)) */
            const uint CP_U32 = 0x3f76384f; /* 9.6179670095e-01 =2/(3ln2) */
            const uint CP_H_U32 = 0x3f764000; /* 9.6191406250e-01 =12b cp */
            const uint CP_L_U32 = 0xb8f623c6; /* -1.1736857402e-04 =tail of cp_h */
            const uint IVLN2_U32 = 0x3fb8aa3b; /* 1.4426950216e+00 */
            const uint IVLN2_H_U32 = 0x3fb8aa00; /* 1.4426879883e+00 */
            const uint IVLN2_L_U32 = 0x36eca570; /* 7.0526075433e-06 */

            sfloat z;
            sfloat ax;
            sfloat z_h;
            sfloat z_l;
            sfloat p_h;
            sfloat p_l;
            sfloat y1;
            sfloat t1;
            sfloat t2;
            sfloat r;
            sfloat s;
            sfloat sn;
            sfloat t;
            sfloat u;
            sfloat v;
            sfloat w;
            int i;
            int j;
            int k;
            int yisint;
            int n;
            int hx;
            int hy;
            int ix;
            int iy;
            int iS;

            hx = (int)x.RawValue;
            hy = (int)y.RawValue;

            ix = hx & 0x7fffffff;
            iy = hy & 0x7fffffff;

            /* x**0 = 1, even if x is NaN */
            if (iy == 0)
            {
                return sfloat.One;
            }

            /* 1**y = 1, even if y is NaN */
            if (hx == 0x3f800000)
            {
                return sfloat.One;
            }

            /* NaN if either arg is NaN */
            if (ix > 0x7f800000 || iy > 0x7f800000)
            {
                return sfloat.NaN;
            }

            /* determine if y is an odd int when x < 0
             * yisint = 0       ... y is not an integer
             * yisint = 1       ... y is an odd int
             * yisint = 2       ... y is an even int
             */
            yisint = 0;
            if (hx < 0)
            {
                if (iy >= 0x4b800000)
                {
                    yisint = 2; /* even integer y */
                }
                else if (iy >= 0x3f800000)
                {
                    k = (iy >> 23) - 0x7f; /* exponent */
                    j = iy >> (23 - k);
                    if ((j << (23 - k)) == iy)
                    {
                        yisint = 2 - (j & 1);
                    }
                }
            }

            /* special value of y */
            if (iy == 0x7f800000)
            {
                /* y is +-inf */
                if (ix == 0x3f800000)
                {
                    /* (-1)**+-inf is 1 */
                    return sfloat.One;
                }
                else if (ix > 0x3f800000)
                {
                    /* (|x|>1)**+-inf = inf,0 */
                    return hy >= 0 ? y : sfloat.Zero;
                }
                else
                {
                    /* (|x|<1)**+-inf = 0,inf */
                    return hy >= 0 ? sfloat.Zero : -y;
                }
            }

            if (iy == 0x3f800000)
            {
                /* y is +-1 */
                return hy >= 0 ? x : sfloat.One / x;
            }

            if (hy == 0x40000000)
            {
                /* y is 2 */
                return x * x;
            }

            if (hy == 0x3f000000
               /* y is  0.5 */
               && hx >= 0)
            {
                /* x >= +0 */
                return sqrtf(x);
            }

            ax = sfloat.Abs(x);
            /* special value of x */
            if (ix == 0x7f800000 || ix == 0 || ix == 0x3f800000)
            {
                /* x is +-0,+-inf,+-1 */
                z = ax;
                if (hy < 0)
                {
                    /* z = (1/|x|) */
                    z = sfloat.One / z;
                }

                if (hx < 0)
                {
                    if (((ix - 0x3f800000) | yisint) == 0)
                    {
                        z = (z - z) / (z - z); /* (-1)**non-int is NaN */
                    }
                    else if (yisint == 1)
                    {
                        z = -z; /* (x<0)**odd = -(|x|**odd) */
                    }
                }

                return z;
            }

            sn = sfloat.One; /* sign of result */
            if (hx < 0)
            {
                if (yisint == 0)
                {
                    /* (x<0)**(non-int) is NaN */
                    //return (x - x) / (x - x);
                    return sfloat.NaN;
                }

                if (yisint == 1)
                {
                    /* (x<0)**(odd int) */
                    sn = -sfloat.One;
                }
            }

            /* |y| is HUGE */
            if (iy > 0x4d000000)
            {
                /* if |y| > 2**27 */
                /* over/underflow if x is not close to one */
                if (ix < 0x3f7ffff8)
                {
                    return hy < 0
                        ? sn * sfloat.FromRaw(HUGE_U32) * sfloat.FromRaw(HUGE_U32)
                        : sn * sfloat.FromRaw(TINY_U32) * sfloat.FromRaw(TINY_U32);
                }

                if (ix > 0x3f800007)
                {
                    return hy > 0
                        ? sn * sfloat.FromRaw(HUGE_U32) * sfloat.FromRaw(HUGE_U32)
                        : sn * sfloat.FromRaw(TINY_U32) * sfloat.FromRaw(TINY_U32);
                }

                /* now |1-x| is TINY <= 2**-20, suffice to compute
                log(x) by x-x^2/2+x^3/3-x^4/4 */
                t = ax - sfloat.One; /* t has 20 trailing zeros */
                w = (t * t) * (sfloat.FromRaw(0x3f000000) - t * (sfloat.FromRaw(0x3eaaaaab) - t * sfloat.FromRaw(0x3e800000)));
                u = sfloat.FromRaw(IVLN2_H_U32) * t; /* IVLN2_H has 16 sig. bits */
                v = t * sfloat.FromRaw(IVLN2_L_U32) - w * sfloat.FromRaw(IVLN2_U32);
                t1 = u + v;
                iS = (int)t1.RawValue;
                t1 = sfloat.FromRaw((uint)iS & 0xfffff000);
                t2 = v - (t1 - u);
            }
            else
            {
                sfloat s2;
                sfloat s_h;
                sfloat s_l;
                sfloat t_h;
                sfloat t_l;

                n = 0;
                /* take care subnormal number */
                if (ix < 0x00800000)
                {
                    ax *= sfloat.FromRaw(TWO24_U32);
                    n -= 24;
                    ix = (int)ax.RawValue;
                }

                n += ((ix) >> 23) - 0x7f;
                j = ix & 0x007fffff;
                /* determine interval */
                ix = j | 0x3f800000; /* normalize ix */
                if (j <= 0x1cc471)
                {
                    /* |x|<sqrt(3/2) */
                    k = 0;
                }
                else if (j < 0x5db3d7)
                {
                    /* |x|<sqrt(3)   */
                    k = 1;
                }
                else
                {
                    k = 0;
                    n += 1;
                    ix -= 0x00800000;
                }

                ax = sfloat.FromRaw((uint)ix);

                /* compute s = s_h+s_l = (x-1)/(x+1) or (x-1.5)/(x+1.5) */
                u = ax - sfloat.FromRaw(k == 0 ? BP_0_U32 : BP_1_U32); /* bp[0]=1.0, bp[1]=1.5 */
                v = sfloat.One / (ax + sfloat.FromRaw(k == 0 ? BP_0_U32 : BP_1_U32));
                s = u * v;
                s_h = s;
                iS = (int)s_h.RawValue;
                s_h = sfloat.FromRaw((uint)iS & 0xfffff000);

                /* t_h=ax+bp[k] High */
                iS = (int)((((uint)ix >> 1) & 0xfffff000) | 0x20000000);
                t_h = sfloat.FromRaw((uint)iS + 0x00400000 + (((uint)k) << 21));
                t_l = ax - (t_h - sfloat.FromRaw(k == 0 ? BP_0_U32 : BP_1_U32));
                s_l = v * ((u - s_h * t_h) - s_h * t_l);

                /* compute log(ax) */
                s2 = s * s;
                r = s2 * s2 * (sfloat.FromRaw(L1_U32) + s2 * (sfloat.FromRaw(L2_U32) + s2 * (sfloat.FromRaw(L3_U32) + s2 * (sfloat.FromRaw(L4_U32) + s2 * (sfloat.FromRaw(L5_U32) + s2 * sfloat.FromRaw(L6_U32))))));
                r += s_l * (s_h + s);
                s2 = s_h * s_h;
                t_h = sfloat.FromRaw(0x40400000) + s2 + r;
                iS = (int)t_h.RawValue;
                t_h = sfloat.FromRaw((uint)iS & 0xfffff000);
                t_l = r - ((t_h - sfloat.FromRaw(0x40400000)) - s2);

                /* u+v = s*(1+...) */
                u = s_h * t_h;
                v = s_l * t_h + t_l * s;

                /* 2/(3log2)*(s+...) */
                p_h = u + v;
                iS = (int)p_h.RawValue;
                p_h = sfloat.FromRaw((uint)iS & 0xfffff000);
                p_l = v - (p_h - u);
                z_h = sfloat.FromRaw(CP_H_U32) * p_h; /* cp_h+cp_l = 2/(3*log2) */
                z_l = sfloat.FromRaw(CP_L_U32) * p_h + p_l * sfloat.FromRaw(CP_U32) + sfloat.FromRaw(k == 0 ? DP_L_0_U32 : DP_L_1_U32);

                /* log2(ax) = (s+..)*2/(3*log2) = n + dp_h + z_h + z_l */
                t = (sfloat)n;
                t1 = ((z_h + z_l) + sfloat.FromRaw(k == 0 ? DP_H_0_U32 : DP_H_1_U32)) + t;
                iS = (int)t1.RawValue;
                t1 = sfloat.FromRaw((uint)iS & 0xfffff000);
                t2 = z_l - (((t1 - t) - sfloat.FromRaw(k == 0 ? DP_H_0_U32 : DP_H_1_U32)) - z_h);
            };

            /* split up y into y1+y2 and compute (y1+y2)*(t1+t2) */
            iS = (int)y.RawValue;
            y1 = sfloat.FromRaw((uint)iS & 0xfffff000);
            p_l = (y - y1) * t1 + y * t2;
            p_h = y1 * t1;
            z = p_l + p_h;
            j = (int)z.RawValue;
            if (j > 0x43000000)
            {
                /* if z > 128 */
                return sn * sfloat.FromRaw(HUGE_U32) * sfloat.FromRaw(HUGE_U32); /* overflow */
            }
            else if (j == 0x43000000)
            {
                /* if z == 128 */
                if (p_l + sfloat.FromRaw(OVT_U32) > z - p_h)
                {
                    return sn * sfloat.FromRaw(HUGE_U32) * sfloat.FromRaw(HUGE_U32); /* overflow */
                }
            }
            else if ((j & 0x7fffffff) > 0x43160000)
            {
                /* z < -150 */
                // FIXME: check should be  (uint32_t)j > 0xc3160000
                return sn * sfloat.FromRaw(TINY_U32) * sfloat.FromRaw(TINY_U32); /* underflow */
            }
            else if ((uint)j == 0xc3160000
                    /* z == -150 */
                    && p_l <= z - p_h)
            {
                return sn * sfloat.FromRaw(TINY_U32) * sfloat.FromRaw(TINY_U32); /* underflow */
            }

            /*
             * compute 2**(p_h+p_l)
             */
            i = j & 0x7fffffff;
            k = (i >> 23) - 0x7f;
            n = 0;
            if (i > 0x3f000000)
            {
                /* if |z| > 0.5, set n = [z+0.5] */
                n = j + (0x00800000 >> (k + 1));
                k = ((n & 0x7fffffff) >> 23) - 0x7f; /* new k for n */
                t = sfloat.FromRaw((uint)n & ~(0x007fffffu >> k));
                n = ((n & 0x007fffff) | 0x00800000) >> (23 - k);
                if (j < 0)
                {
                    n = -n;
                }
                p_h -= t;
            }

            t = p_l + p_h;
            iS = (int)t.RawValue;
            t = sfloat.FromRaw((uint)iS & 0xffff8000);
            u = t * sfloat.FromRaw(LG2_H_U32);
            v = (p_l - (t - p_h)) * sfloat.FromRaw(LG2_U32) + t * sfloat.FromRaw(LG2_L_U32);
            z = u + v;
            w = v - (z - u);
            t = z * z;
            t1 = z - t * (sfloat.FromRaw(P1_U32) + t * (sfloat.FromRaw(P2_U32) + t * (sfloat.FromRaw(P3_U32) + t * (sfloat.FromRaw(P4_U32) + t * sfloat.FromRaw(P5_U32)))));
            r = (z * t1) / (t1 - sfloat.FromRaw(0x40000000)) - (w + z * w);
            z = sfloat.One - (r - z);
            j = (int)z.RawValue;
            j += n << 23;
            if ((j >> 23) <= 0)
            {
                /* subnormal output */
                z = scalbnf(z, n);
            }
            else
            {
                z = sfloat.FromRaw((uint)j);
            }

            return sn * z;
        }
    }
}


================================================
FILE: SoftFloat/libm/Trigonometry.cs
================================================

namespace SoftFloat
{
    public static partial class libm
    {
        const uint pi = 0x40490fdb; // 3.1415926535897932384626433832795
        const uint half_pi = 0x3fc90fdb; // 1.5707963267948966192313216916398
        const uint two_pi = 0x40c90fdb; // 6.283185307179586476925286766559
        const uint pi_over_4 = 0x3f490fdb; // 0.78539816339744830961566084581988
        const uint pi_times_3_over_4 = 0x4016cbe4; // 2.3561944901923449288469825374596

        /// <summary>
        /// Returns the sine of x
        /// </summary>
        public static sfloat sinf(sfloat x)
        {
            const uint pi_squared_times_five = 0x42456460; // 49.348022005446793094172454999381

            // https://en.wikipedia.org/wiki/Bhaskara_I%27s_sine_approximation_formula
            // sin(x) ~= (16x * (pi - x)) / (5pi^2 - 4x * (pi - x)) if 0 <= x <= pi

            // move x into range
            x %= sfloat.FromRaw(two_pi);
            if (x.IsNegative())
            {
                x += sfloat.FromRaw(two_pi);
            }

            bool negate;
            if (x > sfloat.FromRaw(pi))
            {
                // pi < x <= 2pi, we need to move x to the 0 <= x <= pi range
                // also, we need to negate the result before returning it
                x = sfloat.FromRaw(two_pi) - x;
                negate = true;
            }
            else
            {
                negate = false;
            }

            sfloat piMinusX = sfloat.FromRaw(pi) - x;
            sfloat result = ((sfloat)16.0f * x * piMinusX) / (sfloat.FromRaw(pi_squared_times_five) - (sfloat)4.0f * x * piMinusX);
            return negate ? -result : result;
        }

        /// <summary>
        /// Returns the cosine of x
        /// </summary>
        public static sfloat cosf(sfloat x) => sinf(x + sfloat.FromRaw(half_pi));

        /// <summary>
        /// Returns the tangent of x
        /// </summary>
        public static sfloat tanf(sfloat x) => sinf(x) / cosf(x);

        /// <summary>
        /// Returns the square root of (x*x + y*y)
        /// </summary>
        public static sfloat hypotf(sfloat x, sfloat y)
        {
            sfloat w;

            int ha = (int)x.RawValue;
            ha &= 0x7fffffff;

            int hb = (int)y.RawValue;
            hb &= 0x7fffffff;

            if (hb > ha)
            {
                int temp = ha;
                ha = hb;
                hb = temp;
            }

            sfloat a = sfloat.FromRaw((uint)ha); /* a <- |a| */
            sfloat b = sfloat.FromRaw((uint)hb); /* b <- |b| */

            if (ha - hb > 0xf000000)
            {
                return a + b;
            } /* x/y > 2**30 */

            uint k = 0;
            if (ha > 0x58800000)
            {
                /* a>2**50 */
                if (ha >= 0x7f800000)
                {
                    /* Inf or NaN */
                    w = a + b; /* for sNaN */
                    if (ha == 0x7f800000)
                    {
                        w = a;
                    }

                    if (hb == 0x7f800000)
                    {
                        w = b;
                    }

                    return w;
                }

                /* scale a and b by 2**-60 */
                ha -= 0x5d800000;
                hb -= 0x5d800000;
                k += 60;
                a = sfloat.FromRaw((uint)ha);
                b = sfloat.FromRaw((uint)hb);
            }

            if (hb < 0x26800000)
            {
                /* b < 2**-50 */
                if (hb <= 0x007fffff)
                {
                    /* subnormal b or 0 */
                    if (hb == 0)
                    {
                        return a;
                    }

                    sfloat t1 = sfloat.FromRaw(0x3f000000); /* t1=2^126 */
                    b *= t1;
                    a *= t1;
                    k -= 126;
                }
                else
                {
                    /* scale a and b by 2^60 */
                    ha += 0x5d800000; /* a *= 2^60 */
                    hb += 0x5d800000; /* b *= 2^60 */
                    k -= 60;
                    a = sfloat.FromRaw((uint)ha);
                    b = sfloat.FromRaw((uint)hb);
                }
            }

            /* medium size a and b */
            w = a - b;
            if (w > b)
            {
                sfloat t1 = sfloat.FromRaw(((uint)ha) & 0xfffff000);
                sfloat t2 = a - t1;
                w = sqrtf(t1 * t1 - (b * (-b) - t2 * (a + t1)));
            }
            else
            {
                a += a;
                sfloat y1 = sfloat.FromRaw(((uint)hb) & 0xfffff000);
                sfloat y2 = b - y1;
                sfloat t1 = sfloat.FromRaw(((uint)ha) + 0x00800000);
                sfloat t2 = a - t1;
                w = sqrtf(t1 * y1 - (w * (-w) - (t1 * y2 + t2 * b)));
            }

            if (k != 0)
            {
                sfloat t1 = sfloat.FromRaw(0x3f800000 + (k << 23));
                return t1 * w;
            }
            else
            {
                return w;
            };
        }


        private static readonly uint[] ATAN_HI = new uint[4]
        {
            0x3eed6338, // 4.6364760399e-01, /* atan(0.5)hi */
            0x3f490fda, // 7.8539812565e-01, /* atan(1.0)hi */
            0x3f7b985e, // 9.8279368877e-01, /* atan(1.5)hi */
            0x3fc90fda, // 1.5707962513e+00, /* atan(inf)hi */
        };

        private static readonly uint[] ATAN_LO = new uint[4]
        {
            0x31ac3769, // 5.0121582440e-09, /* atan(0.5)lo */
            0x33222168, // 3.7748947079e-08, /* atan(1.0)lo */
            0x33140fb4, // 3.4473217170e-08, /* atan(1.5)lo */
            0x33a22168, // 7.5497894159e-08, /* atan(inf)lo */
        };

        private static readonly uint[] A_T = new uint[5]
        {
            0x3eaaaaa9, // 3.3333328366e-01
            0xbe4cca98, // -1.9999158382e-01
            0x3e11f50d, // 1.4253635705e-01
            0xbdda1247, // -1.0648017377e-01
            0x3d7cac25  // 6.1687607318e-02
        };

        /// <summary>
        /// Returns the arctangent of x
        /// </summary>
        public unsafe static sfloat atanf(sfloat x)
        {
            sfloat z;

            uint ix = x.RawValue;
            bool sign = (ix >> 31) != 0;
            ix &= 0x7fffffff;

            if (ix >= 0x4c800000)
            {
                /* if |x| >= 2**26 */
                if (x.IsNaN())
                {
                    return x;
                }

                sfloat x1p_120 = sfloat.FromRaw(0x03800000); // 0x1p-120 === 2 ^ (-120)
                z = sfloat.FromRaw(ATAN_HI[3]) + x1p_120;
                return sign ? -z : z;
            }

            int id;
            if (ix < 0x3ee00000)
            {
                /* |x| < 0.4375 */
                if (ix < 0x39800000)
                {
                    /* |x| < 2**-12 */
                    //if (ix < 0x00800000)
                    //{
                    //    /* raise underflow for subnormal x */
                    //    force_eval!(x * x);
                    //}
                    return x;
                }
                id = -1;
            }
            else
            {
                x = sfloat.Abs(x);
                if (ix < 0x3f980000)
                {
                    /* |x| < 1.1875 */
                    if (ix < 0x3f300000)
                    {
                        /*  7/16 <= |x| < 11/16 */
                        x = ((sfloat)2.0f * x - (sfloat)1.0f) / ((sfloat)2.0f + x);
                        id = 0;
                    }
                    else
                    {
                        /* 11/16 <= |x| < 19/16 */
                        x = (x - (sfloat)1.0f) / (x + (sfloat)1.0f);
                        id = 1;
                    }
                }
                else if (ix < 0x401c0000)
                {
                    /* |x| < 2.4375 */
                    x = (x - (sfloat)1.5f) / ((sfloat)1.0f + (sfloat)1.5f * x);
                    id = 2;
                }
                else
                {
                    /* 2.4375 <= |x| < 2**26 */
                    x = (sfloat)(-1.0f) / x;
                    id = 3;
                }
            };

            /* end of argument reduction */
            z = x * x;
            sfloat w = z * z;

            /* break sum from i=0 to 10 aT[i]z**(i+1) into odd and even poly */
            sfloat s1 = z * (sfloat.FromRaw(A_T[0]) + w * (sfloat.FromRaw(A_T[2]) + w * sfloat.FromRaw(A_T[4])));
            sfloat s2 = w * (sfloat.FromRaw(A_T[1]) + w * sfloat.FromRaw(A_T[3]));
            if (id < 0)
            {
                return x - x * (s1 + s2);
            }

            z = sfloat.FromRaw(ATAN_HI[id]) - ((x * (s1 + s2) - sfloat.FromRaw(ATAN_LO[id])) - x);
            return sign ? -z : z;
        }

        /// <summary>
        /// Returns the signed angle between the positive x axis, and the direction (x, y)
        /// </summary>
        public static sfloat atan2f(sfloat y, sfloat x)
        {
            if (x.IsNaN() || y.IsNaN())
            {
                return x + y;
            }

            uint ix = x.RawValue;
            uint iy = y.RawValue;

            if (ix == 0x3f800000)
            {
                /* x=1.0 */
                return atanf(y);
            }

            uint m = ((iy >> 31) & 1) | ((ix >> 30) & 2); /* 2*sign(x)+sign(y) */
            ix &= 0x7fffffff;
            iy &= 0x7fffffff;

            const uint PI_LO_U32 = 0xb3bbbd2e; // -8.7422776573e-08

            /* when y = 0 */
            if (iy == 0)
            {
                switch (m)
                {
                    case 0:
                    case 1:
                        return y; /* atan(+-0,+anything)=+-0 */
                    case 2:
                        return sfloat.FromRaw(pi); /* atan(+0,-anything) = pi */
                    case 3:
                    default:
                        return -sfloat.FromRaw(pi); /* atan(-0,-anything) =-pi */
                }
            }

            /* when x = 0 */
            if (ix == 0)
            {
                return (m & 1) != 0 ? -sfloat.FromRaw(half_pi) : sfloat.FromRaw(half_pi);
            }

            /* when x is INF */
            if (ix == 0x7f800000)
            {
                if (iy == 0x7f800000)
                {
                    switch (m)
                    {
                        case 0:
                            return sfloat.FromRaw(pi_over_4); /* atan(+INF,+INF) */
                        case 1:
                            return -sfloat.FromRaw(pi_over_4); /* atan(-INF,+INF) */
                        case 2:
                            return sfloat.FromRaw(pi_times_3_over_4); /* atan(+INF,-INF)*/
                        case 3:
                        default:
                            return -sfloat.FromRaw(pi_times_3_over_4); /* atan(-INF,-INF)*/
                    }
                }
                else
                {
                    switch (m)
                    {
                        case 0:
                            return sfloat.Zero; /* atan(+...,+INF) */
                        case 1:
                            return -sfloat.Zero; /* atan(-...,+INF) */
                        case 2:
                            return sfloat.FromRaw(pi); /* atan(+...,-INF) */
                        case 3:
                        default:
                            return -sfloat.FromRaw(pi); /* atan(-...,-INF) */
                    }
                }
            }

            /* |y/x| > 0x1p26 */
            if (ix + (26 << 23) < iy || iy == 0x7f800000)
            {
                return (m & 1) != 0 ? -sfloat.FromRaw(half_pi) : sfloat.FromRaw(half_pi);
            }

            /* z = atan(|y/x|) with correct underflow */
            sfloat z = (m & 2) != 0 && iy + (26 << 23) < ix
                ? sfloat.Zero /*|y/x| < 0x1p-26, x < 0 */
                : atanf(sfloat.Abs(y / x));

            switch (m)
            {
                case 0:
                    return z; /* atan(+,+) */
                case 1:
                    return -z; /* atan(-,+) */
                case 2:
                    return sfloat.FromRaw(pi) - (z - sfloat.FromRaw(PI_LO_U32)); /* atan(+,-) */
                case 3:
                default:
                    return (z - sfloat.FromRaw(PI_LO_U32)) - sfloat.FromRaw(pi); /* atan(-,-) */
            }
        }

        /// <summary>
        /// Returns the arccosine of x
        /// </summary>
        public static sfloat acosf(sfloat x)
        {
            const uint PIO2_HI_U32 = 0x3fc90fda; // 1.5707962513e+00
            const uint PIO2_LO_U32 = 0x33a22168; // 7.5497894159e-08
            const uint P_S0_U32 = 0x3e2aaa75; // 1.6666586697e-01
            const uint P_S1_U32 = 0xbd2f13ba; // -4.2743422091e-02
            const uint P_S2_U32 = 0xbc0dd36b; // -8.6563630030e-03
            const uint Q_S1_U32 = 0xbf34e5ae; // - 7.0662963390e-01

            static sfloat r(sfloat z)
            {
                sfloat p = z * (sfloat.FromRaw(P_S0_U32) + z * (sfloat.FromRaw(P_S1_U32) + z * sfloat.FromRaw(P_S2_U32)));
                sfloat q = (sfloat)1.0f + z * sfloat.FromRaw(Q_S1_U32);
                return p / q;
            }

            sfloat x1p_120 = sfloat.FromRaw(0x03800000); // 0x1p-120 === 2 ^ (-120)

            sfloat z;
            sfloat w;
            sfloat s;

            uint hx = x.RawValue;
            uint ix = hx & 0x7fffffff;

            /* |x| >= 1 or nan */
            if (ix >= 0x3f800000)
            {
                if (ix == 0x3f800000)
                {
                    if ((hx >> 31) != 0)
                    {
                        return (sfloat)2.0f * sfloat.FromRaw(PIO2_HI_U32) + x1p_120;
                    }

                    return sfloat.Zero;
                }

                return sfloat.NaN;
            }

            /* |x| < 0.5 */
            if (ix < 0x3f000000)
            {
                if (ix <= 0x32800000)
                {
                    /* |x| < 2**-26 */
                    return sfloat.FromRaw(PIO2_HI_U32) + x1p_120;
                }

                return sfloat.FromRaw(PIO2_HI_U32) - (x - (sfloat.FromRaw(PIO2_LO_U32) - x * r(x * x)));
            }

            /* x < -0.5 */
            if ((hx >> 31) != 0)
            {
                z = ((sfloat)1.0f + x) * (sfloat)0.5f;
                s = sqrtf(z);
                w = r(z) * s - sfloat.FromRaw(PIO2_LO_U32);
                return (sfloat)2.0 * (sfloat.FromRaw(PIO2_HI_U32) - (s + w));
            }

            /* x > 0.5 */
            z = ((sfloat)1.0f - x) * (sfloat)0.5f;
            s = sqrtf(z);
            hx = s.RawValue;
            sfloat df = sfloat.FromRaw(hx & 0xfffff000);
            sfloat c = (z - df * df) / (s + df);
            w = r(z) * s + c;
            return (sfloat)2.0f * (df + w);
        }

        /// <summary>
        /// Returns the arcsine of x
        /// </summary>
        public static sfloat asinf(sfloat x) => sfloat.FromRaw(half_pi) - acosf(x);
    }
}


================================================
FILE: SoftFloat/sfloat.cs
================================================

// mostly from https://github.com/CodesInChaos/SoftFloat

// Copyright (c) 2011 CodesInChaos
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software
// and associated documentation files (the "Software"), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies
// or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// The MIT License (MIT) - http://www.opensource.org/licenses/mit-license.php
// If you need a different license please contact me

using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;

namespace SoftFloat
{
    // Internal representation is identical to IEEE binary32 floating point numbers
    [DebuggerDisplay("{ToStringInv()}")]
    public struct sfloat : IEquatable<sfloat>, IComparable<sfloat>, IComparable, IFormattable
    {
        /// <summary>
        /// Raw byte representation of an sfloat number
        /// </summary>
        private readonly uint rawValue;

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private sfloat(uint raw)
        {
            rawValue = raw;
        }

        /// <summary>
        /// Creates an sfloat number from its raw byte representation
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static sfloat FromRaw(uint raw)
        {
            return new sfloat(raw);
        }

        public uint RawValue => rawValue;

        private uint RawMantissa { get { return rawValue & 0x7FFFFF; } }
        private int Mantissa
        {
            get
            {
                if (RawExponent != 0)
                {
                    uint sign = (uint)((int)rawValue >> 31);
                    return (int)(((RawMantissa | 0x800000) ^ sign) - sign);
                }
                else
                {
                    uint sign = (uint)((int)rawValue >> 31);
                    return (int)(((RawMantissa) ^ sign) - sign);
                }
            }
        }

        private sbyte Exponent => (sbyte)(RawExponent - ExponentBias);
        private byte RawExponent => (byte)(rawValue >> MantissaBits);


        private const uint SignMask = 0x80000000;
        private const int MantissaBits = 23;
        private const int ExponentBits = 8;
        private const int ExponentBias = 127;

        private const uint RawZero = 0;
        private const uint RawNaN = 0xFFC00000; // Same as float.NaN
        private const uint RawPositiveInfinity = 0x7F800000;
        private const uint RawNegativeInfinity = RawPositiveInfinity ^ SignMask;
        private const uint RawOne = 0x3F800000;
        private const uint RawMinusOne = RawOne ^ SignMask;
        private const uint RawMaxValue = 0x7F7FFFFF;
        private const uint RawMinValue = 0x7F7FFFFF ^ SignMask;
        private const uint RawEpsilon = 0x00000001;
        private const uint RawLog2OfE = 0;


        public static sfloat Zero => new sfloat(0);
        public static sfloat PositiveInfinity => new sfloat(RawPositiveInfinity);
        public static sfloat NegativeInfinity => new sfloat(RawNegativeInfinity);
        public static sfloat NaN => new sfloat(RawNaN);
        public static sfloat One => new sfloat(RawOne);
        public static sfloat MinusOne => new sfloat(RawMinusOne);
        public static sfloat MaxValue => new sfloat(RawMaxValue);
        public static sfloat MinValue => new sfloat(RawMinValue);
        public static sfloat Epsilon => new sfloat(RawEpsilon);

        public override string ToString() => ((float)this).ToString();

        /// <summary>
        /// Creates an sfloat number from its parts: sign, exponent, mantissa
        /// </summary>
        /// <param name="sign">Sign of the number: false = the number is positive, true = the number is negative</param>
        /// <param name="exponent">Exponent of the number</param>
        /// <param name="mantissa">Mantissa (significand) of the number</param>
        /// <returns></returns>
        public static sfloat FromParts(bool sign, uint exponent, uint mantissa)
        {
            return FromRaw((sign ? SignMask : 0) | ((exponent & 0xff) << MantissaBits) | (mantissa & ((1 << MantissaBits) - 1)));
        }

        /// <summary>
        /// Creates an sfloat number from a float value
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static explicit operator sfloat(float f)
        {
            uint raw = ReinterpretFloatToInt32(f);
            return new sfloat(raw);
        }

        /// <summary>
        /// Converts an sfloat number to a float value
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static explicit operator float(sfloat f)
        {
            uint raw = f.rawValue;
            return ReinterpretIntToFloat32(raw);
        }

        /// <summary>
        /// Converts an sfloat number to an integer
        /// </summary>
        public static explicit operator int(sfloat f)
        {
            if (f.Exponent < 0)
            {
                return 0;
            }

            int shift = MantissaBits - f.Exponent;
            var mantissa = (int)(f.RawMantissa | (1 << MantissaBits));
            int value = shift < 0 ? mantissa << -shift : mantissa >> shift;
            return f.IsPositive() ? value : -value;
        }

        /// <summary>
        /// Creates an sfloat number from an integer
        /// </summary>
        public static explicit operator sfloat(int value)
        {
            if (value == 0)
            {
                return Zero;
            }

            if (value == int.MinValue)
            {
                // special case
                return FromRaw(0xcf000000);
            }

            bool negative = value < 0;
            int u = Math.Abs(value);

            int shifts;

            int lzcnt = clz(u);
            if (lzcnt < 8)
            {
                int count = 8 - lzcnt;
                u >>= count;
                shifts = -count;
            }
            else
            {
                int count = lzcnt - 8;
                u <<= count;
                shifts = count;
            }

            uint exponent = (uint)(ExponentBias + MantissaBits - shifts);
            return FromParts(negative, exponent, (uint)u);
        }

        private static readonly int[] debruijn32 = new int[64]
        {
            32, 8,  17, -1, -1, 14, -1, -1, -1, 20, -1, -1, -1, 28, -1, 18,
            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0,  26, 25, 24,
            4,  11, 23, 31, 3,  7,  10, 16, 22, 30, -1, -1, 2,  6,  13, 9,
            -1, 15, -1, 21, -1, 29, 19, -1, -1, -1, -1, -1, 1,  27, 5,  12
        };

        /// <summary>
        /// Returns the leading zero count of the given 32-bit integer
        /// </summary>
        private static int clz(int x)
        {
            x |= x >> 1;
            x |= x >> 2;
            x |= x >> 4;
            x |= x >> 8;
            x |= x >> 16;

            return debruijn32[(uint)x * 0x8c0b2891u >> 26];
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static sfloat operator -(sfloat f) => new sfloat(f.rawValue ^ 0x80000000);

        private static readonly int[] normalizeAmounts = new int[]
        {
            0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 16, 16, 16, 16, 16, 16, 16, 16, 24, 24, 24, 24, 24, 24, 24
        };

        private static sfloat InternalAdd(sfloat f1, sfloat f2)
        {
            byte rawExp1 = f1.RawExponent;
            byte rawExp2 = f2.RawExponent;
            int deltaExp = rawExp1 - rawExp2;

            if (rawExp1 != 255)
            {
                //Finite
                if (deltaExp > 25)
                {
                    return f1;
                }

                int man1;
                int man2;
                if (rawExp2 != 0)
                {
                    // man1 = f1.Mantissa
                    // http://graphics.stanford.edu/~seander/bithacks.html#ConditionalNegate
                    uint sign1 = (uint)((int)f1.rawValue >> 31);
                    man1 = (int)(((f1.RawMantissa | 0x800000) ^ sign1) - sign1);
                    // man2 = f2.Mantissa
                    uint sign2 = (uint)((int)f2.rawValue >> 31);
                    man2 = (int)(((f2.RawMantissa | 0x800000) ^ sign2) - sign2);
                }
                else
                {
                    // Subnorm
                    // man2 = f2.Mantissa
                    uint sign2 = (uint)((int)f2.rawValue >> 31);
                    man2 = (int)((f2.RawMantissa ^ sign2) - sign2);

                    man1 = f1.Mantissa;

                    rawExp2 = 1;
                    if (rawExp1 == 0)
                    {
                        rawExp1 = 1;
                    }

                    deltaExp = rawExp1 - rawExp2;
                }

                int man = (man1 << 6) + ((man2 << 6) >> deltaExp);
                int absMan = Math.Abs(man);
                if (absMan == 0)
                {
                    return Zero;
                }

                int rawExp = rawExp1 - 6;

                int amount = normalizeAmounts[clz(absMan)];
                rawExp -= amount;
                absMan <<= amount;

                int msbIndex = BitScanReverse8(absMan >> MantissaBits);
                rawExp += msbIndex;
                absMan >>= msbIndex;
                if ((uint)(rawExp - 1) < 254)
                {
                    uint raw = (uint)man & 0x80000000 | (uint)rawExp << MantissaBits | ((uint)absMan & 0x7FFFFF);
                    return new sfloat(raw);
                }
                else
                {
                    if (rawExp >= 255)
                    {
                        //Overflow
                        return man >= 0 ? PositiveInfinity : NegativeInfinity;
                    }

                    if (rawExp >= -24)
                    {
                        uint raw = (uint)man & 0x80000000 | (uint)(absMan >> (-rawExp + 1));
                        return new sfloat(raw);
                    }

                    return Zero;
                }
            }
            else
            {
                // Special

                if (rawExp2 != 255)
                {
                    // f1 is NaN, +Inf, -Inf and f2 is finite
                    return f1;
                }

                // Both not finite
                return f1.rawValue == f2.rawValue ? f1 : NaN;
            }
        }

        public static sfloat operator +(sfloat f1, sfloat f2)
        {
            return f1.RawExponent - f2.RawExponent >= 0 ? InternalAdd(f1, f2) : InternalAdd(f2, f1);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static sfloat operator -(sfloat f1, sfloat f2) => f1 + (-f2);

        public static sfloat operator *(sfloat f1, sfloat f2)
        {
            int man1;
            int rawExp1 = f1.RawExponent;
            uint sign1;
            uint sign2;
            if (rawExp1 == 0)
            {
                // SubNorm
                sign1 = (uint)((int)f1.rawValue >> 31);
                int rawMan1 = (int)f1.RawMantissa;
                if (rawMan1 == 0)
                {
                    if (f2.IsFinite())
                    {
                        // 0 * f2
                        return new sfloat((f1.rawValue ^ f2.rawValue) & SignMask);
                    }
                    else
                    {
                        // 0 * Infinity
                        // 0 * NaN
                        return NaN;
                    }
                }

                int shift = clz(rawMan1 & 0x00ffffff) - 8;
                rawMan1 <<= shift;
                rawExp1 = 1 - shift;

                //Debug.Assert(rawMan1 >> MantissaBits == 1);
                man1 = (int)((rawMan1 ^ sign1) - sign1);
            }
            else if (rawExp1 != 255)
            {
                // Norm
                sign1 = (uint)((int)f1.rawValue >> 31);
                man1 = (int)(((f1.RawMantissa | 0x800000) ^ sign1) - sign1);
            }
            else
            {
                // Non finite
                if (f1.rawValue == RawPositiveInfinity)
                {
                    if (f2.IsZero())
                    {
                        // Infinity * 0
                        return NaN;
                    }

                    if (f2.IsNaN())
                    {
                        // Infinity * NaN
                        return NaN;
                    }

                    if ((int)f2.rawValue >= 0)
                    {
                        // Infinity * f
                        return PositiveInfinity;
                    }
                    else
                    {
                        // Infinity * -f
                        return NegativeInfinity;
                    }
                }
                else if (f1.rawValue == RawNegativeInfinity)
                {
                    if (f2.IsZero() || f2.IsNaN())
                    {
                        // -Infinity * 0
                        // -Infinity * NaN
                        return NaN;
                    }

                    if ((int)f2.rawValue < 0)
                    {
                        // -Infinity * -f
                        return PositiveInfinity;
                    }
                    else
                    {
                        // -Infinity * f
                        return NegativeInfinity;
                    }
                }
                else
                {
                    return f1;
                }
            }

            int man2;
            int rawExp2 = f2.RawExponent;
            if (rawExp2 == 0)
            {
                // SubNorm
                sign2 = (uint)((int)f2.rawValue >> 31);
                int rawMan2 = (int)f2.RawMantissa;
                if (rawMan2 == 0)
                {
                    if (f1.IsFinite())
                    {
                        // f1 * 0
                        return new sfloat((f1.rawValue ^ f2.rawValue) & SignMask);
                    }
                    else
                    {
                        // Infinity * 0
                        // NaN * 0
                        return NaN;
                    }
                }

                int shift = clz(rawMan2 & 0x00ffffff) - 8;
                rawMan2 <<= shift;
                rawExp2 = 1 - shift;

                //Debug.Assert(rawMan2 >> MantissaBits == 1);
                man2 = (int)((rawMan2 ^ sign2) - sign2);
            }
            else if (rawExp2 != 255)
            {
                // Norm
                sign2 = (uint)((int)f2.rawValue >> 31);
                man2 = (int)(((f2.RawMantissa | 0x800000) ^ sign2) - sign2);
            }
            else
            {
                // Non finite
                if (f2.rawValue == RawPositiveInfinity)
                {
                    if (f1.IsZero())
                    {
                        // 0 * Infinity
                        return NaN;
                    }

                    if ((int)f1.rawValue >= 0)
                    {
                        // f * Infinity
                        return PositiveInfinity;
                    }
                    else
                    {
                        // -f * Infinity
                        return NegativeInfinity;
                    }
                }
                else if (f2.rawValue == RawNegativeInfinity)
                {
                    if (f1.IsZero())
                    {
                        // 0 * -Infinity
                        return NaN;
                    }

                    if ((int)f1.rawValue < 0)
                    {
                        // -f * -Infinity
                        return PositiveInfinity;
                    }
                    else
                    {
                        // f * -Infinity
                        return NegativeInfinity;
                    }
                }
                else
                {
                    return f2;
                }
            }

            long longMan = (long)man1 * (long)man2;
            int man = (int)(longMan >> MantissaBits);
            //Debug.Assert(man != 0);
            uint absMan = (uint)Math.Abs(man);
            int rawExp = rawExp1 + rawExp2 - ExponentBias;
            uint sign = (uint)man & 0x80000000;
            if ((absMan & 0x1000000) != 0)
            {
                absMan >>= 1;
                rawExp++;
            }

            //Debug.Assert(absMan >> MantissaBits == 1);
            if (rawExp >= 255)
            {
                // Overflow
                return new sfloat(sign ^ RawPositiveInfinity);
            }

            if (rawExp <= 0)
            {
                // Subnorms/Underflow
                if (rawExp <= -24)
                {
                    return new sfloat(sign);
                }

                absMan >>= -rawExp + 1;
                rawExp = 0;
            }

            uint raw = sign | (uint)rawExp << MantissaBits | absMan & 0x7FFFFF;
            return new sfloat(raw);
        }

        public static sfloat operator /(sfloat f1, sfloat f2)
        {
            if (f1.IsNaN() || f2.IsNaN())
            {
                return NaN;
            }

            int man1;
            int rawExp1 = f1.RawExponent;
            uint sign1;
            uint sign2;
            if (rawExp1 == 0)
            {
                // SubNorm
                sign1 = (uint)((int)f1.rawValue >> 31);
                int rawMan1 = (int)f1.RawMantissa;
                if (rawMan1 == 0)
                {
                    if (f2.IsZero())
                    {
                        // 0 / 0
                        return NaN;
                    }
                    else
                    {
                        // 0 / f
                        return new sfloat((f1.rawValue ^ f2.rawValue) & SignMask);
                    }
                }

                int shift = clz(rawMan1 & 0x00ffffff) - 8;
                rawMan1 <<= shift;
                rawExp1 = 1 - shift;

                //Debug.Assert(rawMan1 >> MantissaBits == 1);
                man1 = (int)((rawMan1 ^ sign1) - sign1);
            }
            else if (rawExp1 != 255)
            {
                // Norm
                sign1 = (uint)((int)f1.rawValue >> 31);
                man1 = (int)(((f1.RawMantissa | 0x800000) ^ sign1) - sign1);
            }
            else
            {
                // Non finite
                if (f1.rawValue == RawPositiveInfinity)
                {
                    if (f2.IsZero())
                    {
                        // Infinity / 0
                        return PositiveInfinity;
                    }

                    // +-Infinity / Infinity
                    return NaN;
                }
                else if (f1.rawValue == RawNegativeInfinity)
                {
                    if (f2.IsZero())
                    {
                        // -Infinity / 0
                        return NegativeInfinity;
                    }

                    // -Infinity / +-Infinity
                    return NaN;
                }
                else
                {
                    // NaN
                    return f1;
                }
            }

            int man2;
            int rawExp2 = f2.RawExponent;
            if (rawExp2 == 0)
            {
                // SubNorm
                sign2 = (uint)((int)f2.rawValue >> 31);
                int rawMan2 = (int)f2.RawMantissa;
                if (rawMan2 == 0)
                {
                    // f / 0
                    return new sfloat(((f1.rawValue ^ f2.rawValue) & SignMask) | RawPositiveInfinity);
                }

                int shift = clz(rawMan2 & 0x00ffffff) - 8;
                rawMan2 <<= shift;
                rawExp2 = 1 - shift;

                //Debug.Assert(rawMan2 >> MantissaBits == 1);
                man2 = (int)((rawMan2 ^ sign2) - sign2);
            }
            else if (rawExp2 != 255)
            {
                // Norm
                sign2 = (uint)((int)f2.rawValue >> 31);
                man2 = (int)(((f2.RawMantissa | 0x800000) ^ sign2) - sign2);
            }
            else
            {
                // Non finite
                if (f2.rawValue == RawPositiveInfinity)
                {
                    if (f1.IsZero())
                    {
                        // 0 / Infinity
                        return Zero;
                    }

                    if ((int)f1.rawValue >= 0)
                    {
                        // f / Infinity
                        return PositiveInfinity;
                    }
                    else
                    {
                        // -f / Infinity
                        return NegativeInfinity;
                    }
                }
                else if (f2.rawValue == RawNegativeInfinity)
                {
                    if (f1.IsZero())
                    {
                        // 0 / -Infinity
                        return new sfloat(SignMask);
                    }

                    if ((int)f1.rawValue < 0)
                    {
                        // -f / -Infinity
                        return PositiveInfinity;
                    }
                    else
                    {
                        // f / -Infinity
                        return NegativeInfinity;
                    }
                }
                else
                {
                    // NaN
                    return f2;
                }
            }

            long longMan = ((long)man1 << MantissaBits) / (long)man2;
            int man = (int)longMan;
            //Debug.Assert(man != 0);
            uint absMan = (uint)Math.Abs(man);
            int rawExp = rawExp1 - rawExp2 + ExponentBias;
            uint sign = (uint)man & 0x80000000;

            if ((absMan & 0x800000) == 0)
            {
                absMan <<= 1;
                --rawExp;
            }

            //Debug.Assert(absMan >> MantissaBits == 1);
            if (rawExp >= 255)
            {
                // Overflow
                return new sfloat(sign ^ RawPositiveInfinity);
            }

            if (rawExp <= 0)
            {
                // Subnorms/Underflow
                if (rawExp <= -24)
                {
                    return new sfloat(sign);
                }

                absMan >>= -rawExp + 1;
                rawExp = 0;
            }

            uint raw = sign | (uint)rawExp << MantissaBits | absMan & 0x7FFFFF;
            return new sfloat(raw);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static sfloat operator %(sfloat f1, sfloat f2) => libm.fmodf(f1, f2);

        private static readonly sbyte[] msb = new sbyte[256]
        {
            -1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
            5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
            6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
            6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
            7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
            7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
            7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
            7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
        };

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static int BitScanReverse8(int b) => msb[b];

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static unsafe uint ReinterpretFloatToInt32(float f) => *(uint*)&f;

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static unsafe float ReinterpretIntToFloat32(uint i) => *(float*)&i;

        public override bool Equals(object obj) => obj != null && GetType() == obj.GetType() && Equals((sfloat)obj);

        public bool Equals(sfloat other)
        {
            if (RawExponent != 255)
            {
                // 0 == -0
                return (rawValue == other.rawValue) || ((rawValue & 0x7FFFFFFF) == 0) && ((other.rawValue & 0x7FFFFFFF) == 0);
            }
            else
            {
                if (RawMantissa == 0)
                {
                    // Infinities
                    return rawValue == other.rawValue;
                }
                else
                {
                    // NaNs are equal for `Equals` (as opposed to the == operator)
                    return other.RawMantissa != 0;
                }
            }
        }

        public override int GetHashCode()
        {
            if (rawValue == SignMask)
            {
                // +0 equals -0
                return 0;
            }

            if (!IsNaN())
            {
                return (int)rawValue;
            }
            else
            {
                // All NaNs are equal
                return unchecked((int)RawNaN);
            }
        }

        public static bool operator ==(sfloat f1, sfloat f2)
        {
            if (f1.RawExponent != 255)
            {
                // 0 == -0
                return (f1.rawValue == f2.rawValue) || ((f1.rawValue & 0x7FFFFFFF) == 0) && ((f2.rawValue & 0x7FFFFFFF) == 0);
            }
            else
            {
                if (f1.RawMantissa == 0)
                {
                    // Infinities
                    return f1.rawValue == f2.rawValue;
                }
                else
                {
                    //NaNs
                    return false;
                }
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static bool operator !=(sfloat f1, sfloat f2) => !(f1 == f2);

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static bool operator <(sfloat f1, sfloat f2) => !f1.IsNaN() && !f2.IsNaN() && f1.CompareTo(f2) < 0;

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static bool operator >(sfloat f1, sfloat f2) => !f1.IsNaN() && !f2.IsNaN() && f1.CompareTo(f2) > 0;

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static bool operator <=(sfloat f1, sfloat f2) => !f1.IsNaN() && !f2.IsNaN() && f1.CompareTo(f2) <= 0;

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static bool operator >=(sfloat f1, sfloat f2) => !f1.IsNaN() && !f2.IsNaN() && f1.CompareTo(f2) >= 0;

        public int CompareTo(sfloat other)
        {
            if (IsNaN() && other.IsNaN())
            {
                return 0;
            }

            uint sign1 = (uint)((int)rawValue >> 31);
            int val1 = (int)(((rawValue) ^ (sign1 & 0x7FFFFFFF)) - sign1);

            uint sign2 = (uint)((int)other.rawValue >> 31);
            int val2 = (int)(((other.rawValue) ^ (sign2 & 0x7FFFFFFF)) - sign2);
            return val1.CompareTo(val2);
        }

        public int CompareTo(object obj) => obj is sfloat f ? CompareTo(f) : throw new ArgumentException("obj");

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool IsInfinity() => (rawValue & 0x7FFFFFFF) == 0x7F800000;

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool IsNegativeInfinity() => rawValue == RawNegativeInfinity;

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool IsPositiveInfinity() => rawValue == RawPositiveInfinity;

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool IsNaN() => (RawExponent == 255) && !IsInfinity();

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool IsFinite() => RawExponent != 255;

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool IsZero() => (rawValue & 0x7FFFFFFF) == 0;

        public string ToString(string format, IFormatProvider formatProvider) => ((float)this).ToString(format, formatProvider);
        public string ToString(string format) => ((float)this).ToString(format);
        public string ToString(IFormatProvider provider) => ((float)this).ToString(provider);
        public string ToStringInv() => ((float)this).ToString(System.Globalization.CultureInfo.InvariantCulture);

        /// <summary>
        /// Returns the absolute value of the given sfloat number
        /// </summary>
        public static sfloat Abs(sfloat f)
        {
            if (f.RawExponent != 255 || f.IsInfinity())
            {
                return new sfloat(f.rawValue & 0x7FFFFFFF);
            }
            else
            {
                // Leave NaN untouched
                return f;
            }
        }

        /// <summary>
        /// Returns the maximum of the two given sfloat values. Returns NaN iff either argument is NaN.
        /// </summary>
        public static sfloat Max(sfloat val1, sfloat val2)
        {
            if (val1 > val2)
            {
                return val1;
            }
            else if (val1.IsNaN())
            {
                return val1;
            }
            else
            {
                return val2;
            }
        }

        /// <summary>
        /// Returns the minimum of the two given sfloat values. Returns NaN iff either argument is NaN.
        /// </summary>
        public static sfloat Min(sfloat val1, sfloat val2)
        {
            if (val1 < val2)
            {
                return val1;
            }
            else if (val1.IsNaN())
            {
                return val1;
            }
            else
            {
                return val2;
            }
        }

        /// <summary>
        /// Returns true if the sfloat number has a positive sign.
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool IsPositive() => (rawValue & 0x80000000) == 0;

        /// <summary>
        /// Returns true if the sfloat number has a negative sign.
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool IsNegative() => (rawValue & 0x80000000) != 0;

        public int Sign()
        {
            if (IsNaN())
            {
                return 0;
            }

            if (IsZero())
            {
                return 0;
            }
            else if (IsPositive())
            {
                return 1;
            }
            else
            {
                return -1;
            }
        }
    }
}


================================================
FILE: SoftFloat.sln
================================================

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.31005.135
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SoftFloat", "SoftFloat\SoftFloat.csproj", "{4D20E349-8393-4AA3-B9A5-B2003DB0DBDA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SoftFloatTest", "SoftFloatTest\SoftFloatTest.csproj", "{5D8C30F8-2817-4F94-B3AD-EC17260B1D5A}"
EndProject
Global
	GlobalSection(SolutionConfigurationPlatforms) = preSolution
		Debug|Any CPU = Debug|Any CPU
		Release|Any CPU = Release|Any CPU
	EndGlobalSection
	GlobalSection(ProjectConfigurationPlatforms) = postSolution
		{4D20E349-8393-4AA3-B9A5-B2003DB0DBDA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{4D20E349-8393-4AA3-B9A5-B2003DB0DBDA}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{4D20E349-8393-4AA3-B9A5-B2003DB0DBDA}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{4D20E349-8393-4AA3-B9A5-B2003DB0DBDA}.Release|Any CPU.Build.0 = Release|Any CPU
		{5D8C30F8-2817-4F94-B3AD-EC17260B1D5A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{5D8C30F8-2817-4F94-B3AD-EC17260B1D5A}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{5D8C30F8-2817-4F94-B3AD-EC17260B1D5A}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{5D8C30F8-2817-4F94-B3AD-EC17260B1D5A}.Release|Any CPU.Build.0 = Release|Any CPU
	EndGlobalSection
	GlobalSection(SolutionProperties) = preSolution
		HideSolutionNode = FALSE
	EndGlobalSection
	GlobalSection(ExtensibilityGlobals) = postSolution
		SolutionGuid = {43647BBF-C10C-4ECB-86BF-773D9178E114}
	EndGlobalSection
EndGlobal


================================================
FILE: SoftFloatTest/.gitignore
================================================
/bin
/obj


================================================
FILE: SoftFloatTest/PCG.cs
================================================
using SoftFloat;

namespace SoftFloatTest
{
    // PCG random-number generator
    // see https://www.pcg-random.org/ for more info

    public struct PCG
    {
        private ulong state;
        private readonly ulong seed;

        private void Advance()
        {
            state = state * 6364136223846793005 + seed;
        }

        // [0, 4294967295]
        public uint Uint32()
        {
            ulong oldstate = state;
            Advance();
            // top 5 bits specify the rotation, leading to the following constants:
            // 64 - 5 = 59
            // 32 - 5 = 27
            // (32 + 5) / 2 = 18
            uint xorshifted = (uint)(((oldstate >> 18) ^ oldstate) >> 27);
            int rot = (int)(oldstate >> 59);
            return (xorshifted >> rot) | (xorshifted << (32 - rot));
        }

        // [-2147483648, 2147483647]
        public int Int32()
        {
            return (int)(((long)Uint32()) - int.MaxValue);
        }

        // [0, 2147483647]
        public int Int32Positive()
        {
            return (int)Uint32();
        }

        // [min, max]
        public uint Uint32RangeInclusive(uint minInclusive, uint maxInclusive)
        {
            return (Uint32() % (maxInclusive + 1 - minInclusive)) + minInclusive;
        }

        // [min, max]
        public int Int32RangeInclusive(int minInclusive, int maxInclusive)
        {
            return (Int32Positive() % (maxInclusive + 1 - minInclusive)) + minInclusive;
        }

        // [0, max)
        public uint Uint32(uint maxExclusive)
        {
            return Uint32() % maxExclusive;
        }

        // [min, max)
        public uint Uint32(uint minInclusive, uint maxExclusive)
        {
            return (Uint32() % (maxExclusive - minInclusive)) + minInclusive;
        }

        // [0, 18446744073709551615]
        public ulong Uint64()
        {
            return (((ulong)Uint32()) << 32) | Uint32();
        }

        // [0, max)
        public ulong Uint64(ulong maxExclusive)
        {
            return Uint64() % maxExclusive;
        }

        public PCG(ulong state, ulong seed)
        {
            this.seed = (seed << 1) | 1u;
            this.state = (this.seed + state) * 6364136223846793005 + this.seed;
            Advance();
            Advance();
        }

        public float FloatInclusive(float minInclusive, float maxInclusive)
        {
            float diff = maxInclusive - minInclusive;
            float rand = Uint32(16777216) / 16777215.0f;
            return diff * rand + minInclusive;
        }

        public float FloatExclusive(float minInclusive, float maxExclusive)
        {
            float diff = maxExclusive - minInclusive;
            float rand = Uint32(16777216) / 16777216.0f;
            return diff * rand + minInclusive;
        }

        public sfloat SFloatInclusive(sfloat minInclusive, sfloat maxInclusive)
        {
            sfloat diff = maxInclusive - minInclusive;
            sfloat rand = (sfloat)Uint32(16777216) / sfloat.FromRaw(0x4b7fffff);
            return diff * rand + minInclusive;
        }

        public sfloat SFloatExclusive(sfloat minInclusive, sfloat maxExclusive)
        {
            sfloat diff = maxExclusive - minInclusive;
            sfloat rand = (sfloat)Uint32(16777216) / sfloat.FromRaw(0x4b800000);
            return diff * rand + minInclusive;
        }
    }
}


================================================
FILE: SoftFloatTest/Program.cs
================================================
using System;

namespace SoftFloatTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Tests.RunAllTests();
        }
    }
}


================================================
FILE: SoftFloatTest/SoftFloatTest.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <ProjectReference Include="..\SoftFloat\SoftFloat.csproj" />
  </ItemGroup>

</Project>


================================================
FILE: SoftFloatTest/Tests.cs
================================================
using SoftFloat;
using System;
using System.Diagnostics;

namespace SoftFloatTest
{
    public static class Tests
    {
        const int RandomTestCount = 100_000;

        public static void RunAllTests()
        {
            TestAddition();
            TestSubtraction();
            TestMultiplication();
            TestDivision();

            RandomTestUnaryOperations();
            RandomTestBinaryOperations();
        }

        private static void RandomTestUnaryOperations()
        {
            RandomTestUnaryOperation(UnaryOperationType.Round);
            RandomTestUnaryOperation(UnaryOperationType.Floor);
            RandomTestUnaryOperation(UnaryOperationType.Ceiling);

            // trigonometry functions are implemented as an approximation, because the
            // libm implementation uses 64-bit double precision floats (which we cannot use here)
            // see https://github.com/rust-lang/libm/blob/master/src/math/sinf.rs for example
            RandomTestTrigonometryOperation(UnaryOperationType.Sine);
            RandomTestTrigonometryOperation(UnaryOperationType.Cosine);

            // tangent can produce inaccurate results near pi/2 + 2k*pi
            //RandomTestTrigonometryOperation(UnaryOperationType.Tangent);

            RandomTestUnaryOperation(UnaryOperationType.SquareRoot);

            // exponential is less accurate at higher values
            RandomTestUnaryOperation(UnaryOperationType.Exponential, 100.0f);

            RandomTestUnaryOperation(UnaryOperationType.LogarithmNatural);
            RandomTestUnaryOperation(UnaryOperationType.LogarithmBase2);

            RandomTestUnaryOperation(UnaryOperationType.ArcSine);
            RandomTestUnaryOperation(UnaryOperationType.ArcCosine);
            RandomTestUnaryOperation(UnaryOperationType.ArcTangent);
        }

        private static void RandomTestBinaryOperations()
        {
            RandomTestBinaryOperation(BinaryOperationType.Addition);
            RandomTestBinaryOperation(BinaryOperationType.Subtraction);
            RandomTestBinaryOperation(BinaryOperationType.Multiplication);
            RandomTestBinaryOperation(BinaryOperationType.Division);
            RandomTestBinaryOperation(BinaryOperationType.Modulus);

            RandomTestBinaryOperation(BinaryOperationType.Power);
            RandomTestBinaryOperation(BinaryOperationType.ArcTangent2);
        }


        private delegate sfloat BinaryOperation(sfloat a, sfloat b);

        private enum BinaryOperationType : int
        {
            Addition, Subtraction, Multiplication, Division, Modulus, Power, ArcTangent2
        }

        private static readonly BinaryOperation[] binaryOperations = new BinaryOperation[]
        {
            (a, b) => a + b,
            (a, b) => a - b,
            (a, b) => a * b,
            (a, b) => a / b,
            (a, b) => a % b,
            libm.powf,
            libm.atan2f
        };

        private static void TestBinaryOperationFloatExact(float a, float b, float expected, BinaryOperationType op)
        {
            BinaryOperation func = binaryOperations[(int)op];
            sfloat result = func((sfloat)a, (sfloat)b);
            bool isOk = result.Equals((sfloat)expected);
            Debug.Assert(isOk);
        }

        private static void TestBinaryOperationFloatApproximate(float a, float b, float expected, BinaryOperationType op)
        {
            BinaryOperation func = binaryOperations[(int)op];
            sfloat result = func((sfloat)a, (sfloat)b);

            if (float.IsNaN(expected) && result.IsNaN())
            {
                // special case, NaN-s cannot be compared
                return;
            }

            if (float.IsInfinity(expected) && result.IsInfinity() && MathF.Sign(expected) == result.Sign())
            {
                // both are the same infinities
                return;
            }

            float allowedError = MathF.Max(1e-6f * MathF.Pow(2.0f, MathF.Log2(MathF.Abs(expected) + 1.0f)), 1e-6f);
            float difference = MathF.Abs((float)result - expected);
            bool isOk = difference < allowedError;
            Debug.Assert(isOk);
        }


        private enum UnaryOperationType : int
        {
            Round, Floor, Ceiling, Sine, Cosine, Tangent, SquareRoot, Exponential, LogarithmNatural, LogarithmBase2,
            ArcSine, ArcCosine, ArcTangent
        }

        private delegate sfloat UnaryOperation(sfloat x);

        private static readonly UnaryOperation[] unaryOperations = new UnaryOperation[]
        {
            libm.roundf,
            libm.floorf,
            libm.ceilf,
            libm.sinf,
            libm.cosf,
            libm.tanf,
            libm.sqrtf,
            libm.expf,
            libm.logf,
            libm.log2f,
            libm.asinf,
            libm.acosf,
            libm.atanf
        };

        private static void TestUnaryOperationFloatExact(float x, float expected, UnaryOperationType op)
        {
            UnaryOperation func = unaryOperations[(int)op];
            sfloat result = func((sfloat)x);
            bool isOk = result.Equals((sfloat)expected);
            Debug.Assert(isOk);
        }

        private static void TestUnaryOperationFloatApproximate(float x, float expected, UnaryOperationType op, float allowedErrorMultiplier = 1.0f)
        {
            UnaryOperation func = unaryOperations[(int)op];
            sfloat result = func((sfloat)x);

            if (float.IsNaN(expected) && result.IsNaN())
            {
                // special case, NaN-s cannot be compared
                return;
            }

            if (float.IsInfinity(expected) && result.IsInfinity() && MathF.Sign(expected) == result.Sign())
            {
                // both are the same infinities
                return;
            }

            float allowedError = MathF.Max(1e-6f * allowedErrorMultiplier * MathF.Pow(2.0f, MathF.Log2(MathF.Abs(expected) + 1.0f)), 1e-6f);

            float difference = MathF.Abs((float)result - expected);
            bool isOk = difference <= allowedError;
            if (!isOk && (op == UnaryOperationType.Round || op == UnaryOperationType.Floor || op == UnaryOperationType.Ceiling))
            {
                // Because of the loss of precision that can result from representing decimal values
                // as floating-point numbers or performing arithmetic operations on floating-point values,
                // in some cases the Round method may not appear to round midpoint values to the nearest even integer.
                // https://docs.microsoft.com/en-us/dotnet/api/system.math.round

                if (MathF.Abs(x % 1.0f) - 0.5f < 0.01f)
                {
                    // x is near a midpoint, it's possible that rounding happened in a different direction
                    isOk = MathF.Abs((float)result - expected) <= 1.0f;
                }
            }

            Debug.Assert(isOk);
        }

        private static void TestTrigonometryOperationApproximate(float x, float expected, UnaryOperationType op)
        {
            UnaryOperation func = unaryOperations[(int)op];
            sfloat result = func((sfloat)x);

            if (float.IsNaN(expected) && result.IsNaN())
            {
                // special case, NaN-s cannot be compared
                return;
            }

            if (float.IsInfinity(expected) && result.IsInfinity() && MathF.Sign(expected) == result.Sign())
            {
                // both are the same infinities
                return;
            }

            float allowedError = MathF.Max(0.005f * MathF.Pow(2.0f, MathF.Log2(MathF.Abs(expected) + 1.0f)), 1e-6f);

            float difference = MathF.Abs((float)result - expected);
            bool isOk = difference <= allowedError;

            Debug.Assert(isOk);
        }


        private static void RandomTestBinaryOperation(BinaryOperationType op)
        {
            Func<float, float, float> func = op switch
            {
                BinaryOperationType.Addition => (float a, float b) => a + b,
                BinaryOperationType.Subtraction => (float a, float b) => a - b,
                BinaryOperationType.Multiplication => (float a, float b) => a * b,
                BinaryOperationType.Division => (float a, float b) => a / b,
                BinaryOperationType.Modulus => (float a, float b) => a % b,
                BinaryOperationType.Power => MathF.Pow,
                BinaryOperationType.ArcTangent2 => MathF.Atan2,
                _ => throw new ArgumentException(),
            };

            PCG rand = new PCG(0, 0);

            // very small values
            for (int i = 0; i < RandomTestCount; ++i)
            {
                float a = rand.FloatInclusive(-1e-10f, 1e-10f);
                float b = rand.FloatInclusive(-1e-10f, 1e-10f);
                TestBinaryOperationFloatApproximate(a, b, func(a, b), op);
            }

            // small values
            for (int i = 0; i < RandomTestCount; ++i)
            {
                float a = rand.FloatInclusive(-1.0f, 1.0f);
                float b = rand.FloatInclusive(-1.0f, 1.0f);
                TestBinaryOperationFloatApproximate(a, b, func(a, b), op);
            }

            // large values
            for (int i = 0; i < RandomTestCount; ++i)
            {
                float a = rand.FloatInclusive(-100000.0f, 100000.0f);
                float b = rand.FloatInclusive(-100000.0f, 100000.0f);
                TestBinaryOperationFloatApproximate(a, b, func(a, b), op);
            }

            // huge values
            for (int i = 0; i < RandomTestCount; ++i)
            {
                float a = rand.FloatInclusive(-1000000000.0f, 1000000000.0f);
                float b = rand.FloatInclusive(-1000000000.0f, 1000000000.0f);
                TestBinaryOperationFloatApproximate(a, b, func(a, b), op);
            }

            // gigantic values
            for (int i = 0; i < RandomTestCount; ++i)
            {
                float a = rand.FloatInclusive(-1e38f, 1e38f);
                float b = rand.FloatInclusive(-1e38f, 1e38f);
                TestBinaryOperationFloatApproximate(a, b, func(a, b), op);
            }
        }

        private static void RandomTestUnaryOperation(UnaryOperationType op, float allowedErrorMultiplier = 1.0f)
        {
            Func<float, float> func = op switch
            {
                UnaryOperationType.Round => MathF.Round,
                UnaryOperationType.Floor => MathF.Floor,
                UnaryOperationType.Ceiling => MathF.Ceiling,
                UnaryOperationType.Sine => MathF.Sin,
                UnaryOperationType.Cosine => MathF.Cos,
                UnaryOperationType.Tangent => MathF.Tan,
                UnaryOperationType.SquareRoot => MathF.Sqrt,
                UnaryOperationType.Exponential => MathF.Exp,
                UnaryOperationType.LogarithmNatural => MathF.Log,
                UnaryOperationType.LogarithmBase2 => MathF.Log2,
                UnaryOperationType.ArcSine => MathF.Asin,
                UnaryOperationType.ArcCosine => MathF.Acos,
                UnaryOperationType.ArcTangent => MathF.Atan,
                _ => throw new ArgumentException(),
            };

            PCG rand = new PCG(0, 0);

            // very small values
            for (int i = 0; i < RandomTestCount; ++i)
            {
                float x = rand.FloatInclusive(-1e-40f, 1e-40f);
                TestUnaryOperationFloatApproximate(x, func(x), op, allowedErrorMultiplier);
            }

            // small values
            for (int i = 0; i < RandomTestCount; ++i)
            {
                float x = rand.FloatInclusive(-1.0f, 1.0f);
                TestUnaryOperationFloatApproximate(x, func(x), op, allowedErrorMultiplier);
            }

            // large values
            for (int i = 0; i < RandomTestCount; ++i)
            {
                float x = rand.FloatInclusive(-100000.0f, 100000.0f);
                TestUnaryOperationFloatApproximate(x, func(x), op, allowedErrorMultiplier);
            }

            // huge values
            for (int i = 0; i < RandomTestCount; ++i)
            {
                float x = rand.FloatInclusive(-1000000000.0f, 1000000000.0f);
                TestUnaryOperationFloatApproximate(x, func(x), op, allowedErrorMultiplier);
            }
        }

        private static void RandomTestTrigonometryOperation(UnaryOperationType op)
        {
            Func<float, float> func = op switch
            {
                UnaryOperationType.Sine => MathF.Sin,
                UnaryOperationType.Cosine => MathF.Cos,
                UnaryOperationType.Tangent => MathF.Tan,
                _ => throw new ArgumentException(),
            };

            PCG rand = new PCG(0, 0);

            // small values
            for (int i = 0; i < RandomTestCount; ++i)
            {
                float x = rand.FloatInclusive(-1.0f, 1.0f);
                TestTrigonometryOperationApproximate(x, func(x), op);
            }

            // medium values
            for (int i = 0; i < RandomTestCount; ++i)
            {
                float x = rand.FloatInclusive(-100.0f, 100.0f);
                TestTrigonometryOperationApproximate(x, func(x), op);
            }
        }

        public static void TestAddition()
        {
            const BinaryOperationType op = BinaryOperationType.Addition;

            TestBinaryOperationFloatExact(0.0f, 0.0f, 0.0f, op);
            TestBinaryOperationFloatExact(1.0f, 0.0f, 1.0f, op);
            TestBinaryOperationFloatExact(0.0f, 1.0f, 1.0f, op);

            TestBinaryOperationFloatExact(-0.0f, 0.0f, 0.0f, op);
            TestBinaryOperationFloatExact(-0.0f, 0.0f, -0.0f, op);
            TestBinaryOperationFloatExact(0.0f, 0.0f, -0.0f, op);

            TestBinaryOperationFloatExact(1.0f, -1.0f, 0.0f, op);
            TestBinaryOperationFloatExact(-1.0f, -1.0f, -2.0f, op);

            TestBinaryOperationFloatApproximate(123.456f, 456.789f, 580.245f, op);

            TestBinaryOperationFloatExact(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity, op);
            TestBinaryOperationFloatExact(float.PositiveInfinity, float.NegativeInfinity, float.NaN, op);

            TestBinaryOperationFloatExact(float.NaN, float.NaN, float.NaN, op);
            TestBinaryOperationFloatExact(0.0f, float.NaN, float.NaN, op);
            TestBinaryOperationFloatExact(-999999.0f, float.NaN, float.NaN, op);

            RandomTestBinaryOperation(op);
        }


        public static void TestSubtraction()
        {
            const BinaryOperationType op = BinaryOperationType.Subtraction;

            TestBinaryOperationFloatExact(0.0f, 0.0f, 0.0f, op);
            TestBinaryOperationFloatExact(1.0f, 0.0f, 1.0f, op);
            TestBinaryOperationFloatExact(0.0f, 1.0f, -1.0f, op);

            TestBinaryOperationFloatExact(-0.0f, 0.0f, 0.0f, op);
            TestBinaryOperationFloatExact(-0.0f, 0.0f, -0.0f, op);
            TestBinaryOperationFloatExact(0.0f, 0.0f, -0.0f, op);

            TestBinaryOperationFloatExact(1.0f, -1.0f, 2.0f, op);
            TestBinaryOperationFloatExact(-1.0f, -1.0f, 0.0f, op);

            TestBinaryOperationFloatApproximate(123.456f, 456.789f, -333.333f, op);

            TestBinaryOperationFloatExact(float.PositiveInfinity, float.PositiveInfinity, float.NaN, op);
            TestBinaryOperationFloatExact(float.PositiveInfinity, float.NegativeInfinity, float.PositiveInfinity, op);

            TestBinaryOperationFloatExact(float.NaN, float.NaN, float.NaN, op);
            TestBinaryOperationFloatExact(0.0f, float.NaN, float.NaN, op);
            TestBinaryOperationFloatExact(-999999.0f, float.NaN, float.NaN, op);

            RandomTestBinaryOperation(op);
        }

        public static void TestMultiplication()
        {
            const BinaryOperationType op = BinaryOperationType.Multiplication;

            TestBinaryOperationFloatExact(0.0f, 0.0f, 0.0f, op);
            TestBinaryOperationFloatExact(1.0f, 0.0f, 0.0f, op);
            TestBinaryOperationFloatExact(0.0f, 1.0f, 0.0f, op);

            TestBinaryOperationFloatExact(-0.0f, 0.0f, 0.0f, op);
            TestBinaryOperationFloatExact(-0.0f, 0.0f, -0.0f, op);
            TestBinaryOperationFloatExact(0.0f, 0.0f, -0.0f, op);

            TestBinaryOperationFloatExact(1.0f, -1.0f, -1.0f, op);
            TestBinaryOperationFloatExact(-1.0f, -1.0f, 1.0f, op);

            TestBinaryOperationFloatApproximate(123.456f, 456.789f, 56393.34f, op);
            TestBinaryOperationFloatApproximate(1e-40f, 1e-42f, 0.0f, op);

            TestBinaryOperationFloatExact(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity, op);
            TestBinaryOperationFloatExact(float.PositiveInfinity, float.NegativeInfinity, float.NegativeInfinity, op);
            TestBinaryOperationFloatExact(float.NegativeInfinity, float.NegativeInfinity, float.PositiveInfinity, op);
            TestBinaryOperationFloatExact(float.NaN, float.PositiveInfinity, float.NaN, op);
            TestBinaryOperationFloatExact(0.0f, float.PositiveInfinity, float.NaN, op);

            TestBinaryOperationFloatExact(float.NaN, float.NaN, float.NaN, op);
            TestBinaryOperationFloatExact(0.0f, float.NaN, float.NaN, op);
            TestBinaryOperationFloatExact(-999999.0f, float.NaN, float.NaN, op);

            RandomTestBinaryOperation(op);
        }

        public static void TestDivision()
        {
            const BinaryOperationType op = BinaryOperationType.Division;

            TestBinaryOperationFloatExact(0.0f, 0.0f, float.NaN, op);
            TestBinaryOperationFloatExact(1.0f, 0.0f, float.PositiveInfinity, op);
            TestBinaryOperationFloatExact(0.0f, 1.0f, 0.0f, op);

            TestBinaryOperationFloatExact(-0.0f, 0.0f, float.NaN, op);
            TestBinaryOperationFloatExact(-0.0f, 0.0f, float.NaN, op);
            TestBinaryOperationFloatExact(0.0f, 0.0f, float.NaN, op);

            TestBinaryOperationFloatExact(1.0f, -1.0f, -1.0f, op);
            TestBinaryOperationFloatExact(-1.0f, -1.0f, 1.0f, op);

            TestBinaryOperationFloatApproximate(123.456f, 456.789f, 0.2702692f, op);
            TestBinaryOperationFloatApproximate(1e-40f, 1e-42f, 99.94678f, op);

            TestBinaryOperationFloatExact(float.PositiveInfinity, float.PositiveInfinity, float.NaN, op);
            TestBinaryOperationFloatExact(float.PositiveInfinity, float.NegativeInfinity, float.NaN, op);
            TestBinaryOperationFloatExact(float.NegativeInfinity, float.NegativeInfinity, float.NaN, op);
            TestBinaryOperationFloatExact(float.NaN, float.PositiveInfinity, float.NaN, op);
            TestBinaryOperationFloatExact(0.0f, float.PositiveInfinity, 0.0f, op);
            TestBinaryOperationFloatExact(float.PositiveInfinity, 0.0f, float.PositiveInfinity, op);

            TestBinaryOperationFloatExact(float.NaN, float.NaN, float.NaN, op);
            TestBinaryOperationFloatExact(0.0f, float.NaN, float.NaN, op);
            TestBinaryOperationFloatExact(-999999.0f, float.NaN, float.NaN, op);

            RandomTestBinaryOperation(op);
        }
    }
}
Download .txt
gitextract_fgkxyhc3/

├── .gitignore
├── LICENSE
├── README.md
├── SoftFloat/
│   ├── .gitignore
│   ├── SoftFloat.csproj
│   ├── libm/
│   │   ├── Arithmetic.cs
│   │   ├── LICENSE.txt
│   │   ├── Transcendental.cs
│   │   └── Trigonometry.cs
│   └── sfloat.cs
├── SoftFloat.sln
└── SoftFloatTest/
    ├── .gitignore
    ├── PCG.cs
    ├── Program.cs
    ├── SoftFloatTest.csproj
    └── Tests.cs
Download .txt
SYMBOL INDEX (67 symbols across 7 files)

FILE: SoftFloat/libm/Arithmetic.cs
  class libm (line 4) | public static partial class libm
    method remquof (line 9) | public static void remquof(sfloat x, sfloat y, out sfloat remainder, o...
    method remainderf (line 149) | public static sfloat remainderf(sfloat x, sfloat y)
    method fmodf (line 158) | public static sfloat fmodf(sfloat x, sfloat y)
    method roundf (line 274) | public static sfloat roundf(sfloat x)
    method floorf (line 320) | public static sfloat floorf(sfloat x)
    method ceilf (line 361) | public static sfloat ceilf(sfloat x)
    method truncf (line 402) | public static sfloat truncf(sfloat x)
    method sqrtf (line 431) | public static sfloat sqrtf(sfloat x)

FILE: SoftFloat/libm/Transcendental.cs
  class libm (line 4) | public static partial class libm
    method scalbnf (line 6) | private static sfloat scalbnf(sfloat x, int n)
    method expf (line 47) | public static sfloat expf(sfloat x)
    method logf (line 140) | public static sfloat logf(sfloat x)
    method log2f (line 206) | public static sfloat log2f(sfloat x)
    method powf (line 292) | public static sfloat powf(sfloat x, sfloat y)

FILE: SoftFloat/libm/Trigonometry.cs
  class libm (line 4) | public static partial class libm
    method sinf (line 15) | public static sfloat sinf(sfloat x)
    method cosf (line 50) | public static sfloat cosf(sfloat x) => sinf(x + sfloat.FromRaw(half_pi));
    method tanf (line 55) | public static sfloat tanf(sfloat x) => sinf(x) / cosf(x);
    method hypotf (line 60) | public static sfloat hypotf(sfloat x, sfloat y)
    method atanf (line 199) | public unsafe static sfloat atanf(sfloat x)
    method atan2f (line 288) | public static sfloat atan2f(sfloat y, sfloat x)
    method acosf (line 395) | public static sfloat acosf(sfloat x)
    method asinf (line 470) | public static sfloat asinf(sfloat x) => sfloat.FromRaw(half_pi) - acos...

FILE: SoftFloat/sfloat.cs
  method sfloat (line 40) | [MethodImpl(MethodImplOptions.AggressiveInlining)]
  method FromRaw (line 49) | [MethodImpl(MethodImplOptions.AggressiveInlining)]
  method ToString (line 106) | public override string ToString() => ((float)this).ToString();
  method FromParts (line 115) | public static sfloat FromParts(bool sign, uint exponent, uint mantissa)
  method clz (line 206) | private static int clz(int x)
  method InternalAdd (line 225) | private static sfloat InternalAdd(sfloat f1, sfloat f2)
  method BitScanReverse8 (line 740) | [MethodImpl(MethodImplOptions.AggressiveInlining)]

FILE: SoftFloatTest/PCG.cs
  type PCG (line 8) | public struct PCG
    method Advance (line 13) | private void Advance()
    method Uint32 (line 19) | public uint Uint32()
    method Int32 (line 33) | public int Int32()
    method Int32Positive (line 39) | public int Int32Positive()
    method Uint32RangeInclusive (line 45) | public uint Uint32RangeInclusive(uint minInclusive, uint maxInclusive)
    method Int32RangeInclusive (line 51) | public int Int32RangeInclusive(int minInclusive, int maxInclusive)
    method Uint32 (line 57) | public uint Uint32(uint maxExclusive)
    method Uint32 (line 63) | public uint Uint32(uint minInclusive, uint maxExclusive)
    method Uint64 (line 69) | public ulong Uint64()
    method Uint64 (line 75) | public ulong Uint64(ulong maxExclusive)
    method PCG (line 80) | public PCG(ulong state, ulong seed)
    method FloatInclusive (line 88) | public float FloatInclusive(float minInclusive, float maxInclusive)
    method FloatExclusive (line 95) | public float FloatExclusive(float minInclusive, float maxExclusive)
    method SFloatInclusive (line 102) | public sfloat SFloatInclusive(sfloat minInclusive, sfloat maxInclusive)
    method SFloatExclusive (line 109) | public sfloat SFloatExclusive(sfloat minInclusive, sfloat maxExclusive)

FILE: SoftFloatTest/Program.cs
  class Program (line 5) | class Program
    method Main (line 7) | static void Main(string[] args)

FILE: SoftFloatTest/Tests.cs
  class Tests (line 7) | public static class Tests
    method RunAllTests (line 11) | public static void RunAllTests()
    method RandomTestUnaryOperations (line 22) | private static void RandomTestUnaryOperations()
    method RandomTestBinaryOperations (line 50) | private static void RandomTestBinaryOperations()
    type BinaryOperationType (line 65) | private enum BinaryOperationType : int
    method TestBinaryOperationFloatExact (line 81) | private static void TestBinaryOperationFloatExact(float a, float b, fl...
    method TestBinaryOperationFloatApproximate (line 89) | private static void TestBinaryOperationFloatApproximate(float a, float...
    type UnaryOperationType (line 113) | private enum UnaryOperationType : int
    method TestUnaryOperationFloatExact (line 138) | private static void TestUnaryOperationFloatExact(float x, float expect...
    method TestUnaryOperationFloatApproximate (line 146) | private static void TestUnaryOperationFloatApproximate(float x, float ...
    method TestTrigonometryOperationApproximate (line 184) | private static void TestTrigonometryOperationApproximate(float x, floa...
    method RandomTestBinaryOperation (line 210) | private static void RandomTestBinaryOperation(BinaryOperationType op)
    method RandomTestUnaryOperation (line 267) | private static void RandomTestUnaryOperation(UnaryOperationType op, fl...
    method RandomTestTrigonometryOperation (line 318) | private static void RandomTestTrigonometryOperation(UnaryOperationType...
    method TestAddition (line 345) | public static void TestAddition()
    method TestSubtraction (line 373) | public static void TestSubtraction()
    method TestMultiplication (line 400) | public static void TestMultiplication()
    method TestDivision (line 431) | public static void TestDivision()
Condensed preview — 16 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (117K chars).
[
  {
    "path": ".gitignore",
    "chars": 5,
    "preview": ".vs/\n"
  },
  {
    "path": "LICENSE",
    "chars": 1064,
    "preview": "MIT License\n\nCopyright (c) 2021 Kimbatt\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof"
  },
  {
    "path": "README.md",
    "chars": 2311,
    "preview": "# Soft float starter pack\nSoftware implementation of floating point numbers and operations.  \nSoft floats can be used fo"
  },
  {
    "path": "SoftFloat/.gitignore",
    "chars": 10,
    "preview": "/bin\n/obj\n"
  },
  {
    "path": "SoftFloat/SoftFloat.csproj",
    "chars": 319,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <OutputType>Library</OutputType>\n    <TargetFramework>netcoreap"
  },
  {
    "path": "SoftFloat/libm/Arithmetic.cs",
    "chars": 12618,
    "preview": "\nnamespace SoftFloat\n{\n    public static partial class libm\n    {\n        /// <summary>\n        /// Returns the remaind"
  },
  {
    "path": "SoftFloat/libm/LICENSE.txt",
    "chars": 1058,
    "preview": "Copyright (c) 2018 Jorge Aparicio\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this "
  },
  {
    "path": "SoftFloat/libm/Transcendental.cs",
    "chars": 23689,
    "preview": "\nnamespace SoftFloat\n{\n    public static partial class libm\n    {\n        private static sfloat scalbnf(sfloat x, int n"
  },
  {
    "path": "SoftFloat/libm/Trigonometry.cs",
    "chars": 15277,
    "preview": "\nnamespace SoftFloat\n{\n    public static partial class libm\n    {\n        const uint pi = 0x40490fdb; // 3.141592653589"
  },
  {
    "path": "SoftFloat/sfloat.cs",
    "chars": 31935,
    "preview": "\n// mostly from https://github.com/CodesInChaos/SoftFloat\n\n// Copyright (c) 2011 CodesInChaos\n//\n// Permission is hereb"
  },
  {
    "path": "SoftFloat.sln",
    "chars": 1593,
    "preview": "\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio Version 16\nVisualStudioVersion = 16.0.3100"
  },
  {
    "path": "SoftFloatTest/.gitignore",
    "chars": 10,
    "preview": "/bin\n/obj\n"
  },
  {
    "path": "SoftFloatTest/PCG.cs",
    "chars": 3418,
    "preview": "using SoftFloat;\n\nnamespace SoftFloatTest\n{\n    // PCG random-number generator\n    // see https://www.pcg-random.org/ f"
  },
  {
    "path": "SoftFloatTest/Program.cs",
    "chars": 167,
    "preview": "using System;\n\nnamespace SoftFloatTest\n{\n    class Program\n    {\n        static void Main(string[] args)\n        {\n    "
  },
  {
    "path": "SoftFloatTest/SoftFloatTest.csproj",
    "chars": 265,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <OutputType>Exe</OutputType>\n    <TargetFramework>netcoreapp3.1"
  },
  {
    "path": "SoftFloatTest/Tests.cs",
    "chars": 19306,
    "preview": "using SoftFloat;\nusing System;\nusing System.Diagnostics;\n\nnamespace SoftFloatTest\n{\n    public static class Tests\n    {\n"
  }
]

About this extraction

This page contains the full source code of the Kimbatt/soft-float-starter-pack GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 16 files (110.4 KB), approximately 30.5k tokens, and a symbol index with 67 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!