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 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);
        }
Beispiel #3
0
    /// <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(-,-) */
        }
    }
Beispiel #4
0
    /// <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);
    }
Beispiel #5
0
    /// <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;
    }
Beispiel #6
0
    /// <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));
    }
Beispiel #7
0
    /// <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));
    }