예제 #1
0
        // This function is not well-tested. It may be wildly inaccurate.
        /// <summary>
        /// Returns the tangent of the specified angle.
        /// </summary>
        /// <param name="x">An angle, measured in radians.</param>
        /// <returns></returns>
        public static Fix64 Tan(Fix64 x)
        {
            var clampedPi = x.RawValue % _PI;
            var flip      = false;

            if (clampedPi < 0)
            {
                clampedPi = -clampedPi;
                flip      = true;
            }
            if (clampedPi > PI_OVER_2)
            {
                flip      = !flip;
                clampedPi = PI_OVER_2 - (clampedPi - PI_OVER_2);
            }

            var clamped = new Fix64(clampedPi);

            // Find the two closest values in the LUT and perform linear interpolation
            var rawIndex     = Fix64.FastMultiply(clamped, LutInterval);
            var roundedIndex = Round(rawIndex);
            var indexError   = Fix64.FastSubtract(rawIndex, roundedIndex);

            var nearestValue       = new Fix64(TanLut[(int)roundedIndex]);
            var secondNearestValue = new Fix64(TanLut[(int)roundedIndex + Sign(indexError)]);

            var delta             = Fix64.FastMultiply(indexError, FastAbs(Fix64.FastSubtract(nearestValue, secondNearestValue))).RawValue;
            var interpolatedValue = nearestValue.RawValue + delta;
            var finalValue        = flip ? -interpolatedValue : interpolatedValue;

            return(new Fix64(finalValue));
        }
예제 #2
0
        // Provides at least 6 decimals of accuracy.
        /// <summary>
        /// Returns 2 raised to the specified power.
        /// </summary>
        /// <param name="x">A number specifying a power.</param>
        /// <returns></returns>
        public static Fix64 Pow2(Fix64 x)
        {
            if (x.RawValue == 0)
            {
                return(Fix64.One);
            }

            // Avoid negative arguments by exploiting that exp(-x) = 1/exp(x).
            bool neg = x.RawValue < 0;

            if (neg)
            {
                x = -x;
            }

            if (x == Fix64.One)
            {
                return(neg ? Fix64.One / (Fix64)2 : (Fix64)2);
            }
            if (x >= Log2Max)
            {
                return(neg ? Fix64.One / Fix64.MaxValue : Fix64.MaxValue);
            }
            if (x <= Log2Min)
            {
                return(neg ? Fix64.MaxValue : Fix64.Zero);
            }

            /* The algorithm is based on the power series for exp(x):
             * http://en.wikipedia.org/wiki/Exponential_function#Formal_definition
             *
             * From term n, we get term n+1 by multiplying with x/n.
             * When the sum term drops to zero, we can stop summing.
             */

            int integerPart = (int)Floor(x);

            // Take fractional part of exponent
            x = new Fix64(x.RawValue & 0x00000000FFFFFFFF);

            var result = Fix64.One;
            var term   = Fix64.One;
            int i      = 1;

            while (term.RawValue != 0)
            {
                term    = Fix64.FastMultiply(Fix64.FastMultiply(x, term), E) / (Fix64)i;
                result += term;
                i++;
            }

            result = Fix64.FromRaw(result.RawValue << integerPart);
            if (neg)
            {
                result = Fix64.One / result;
            }

            return(result);
        }
예제 #3
0
        // Provides at least 9 decimals of accuracy.
        /// <summary>
        /// Returns the base 2 logarithm of a specified number.
        /// </summary>
        /// <param name="x">A number whose logarithm is to be found.</param>
        /// <returns></returns>
        public static Fix64 Log2(Fix64 x)
        {
            if (x.RawValue <= 0)
            {
                throw new ArgumentOutOfRangeException("Non-positive value passed to Ln", "x");
            }

            // This implementation is based on Clay. S. Turner's fast binary logarithm
            // algorithm (C. S. Turner,  "A Fast Binary Logarithm Algorithm", IEEE Signal
            //     Processing Mag., pp. 124,140, Sep. 2010.)

            long b = 1U << (Fix64.FRACTIONAL_PLACES - 1);
            long y = 0;

            long rawX = x.RawValue;

            while (rawX < Fix64.ONE)
            {
                rawX <<= 1;
                y     -= Fix64.ONE;
            }

            while (rawX >= (Fix64.ONE << 1))
            {
                rawX >>= 1;
                y     += Fix64.ONE;
            }

            var z = new Fix64(rawX);

            for (int i = 0; i < Fix64.FRACTIONAL_PLACES; i++)
            {
                z = Fix64.FastMultiply(z, z);
                if (z.RawValue >= (Fix64.ONE << 1))
                {
                    z  = new Fix64(z.RawValue >> 1);
                    y += b;
                }
                b >>= 1;
            }

            return(new Fix64(y));
        }
예제 #4
0
        // The relative error is less than 1E-10 for x in [-2PI, 2PI], and less than 1E-7 in the worst case.
        /// <summary>
        /// Returns the sine of the specified angle.
        /// </summary>
        /// <param name="x">An angle, measured in radians.</param>
        /// <returns></returns>
        public static Fix64 Sin(Fix64 x)
        {
            var clampedL = ClampSinValue(x.RawValue, out var flipHorizontal, out var flipVertical);
            var clamped  = new Fix64(clampedL);

            // Find the two closest values in the LUT and perform linear interpolation
            // This is what kills the performance of this function on x86 - x64 is fine though
            var rawIndex     = Fix64.FastMultiply(clamped, LutInterval);
            var roundedIndex = Round(rawIndex);
            var indexError   = Fix64.FastSubtract(rawIndex, roundedIndex);

            var nearestValue = new Fix64(SinLut[flipHorizontal ?
                                                SinLut.Length - 1 - (int)roundedIndex :
                                                (int)roundedIndex]);
            var secondNearestValue = new Fix64(SinLut[flipHorizontal ?
                                                      SinLut.Length - 1 - (int)roundedIndex - Sign(indexError) :
                                                      (int)roundedIndex + Sign(indexError)]);

            var delta             = Fix64.FastMultiply(indexError, FastAbs(Fix64.FastSubtract(nearestValue, secondNearestValue))).RawValue;
            var interpolatedValue = nearestValue.RawValue + (flipHorizontal ? -delta : delta);
            var finalValue        = flipVertical ? -interpolatedValue : interpolatedValue;

            return(new Fix64(finalValue));
        }
예제 #5
0
 // Provides at least 7 decimals of accuracy.
 /// <summary>
 /// Returns the natural (base e) logarithm of a specified number.
 /// </summary>
 /// <param name="x">The number whose logarithm is to be found.</param>
 /// <returns></returns>
 public static Fix64 Log(Fix64 x)
 {
     return(Fix64.FastMultiply(Log2(x), E));
 }