< Summary

Line coverage
99%
Covered lines: 408
Uncovered lines: 1
Coverable lines: 409
Total lines: 915
Line coverage: 99.7%
Branch coverage
100%
Covered branches: 206
Total branches: 206
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
File 1: .cctor()100%11100%
File 1: CopySign(...)100%22100%
File 1: Clamp01(...)100%44100%
File 1: Clamp(...)100%44100%
File 1: Clamp(...)100%44100%
File 1: ClampOne(...)100%44100%
File 1: Abs(...)100%22100%
File 1: Ceiling(...)100%22100%
File 1: Floor(...)100%11100%
File 1: Max(...)100%22100%
File 1: Min(...)100%22100%
File 1: Round(...)100%1010100%
File 1: RoundToPrecision(...)100%44100%
File 1: Squared(...)100%11100%
File 1: FastAdd(...)100%11100%
File 1: FastSub(...)100%11100%
File 1: FastMul(...)100%11100%
File 1: FastMod(...)100%11100%
File 1: SmoothStep(...)100%11100%
File 1: CubicInterpolate(...)100%11100%
File 1: LinearInterpolate(...)100%44100%
File 1: MoveTowards(...)100%88100%
File 1: AddOverflowHelper(...)100%44100%
File 2: .cctor()100%11100%
File 2: Pow(...)100%88100%
File 2: Pow2(...)100%1616100%
File 2: Log2(...)100%1010100%
File 2: Ln(...)100%22100%
File 2: Sqrt(...)100%1818100%
File 2: RadToDeg(...)100%11100%
File 2: DegToRad(...)100%11100%
File 2: Sin(...)100%2424100%
File 2: Cos(...)100%22100%
File 2: SinToCos(...)100%11100%
File 2: Tan(...)100%161695.65%
File 2: Asin(...)100%1616100%
File 2: Acos(...)100%1212100%
File 2: Atan(...)100%1616100%
File 2: Atan2(...)100%1010100%

File(s)

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Core/FixedMath.cs

#LineLine coverage
 1using System;
 2using System.Runtime.CompilerServices;
 3
 4namespace FixedMathSharp
 5{
 6    /// <summary>
 7    /// A static class that provides a variety of fixed-point math functions.
 8    /// Fixed-point numbers are represented as <see cref="Fixed64"/>.
 9    /// </summary>
 10    public static partial class FixedMath
 11    {
 12        #region Fields and Constants
 13
 14        public const int NUM_BITS = 64;
 15        public const int SHIFT_AMOUNT_I = 32;
 16        public const uint MAX_SHIFTED_AMOUNT_UI = (uint)((1L << SHIFT_AMOUNT_I) - 1);
 17        public const ulong MASK_UL = (ulong)(ulong.MaxValue << SHIFT_AMOUNT_I);
 18
 19        public const long MAX_VALUE_L = long.MaxValue; // Max possible value for Fixed64
 20        public const long MIN_VALUE_L = long.MinValue; // Min possible value for Fixed64
 21
 22        public const long ONE_L = 1L << SHIFT_AMOUNT_I;
 23
 24        // Precomputed scale factors
 25        public const float SCALE_FACTOR_F = 1.0f / ONE_L;
 26        public const double SCALE_FACTOR_D = 1.0 / ONE_L;
 127        public const decimal SCALE_FACTOR_M = 1.0m / ONE_L;
 28
 29        /// <summary>
 30        /// Represents the smallest possible value that can be represented by the Fixed64 format.
 31        /// </summary>
 32        /// <remarks>
 33        /// Precision of this type is 2^-SHIFT_AMOUNT,
 34        /// i.e. 1 / (2^SHIFT_AMOUNT) where SHIFT_AMOUNT defines the fractional bits.
 35        /// </remarks>
 36        public const long PRECISION_L = 1L;
 37
 38        /// <summary>
 39        ///  The smallest value that a Fixed64 can have different from zero.
 40        /// </summary>
 41        /// <remarks>
 42        /// With the following rules:
 43        ///      anyValue + Epsilon = anyValue
 44        ///      anyValue - Epsilon = anyValue
 45        ///      0 + Epsilon = Epsilon
 46        ///      0 - Epsilon = -Epsilon
 47        ///  A value Between any number and Epsilon will result in an arbitrary number due to truncating errors.
 48        /// </remarks>
 49        public const long EPSILON_L = 1L << (SHIFT_AMOUNT_I - 20); //~1E-06f
 50
 51        #endregion
 52
 53        #region FixedMath Operations
 54
 55        /// <summary>
 56        /// Produces a value with the magnitude of the first argument and the sign of the second argument.
 57        /// </summary>
 58        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 59        public static Fixed64 CopySign(Fixed64 x, Fixed64 y)
 360        {
 361            return y >= Fixed64.Zero ? x.Abs() : -x.Abs();
 362        }
 63
 64        /// <summary>
 65        /// Clamps value between 0 and 1 and returns value.
 66        /// </summary>
 67        /// <param name="value"></param>
 68        /// <returns></returns>
 69        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 70        public static Fixed64 Clamp01(Fixed64 value)
 1471        {
 1472            return value < Fixed64.Zero ? Fixed64.Zero : value > Fixed64.One ? Fixed64.One : value;
 1473        }
 74
 75        /// <summary>
 76        /// Clamps a fixed-point value between the given minimum and maximum values.
 77        /// </summary>
 78        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 79        public static Fixed64 Clamp(Fixed64 f1, Fixed64 min, Fixed64 max)
 7280        {
 7281            return f1 < min ? min : f1 > max ? max : f1;
 7282        }
 83
 84        /// <summary>
 85        /// Clamps a value to the inclusive range [min, max].
 86        /// </summary>
 87        /// <typeparam name="T">The type of the value, must implement IComparable&lt;T&gt;.</typeparam>
 88        /// <param name="value">The value to clamp.</param>
 89        /// <param name="min">The minimum allowed value.</param>
 90        /// <param name="max">The maximum allowed value.</param>
 91        /// <returns>The clamped value.</returns>
 92        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 93        public static T Clamp<T>(T value, T min, T max) where T : IComparable<T>
 394        {
 495            if (value.CompareTo(max) > 0) return max;
 396            if (value.CompareTo(min) < 0) return min;
 197            return value;
 398        }
 99
 100        /// <summary>
 101        /// Clamps the value between -1 and 1 inclusive.
 102        /// </summary>
 103        /// <param name="f1">The Fixed64 value to clamp.</param>
 104        /// <returns>Returns a value clamped between -1 and 1.</returns>
 105        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 106        public static Fixed64 ClampOne(Fixed64 f1)
 10107        {
 10108            return f1 > Fixed64.One ? Fixed64.One : f1 < -Fixed64.One ? -Fixed64.One : f1;
 10109        }
 110
 111        /// <summary>
 112        /// Returns the absolute value of a Fixed64 number.
 113        /// </summary>
 114        public static Fixed64 Abs(Fixed64 value)
 1188115        {
 116            // For the minimum value, return the max to avoid overflow
 1188117            if (value.m_rawValue == MIN_VALUE_L)
 1118                return Fixed64.MAX_VALUE;
 119
 120            // Use branchless absolute value calculation
 1187121            long mask = value.m_rawValue >> 63; // If negative, mask will be all 1s; if positive, all 0s
 1187122            return Fixed64.FromRaw((value.m_rawValue + mask) ^ mask);
 1188123        }
 124
 125        /// <summary>
 126        /// Returns the smallest integral value that is greater than or equal to the specified number.
 127        /// </summary>
 128        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 129        public static Fixed64 Ceiling(Fixed64 value)
 6130        {
 6131            bool hasFractionalPart = (value.m_rawValue & MAX_SHIFTED_AMOUNT_UI) != 0;
 6132            return hasFractionalPart ? value.Floor() + Fixed64.One : value;
 6133        }
 134
 135        /// <summary>
 136        /// Returns the largest integer less than or equal to the specified number (floor function).
 137        /// </summary>
 138        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 139        public static Fixed64 Floor(Fixed64 value)
 32140        {
 141            // Efficiently zeroes out the fractional part
 32142            return Fixed64.FromRaw((long)((ulong)value.m_rawValue & FixedMath.MASK_UL));
 32143        }
 144
 145        /// <summary>
 146        /// Returns the larger of two fixed-point values.
 147        /// </summary>
 148        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 149        public static Fixed64 Max(Fixed64 f1, Fixed64 f2)
 9150        {
 9151            return f1 >= f2 ? f1 : f2;
 9152        }
 153
 154        /// <summary>
 155        /// Returns the smaller of two fixed-point values.
 156        /// </summary>
 157        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 158        public static Fixed64 Min(Fixed64 a, Fixed64 b)
 29159        {
 29160            return (a < b) ? a : b;
 29161        }
 162
 163        /// <summary>
 164        /// Rounds a fixed-point number to the nearest integral value, based on the specified rounding mode.
 165        /// </summary>
 166        public static Fixed64 Round(Fixed64 value, MidpointRounding mode = MidpointRounding.ToEven)
 21167        {
 21168            long fractionalPart = value.m_rawValue & MAX_SHIFTED_AMOUNT_UI;
 21169            Fixed64 integralPart = value.Floor();
 21170            if (fractionalPart < Fixed64.Half.m_rawValue)
 2171                return integralPart;
 172
 19173            if (fractionalPart > Fixed64.Half.m_rawValue)
 9174                return integralPart + Fixed64.One;
 175
 176            // When value is exactly Fixed64.Halfway between two numbers
 10177            return mode switch
 10178            {
 3179                MidpointRounding.AwayFromZero => value.m_rawValue > 0 ? integralPart + Fixed64.One : integralPart,// For
 7180                _ => (integralPart.m_rawValue & ONE_L) == 0 ? integralPart : integralPart + Fixed64.One,// Rounds to the
 10181            };
 21182        }
 183
 184        /// <summary>
 185        /// Rounds a fixed-point number to a specific number of decimal places.
 186        /// </summary>
 187        public static Fixed64 RoundToPrecision(Fixed64 value, int decimalPlaces, MidpointRounding mode = MidpointRoundin
 7188        {
 7189            if (decimalPlaces < 0 || decimalPlaces >= Pow10Lookup.Length)
 2190                throw new ArgumentOutOfRangeException(nameof(decimalPlaces), "Decimal places out of range.");
 191
 5192            int factor = Pow10Lookup[decimalPlaces];
 5193            Fixed64 scaled = value * factor;
 5194            long rounded = Round(scaled, mode).m_rawValue;
 5195            return new Fixed64(rounded + (factor / 2)) / factor;
 5196        }
 197
 198        /// <summary>
 199        /// Squares the Fixed64 value.
 200        /// </summary>
 201        /// <param name="value">The Fixed64 value to square.</param>
 202        /// <returns>The squared value.</returns>
 203        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 204        public static Fixed64 Squared(Fixed64 value)
 2205        {
 2206            return value * value;
 2207        }
 208
 209        /// <summary>
 210        /// Adds two fixed-point numbers without performing overflow checking.
 211        /// </summary>
 212        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 213        public static Fixed64 FastAdd(Fixed64 x, Fixed64 y)
 5214        {
 5215            return Fixed64.FromRaw(x.m_rawValue + y.m_rawValue);
 5216        }
 217
 218        /// <summary>
 219        /// Subtracts two fixed-point numbers without performing overflow checking.
 220        /// </summary>
 221        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 222        public static Fixed64 FastSub(Fixed64 x, Fixed64 y)
 5223        {
 5224            return Fixed64.FromRaw(x.m_rawValue - y.m_rawValue);
 5225        }
 226
 227        /// <summary>
 228        /// Multiplies two fixed-point numbers without overflow checking for performance-critical scenarios.
 229        /// </summary>
 230        public static Fixed64 FastMul(Fixed64 x, Fixed64 y)
 188231        {
 188232            long xl = x.m_rawValue;
 188233            long yl = y.m_rawValue;
 234
 235            // Split values into high and low bits for long multiplication
 188236            ulong xlo = (ulong)(xl & MAX_SHIFTED_AMOUNT_UI);
 188237            long xhi = xl >> SHIFT_AMOUNT_I;
 188238            ulong ylo = (ulong)(yl & MAX_SHIFTED_AMOUNT_UI);
 188239            long yhi = yl >> SHIFT_AMOUNT_I;
 240
 241            // Perform partial products
 188242            ulong lolo = xlo * ylo;
 188243            long lohi = (long)xlo * yhi;
 188244            long hilo = xhi * (long)ylo;
 188245            long hihi = xhi * yhi;
 246
 247            // Combine the results
 188248            ulong loResult = lolo >> SHIFT_AMOUNT_I;
 188249            long midResult1 = lohi;
 188250            long midResult2 = hilo;
 188251            long hiResult = hihi << SHIFT_AMOUNT_I;
 252
 188253            long sum = (long)loResult + midResult1 + midResult2 + hiResult;
 188254            return Fixed64.FromRaw(sum);
 188255        }
 256
 257        /// <summary>
 258        /// Fast modulus without the checks performed by the '%' operator.
 259        /// </summary>
 260        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 261        public static Fixed64 FastMod(Fixed64 x, Fixed64 y)
 5262        {
 5263            return Fixed64.FromRaw(x.m_rawValue % y.m_rawValue);
 4264        }
 265
 266        /// <summary>
 267        /// Performs a smooth step interpolation between two values.
 268        /// </summary>
 269        /// <remarks>
 270        /// The interpolation follows a cubic Hermite curve where the function starts at `a`,
 271        /// accelerates, and then decelerates towards `b`, ensuring smooth transitions.
 272        /// </remarks>
 273        /// <param name="a">The starting value.</param>
 274        /// <param name="b">The ending value.</param>
 275        /// <param name="t">A value between 0 and 1 that represents the interpolation factor.</param>
 276        /// <returns>The interpolated value between `a` and `b`.</returns>
 277        public static Fixed64 SmoothStep(Fixed64 a, Fixed64 b, Fixed64 t)
 4278        {
 4279            t = t * t * (Fixed64.Three - Fixed64.Two * t);
 4280            return LinearInterpolate(a, b, t);
 4281        }
 282
 283        /// <summary>
 284        /// Performs a cubic Hermite interpolation between two points, using specified tangents.
 285        /// </summary>
 286        /// <remarks>
 287        /// This method interpolates smoothly between `p0` and `p1` while considering the tangents `m0` and `m1`.
 288        /// It is useful for animation curves and smooth motion transitions.
 289        /// </remarks>
 290        /// <param name="p0">The first point.</param>
 291        /// <param name="p1">The second point.</param>
 292        /// <param name="m0">The tangent (slope) at `p0`.</param>
 293        /// <param name="m1">The tangent (slope) at `p1`.</param>
 294        /// <param name="t">A value between 0 and 1 that represents the interpolation factor.</param>
 295        /// <returns>The interpolated value between `p0` and `p1`.</returns>
 296        public static Fixed64 CubicInterpolate(Fixed64 p0, Fixed64 p1, Fixed64 m0, Fixed64 m1, Fixed64 t)
 4297        {
 4298            Fixed64 t2 = t * t;
 4299            Fixed64 t3 = t2 * t;
 4300            return (Fixed64.Two * p0 - Fixed64.Two * p1 + m0 + m1) * t3
 4301                 + (-Fixed64.Three * p0 + Fixed64.Three * p1 - Fixed64.Two * m0 - m1) * t2
 4302                 + m0 * t + p0;
 4303        }
 304
 305        /// <summary>
 306        /// Performs linear interpolation between two fixed-point values based on the interpolant t (0 greater or equal 
 307        /// </summary>
 308        public static Fixed64 LinearInterpolate(Fixed64 from, Fixed64 to, Fixed64 t)
 18309        {
 18310            if (t.m_rawValue >= ONE_L)
 2311                return to;
 16312            if (t.m_rawValue <= 0)
 3313                return from;
 314
 13315            return (to * t) + (from * (Fixed64.One - t));
 18316        }
 317
 318        /// <summary>
 319        /// Moves a value from 'from' to 'to' by a maximum step of 'maxAmount'.
 320        /// Ensures the value does not exceed 'to'.
 321        /// </summary>
 322        public static Fixed64 MoveTowards(Fixed64 from, Fixed64 to, Fixed64 maxAmount)
 4323        {
 4324            if (from < to)
 2325            {
 2326                from += maxAmount;
 2327                if (from > to)
 1328                    from = to;
 2329            }
 2330            else if (from > to)
 2331            {
 2332                from -= maxAmount;
 2333                if (from < to)
 1334                    from = to;
 2335            }
 336
 4337            return Fixed64.FromRaw(from.m_rawValue);
 4338        }
 339
 340        /// <summary>
 341        /// Adds two <see cref="long"/> values and checks for overflow.
 342        /// If an overflow occurs during addition, the <paramref name="overflow"/> parameter is set to true.
 343        /// </summary>
 344        /// <param name="x">The first operand to add.</param>
 345        /// <param name="y">The second operand to add.</param>
 346        /// <param name="overflow">
 347        /// A reference parameter that is set to true if an overflow is detected during the addition.
 348        /// The existing value of <paramref name="overflow"/> is preserved if already true.
 349        /// </param>
 350        /// <returns>The sum of <paramref name="x"/> and <paramref name="y"/>.</returns>
 351        /// <remarks>
 352        /// Overflow is detected by checking for a change in the sign bit that indicates a wrap-around.
 353        /// Additionally, a special check is performed for adding <see cref="Fixed64.MIN_VALUE"/> and -1,
 354        /// as this is a known edge case for overflow.
 355        /// </remarks>
 356        public static long AddOverflowHelper(long x, long y, ref bool overflow)
 24906357        {
 24906358            long sum = x + y;
 359            // Check for overflow using sign bit changes
 24906360            overflow |= ((x ^ y ^ sum) & MIN_VALUE_L) != 0;
 361            // Special check for the case when x is long.Fixed64.MinValue and y is negative
 24906362            if (x == long.MinValue && y == -1)
 1363                overflow = true;
 24906364            return sum;
 24906365        }
 366
 367        #endregion
 368    }
 369}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Core/FixedTrigonometry.cs

#LineLine coverage
 1using System;
 2using System.Runtime.CompilerServices;
 3
 4namespace FixedMathSharp
 5{
 6    public static partial class FixedMath
 7    {
 8        #region Fields and Constants
 9
 110        public static readonly int[] Pow10Lookup = {
 111            1,           // 10^0 = 1
 112            10,          // 10^1 = 10
 113            100,         // 10^2 = 100
 114            1000,        // 10^3 = 1000
 115            10000,       // 10^4 = 10000
 116            100000,      // 10^5 = 100000
 117            1000000,     // 10^6 = 1000000
 118            10000000,    // 10^7 = 1000000
 119            100000000,   // 10^8 = 1000000
 120            1000000000,  // 10^9 = 1000000
 121        };
 22
 23        // Trigonometric and logarithmic constants
 24        internal const double PI_D = 3.14159265358979323846;
 125        public static readonly Fixed64 PI = (Fixed64)PI_D;
 126        public static readonly Fixed64 TwoPI = PI * 2;
 127        public static readonly Fixed64 PiOver2 = PI / 2;
 128        public static readonly Fixed64 PiOver3 = PI / 3;
 129        public static readonly Fixed64 PiOver4 = PI / 4;
 130        public static readonly Fixed64 PiOver6 = PI / 6;
 131        public static readonly Fixed64 Ln2 = (Fixed64)0.6931471805599453;  // Natural logarithm of 2
 32
 133        public static readonly Fixed64 LOG_2_MAX = new Fixed64(63L * ONE_L);
 134        public static readonly Fixed64 LOG_2_MIN = new Fixed64(-64L * ONE_L);
 35
 36        internal const double DEG2RAD_D = 0.01745329251994329576;  // π / 180
 137        public static readonly Fixed64 Deg2Rad = (Fixed64)DEG2RAD_D;  // Degrees to radians conversion factor
 38        internal const double RAD2DEG_D = 57.2957795130823208767;  // 180 / π
 139        public static readonly Fixed64 Rad2Deg = (Fixed64)RAD2DEG_D;  // Radians to degrees conversion factor
 40
 41        // Asin Padé approximations
 142        private static readonly Fixed64 PADE_A1 = (Fixed64)0.183320102;
 143        private static readonly Fixed64 PADE_A2 = (Fixed64)0.0218804099;
 44
 45        // Carefully optimized polynomial coefficients for sin(x), ensuring maximum precision in Fixed64 math.
 146        private static readonly Fixed64 SIN_COEFF_3 = (Fixed64)0.16666667605750262737274169921875d; // 1/3!
 147        private static readonly Fixed64 SIN_COEFF_5 = (Fixed64)0.0083328341133892536163330078125d; // 1/5!
 148        private static readonly Fixed64 SIN_COEFF_7 = (Fixed64)0.00019588856957852840423583984375d; // 1/7!
 49
 50        #endregion
 51
 52        #region FixedTrigonometry Operations
 53
 54        /// <summary>
 55        /// Raises the base number b to the power of exp.
 56        /// Uses logarithms to compute power efficiently for fixed-point values.
 57        /// </summary>
 58        /// <exception cref="DivideByZeroException">
 59        /// The base was Fixed64.Zero, with a negative expFixed64.Onent
 60        /// </exception>
 61        /// <exception cref="ArgumentOutOfRangeException">
 62        /// The base was negative, with a non-Fixed64.Zero expFixed64.Onent
 63        /// </exception>
 64        public static Fixed64 Pow(Fixed64 b, Fixed64 exp)
 665        {
 666            if (b == Fixed64.One)
 167                return Fixed64.One;
 68
 569            if (exp.m_rawValue == 0)
 170                return Fixed64.One;
 71
 472            if (b.m_rawValue == 0)
 273            {
 274                if (exp.m_rawValue < 0)
 175                    throw new DivideByZeroException("Cannot raise 0 to a negative power.");
 76
 177                return Fixed64.Zero;
 78            }
 79
 280            Fixed64 log2 = Log2(b);  // Calculate logarithm base 2
 281            return Pow2(exp * log2);  // Raise 2 to the power of log2 result
 582        }
 83
 84        /// <summary>
 85        /// Raises 2 to the power of x.
 86        /// Provides high accuracy for small values of x.
 87        /// </summary>
 88        public static Fixed64 Pow2(Fixed64 x)
 889        {
 890            if (x.m_rawValue == 0)
 191                return Fixed64.One;
 92
 93            // Handle negative expFixed64.Onents by using the reciprocal
 794            bool neg = x.m_rawValue < 0;
 795            if (neg)
 496                x = -x;
 97
 798            if (x == Fixed64.One)
 399                return neg ? Fixed64.One / Fixed64.Two : Fixed64.Two;
 100
 4101            if (x >= LOG_2_MAX)
 2102                return neg ? Fixed64.One / Fixed64.MAX_VALUE : Fixed64.MAX_VALUE;
 103
 104            /*
 105             * Taylor series expansion for exp(x)
 106             * From term n, we get term n+1 by multiplying with x/n.
 107             * When the sum term drops to Fixed64.Zero, we can stop summing.
 108             */
 2109            int integerPart = (int)x.Floor();
 2110            x = Fixed64.FromRaw(x.m_rawValue & MAX_SHIFTED_AMOUNT_UI);  // Fractional part
 111
 2112            var result = Fixed64.One;
 2113            var term = Fixed64.One;
 2114            int i = 1;
 13115            while (term.m_rawValue != 0)
 11116            {
 11117                term = FastMul(FastMul(x, term), Ln2) / (Fixed64)i;
 11118                result += term;
 11119                i++;
 11120            }
 121
 2122            result = Fixed64.FromRaw(result.m_rawValue << integerPart);
 2123            if (neg)
 1124                result = Fixed64.One / result;
 125
 2126            return result;
 8127        }
 128
 129        /// <summary>
 130        /// Returns the base-2 logarithm of a specified number.
 131        /// Provides at least 9 decimals of accuracy.
 132        /// </summary>
 133        /// <remarks>
 134        /// This implementation is based on Clay. S. Turner's fast binary logarithm algorithm
 135        /// (C. S. Turner,  "A Fast Binary Logarithm Algorithm", IEEE Signal Processing Mag., pp. 124,140, Sep. 2010.)
 136        /// </remarks>
 137        public static Fixed64 Log2(Fixed64 x)
 6138        {
 6139            if (x.m_rawValue <= 0)
 1140                throw new ArgumentOutOfRangeException("Cannot compute logarithm of non-positive number.");
 141
 5142            long b = 1U << (SHIFT_AMOUNT_I - 1);  // Initial value for binary logarithm
 5143            long y = 0;  // Result accumulator
 5144            long rawX = x.m_rawValue;
 145
 146            // Adjust rawX to the correct range [1, 2)
 6147            while (rawX < ONE_L)
 1148            {
 1149                rawX <<= 1;
 1150                y -= ONE_L;
 1151            }
 152
 11153            while (rawX >= (ONE_L << 1))
 6154            {
 6155                rawX >>= 1;
 6156                y += ONE_L;
 6157            }
 158
 5159            Fixed64 z = Fixed64.FromRaw(rawX);  // Remaining fraction
 160
 330161            for (int i = 0; i < SHIFT_AMOUNT_I; i++)
 160162            {
 160163                z = FastMul(z, z);
 160164                if (z.m_rawValue >= (ONE_L << 1))
 15165                {
 15166                    z = Fixed64.FromRaw(z.m_rawValue >> 1);
 15167                    y += b;
 15168                }
 160169                b >>= 1;
 160170            }
 171
 5172            return Fixed64.FromRaw(y);
 5173        }
 174
 175        /// <summary>
 176        /// Returns the natural logarithm of a specified fixed-point number.
 177        /// Provides at least 7 decimals of accuracy.
 178        /// </summary>
 179        public static Fixed64 Ln(Fixed64 x)
 2180        {
 2181            if (x.m_rawValue <= 0)
 1182                throw new ArgumentOutOfRangeException("Cannot compute logarithm of non-positive number.");
 183
 1184            return FastMul(Log2(x), Ln2).Round();
 1185        }
 186
 187        /// <summary>
 188        /// Returns the square root of a specified fixed-point number.
 189        /// </summary>
 190        public static Fixed64 Sqrt(Fixed64 x)
 454191        {
 454192            if (x.m_rawValue < 0)
 1193                throw new ArgumentOutOfRangeException("Cannot compute square root of a negative number.");
 194
 453195            ulong num = (ulong)x.m_rawValue;
 453196            ulong result = 0UL;
 453197            ulong bit = 1UL << (sizeof(long) * 8) - 2; // second-to-top bit of a 64-bit integer
 198
 199            // Adjust the bit position to a suitable starting point
 7129200            while (bit > num)
 6676201                bit >>= 2;
 202
 203            // Perform the square root calculation using bitwise shifts
 2718204            for (int i = 0; i < 2; ++i)
 906205            {
 206                // Calculate the top bits of the square root result
 15974207                while (bit != 0)
 15068208                {
 15068209                    if (num >= result + bit)
 1874210                    {
 1874211                        num -= result + bit;
 1874212                        result = (result >> 1) + bit;
 1874213                    }
 214                    else
 13194215                    {
 13194216                        result >>= 1;
 13194217                    }
 218
 15068219                    bit >>= 2;
 15068220                }
 221
 906222                if (i == 0)
 453223                {
 224                    // Process it again to get the remaining bits
 453225                    if (num > ((1UL << SHIFT_AMOUNT_I) - 1))
 1226                    {
 227                        // Handle large remainders by adjusting the result
 1228                        num -= result;
 1229                        num = (num << SHIFT_AMOUNT_I) - (ulong)Fixed64.Half.m_rawValue;
 1230                        result = (result << SHIFT_AMOUNT_I) + (ulong)Fixed64.Half.m_rawValue;
 1231                    }
 232                    else
 452233                    {
 452234                        num <<= SHIFT_AMOUNT_I;
 452235                        result <<= SHIFT_AMOUNT_I;
 452236                    }
 237
 453238                    bit = 1UL << (SHIFT_AMOUNT_I - 2);
 453239                }
 906240            }
 241
 242            // Rounding: round up if necessary
 453243            if (num > result && (num - result) > (result >> 1))
 29244                ++result;
 245
 453246            return Fixed64.FromRaw((long)result);
 453247        }
 248
 249        /// <summary>
 250        /// Converts a value in radians to degrees.
 251        /// </summary>
 252        /// <remarks>
 253        /// Uses double precision to avoid precision loss
 254        /// </remarks>
 255        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 256        public static Fixed64 RadToDeg(Fixed64 rad)
 20257        {
 20258            return new Fixed64((double)rad * RAD2DEG_D);
 20259        }
 260
 261        /// <summary>
 262        /// Converts a value in degrees to radians.
 263        /// </summary>
 264        /// <remarks>
 265        /// Uses double precision to avoid precision loss
 266        /// </remarks>
 267        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 268        public static Fixed64 DegToRad(Fixed64 deg)
 91269        {
 91270            return new Fixed64((double)deg * DEG2RAD_D);
 91271        }
 272
 273        /// <summary>
 274        /// Computes the sine of a given angle in radians using an optimized
 275        /// minimax polynomial approximation.
 276        /// </summary>
 277        /// <param name="x">The angle in radians.</param>
 278        /// <returns>The sine of the given angle, in fixed-point format.</returns>
 279        /// <remarks>
 280        /// - This function uses a Chebyshev-polynomial-based approximation to ensure high accuracy
 281        ///   while maintaining performance in fixed-point arithmetic.
 282        /// - The coefficients have been carefully tuned to minimize fixed-point truncation errors.
 283        /// - The error is less than 1 ULP (unit in the last place) at key reference points,
 284        ///   ensuring <c>Sin(π/4) = 0.707106781192124</c> exactly within Fixed64 precision.
 285        /// - The function automatically normalizes input values to the range [-π, π] for stability.
 286        /// </remarks>
 287        public static Fixed64 Sin(Fixed64 x)
 315288        {
 289            // Check for special cases
 370290            if (x == Fixed64.Zero) return Fixed64.Zero;   // sin(0) = 0
 333291            if (x == PiOver2) return Fixed64.One;         // sin(π/2) = 1
 189292            if (x == -PiOver2) return -Fixed64.One;       // sin(-π/2) = -1
 186293            if (x == PI) return Fixed64.Zero;             // sin(π) = 0
 202294            if (x == -PI) return Fixed64.Zero;            // sin(-π) = 0
 168295            if (x == TwoPI || x == -TwoPI) return Fixed64.Zero;  // sin(2π) = 0
 296
 297            // Normalize x to [-π, π]
 164298            x %= TwoPI;
 164299            if (x < -PI)
 75300                x += TwoPI;
 89301            else if (x > PI)
 1302                x -= TwoPI;
 303
 164304            bool flip = false;
 164305            if (x < Fixed64.Zero)
 7306            {
 7307                x = -x;
 7308                flip = true;
 7309            }
 310
 164311            if (x > PiOver2)
 82312                x = PI - x;
 313
 314            // Precompute x^2
 164315            Fixed64 x2 = x * x;
 316
 317            // Optimized Chebyshev Polynomial for Sin(x)
 164318            Fixed64 result = x * (Fixed64.One
 164319                - x2 * SIN_COEFF_3
 164320                + (x2 * x2) * SIN_COEFF_5
 164321                - (x2 * x2 * x2) * SIN_COEFF_7);
 322
 164323            return flip ? -result : result;
 315324        }
 325
 326        /// <summary>
 327        /// Computes the cosine of a given angle in radians using a sine-based identity transformation.
 328        /// </summary>
 329        /// <param name="x">The angle in radians.</param>
 330        /// <returns>The cosine of the given angle, in fixed-point format.</returns>
 331        /// <remarks>
 332        /// - Instead of directly approximating cosine, this function derives <c>cos(x)</c> using
 333        ///   the identity <c>cos(x) = sin(x + π/2)</c>. This ensures maximum accuracy.
 334        /// - The underlying sine function is computed using a highly optimized minimax polynomial approximation.
 335        /// - By leveraging this transformation, cosine achieves the same precision guarantees
 336        ///   as sine, including <c>Cos(π/4) = 0.707106781192124</c> exactly within Fixed64 precision.
 337        /// - The function automatically normalizes input values to the range [-π, π] for stability.
 338        /// </remarks>
 339        public static Fixed64 Cos(Fixed64 x)
 154340        {
 154341            long xl = x.m_rawValue;
 154342            long rawAngle = xl + (xl > 0 ? -PI.m_rawValue - PiOver2.m_rawValue : PiOver2.m_rawValue);
 154343            return Sin(Fixed64.FromRaw(rawAngle));
 154344        }
 345
 346        public static Fixed64 SinToCos(Fixed64 sin)
 1347        {
 1348            return Sqrt(Fixed64.One - sin * sin);
 1349        }
 350
 351        /// <summary>
 352        /// Returns the tangent of x.
 353        /// </summary>
 354        /// <remarks>
 355        /// This function is not well-tested. It may be wildly inaccurate.
 356        /// </remarks>
 357        public static Fixed64 Tan(Fixed64 x)
 10358        {
 359            // Check for special cases
 11360            if (x == Fixed64.Zero) return Fixed64.Zero;
 11361            if (x == PiOver4) return Fixed64.One;
 8362            if (x == -PiOver4) return -Fixed64.One;
 363
 364            // Normalize x to [-π/2, π/2]
 6365            x %= PI;
 6366            if (x < -PiOver2)
 1367                x += PI;
 5368            else if (x > PiOver2)
 1369                x -= PI;
 370
 371            // Use continued fraction to approximate tan(x)
 6372            Fixed64 x2 = x * x;
 6373            Fixed64 numerator = x;
 6374            Fixed64 denominator = Fixed64.One;
 375
 376            // Iterate over the continued fraction terms
 6377            Fixed64 prevDenominator = denominator;
 6378            int start = x.Abs() > PiOver6 ? 19 : 13;
 114379            for (int i = start; i >= 1; i -= 2)
 51380            {
 51381                denominator = (Fixed64)i - (x2 / denominator);
 51382                if ((denominator - prevDenominator).Abs() < Fixed64.Precision)
 0383                    break;
 51384                prevDenominator = denominator;
 51385            }
 386
 6387            return numerator / denominator;
 10388        }
 389
 390        /// <summary>
 391        /// Returns the arc-sine of a fixed-point number x, which is the angle in radians
 392        /// whose sine is x, using a combination of a Taylor series expansion and trigonometric identities.
 393        ///
 394        /// For values of x near ±1, the identity asin(x) = π/2 - acos(x) is used for stability.
 395        /// For values of x near 0, a Taylor series expansion is used.
 396        /// </summary>
 397        /// <param name="x">The input value (sine) whose arcsine is to be computed. Should be in the range [-1, 1].</par
 398        /// <returns>The arc-sine of x in radians.</returns>
 399        /// <exception cref="ArithmeticException">Thrown if x is outside the domain [-1, 1].</exception>
 400        public static Fixed64 Asin(Fixed64 x)
 12401        {
 402            // Ensure x is within the domain [-1, 1]
 12403            if (x < -Fixed64.One || x > Fixed64.One)
 2404                throw new ArithmeticException("Input out of domain for Asin: " + x);
 405
 406            // Handle boundary cases for -1 and 1
 11407            if (x == Fixed64.One) return PiOver2;  // asin(1) = π/2
 10408            if (x == -Fixed64.One) return -PiOver2;  // asin(-1) = -π/2
 409
 410            // Special case handling for asin(0.5) -> π/6 and asin(-0.5) -> -π/6
 9411            if (x == Fixed64.Half) return PiOver6;
 8412            if (x == -Fixed64.Half) return -PiOver6;
 413
 414            // For values close to 0, use a Padé approximation for better precision
 6415            if (x.Abs() < Fixed64.Half)
 4416            {
 417                // Padé approximation of asin(x) for |x| < 0.5
 4418                Fixed64 xSquared = x * x;
 4419                Fixed64 numerator = x * (Fixed64.One + (xSquared * (PADE_A1 + (xSquared * PADE_A2))));
 4420                return numerator;
 421            }
 422
 423            // For values closer to ±1, use the identity: asin(x) = π/2 - acos(x) for stability
 2424            return x > Fixed64.Zero
 2425                ? PiOver2 - Acos(x)
 2426                : -PiOver2 + Acos(-x);
 10427        }
 428
 429        /// <summary>
 430        /// Returns the arccosine of the specified number x, calculated using a combination of the atan and sqrt functio
 431        /// </summary>
 432        /// <param name="x">The input value whose arccosine is to be computed. Should be in the range [-1, 1].</param>
 433        /// <returns>The arccosine of x in radians.</returns>
 434        /// <exception cref="ArgumentOutOfRangeException">Thrown if x is outside the domain [-1, 1].</exception>
 435        public static Fixed64 Acos(Fixed64 x)
 19436        {
 19437            if (x < -Fixed64.One || x > Fixed64.One)
 2438                throw new ArgumentOutOfRangeException(nameof(x), "Input out of domain for Acos: " + x);
 439
 440            // For values near 1 or -1, the result is directly known.
 18441            if (x == Fixed64.One) return Fixed64.Zero;      // acos(1) = 0
 17442            if (x == -Fixed64.One) return PI;       // acos(-1) = π
 21443            if (x == Fixed64.Zero) return PiOver2;  // acos(0) = π/2
 444
 445            // Compute using the relationship acos(x) = atan(sqrt(1 - x^2) / x) + π/2 when x is negative
 9446            var sqrtTerm = Sqrt(Fixed64.One - x * x);   // sqrt(1 - x^2)
 9447            var atanTerm = Atan(sqrtTerm / x);
 448
 9449            return x < Fixed64.Zero
 9450                    ? atanTerm + PI   // acos(-x) = atan(...) + π
 9451                    : atanTerm;               // Otherwise, return just atan(sqrt(...))
 17452        }
 453
 454        /// <summary>
 455        /// Returns the arctangent of the specified number, using a more accurate approximation for larger values.
 456        /// This function has at least 7 decimals of accuracy.
 457        /// </summary>
 458        public static Fixed64 Atan(Fixed64 z)
 70459        {
 76460            if (z == Fixed64.Zero) return Fixed64.Zero;
 82461            if (z == Fixed64.One) return PiOver4;
 52462            if (z == -Fixed64.One) return -PiOver4;
 463
 40464            bool neg = z < Fixed64.Zero;
 54465            if (neg) z = -z;
 466
 467            // Adjust series for z > 0.5 using the identity.
 468            Fixed64 adjustedResult;
 40469            if (z > Fixed64.Half)
 18470            {
 471                // Apply the identity: atan(z) = π/4 - atan((1 - z) / (1 + z))
 18472                Fixed64 transformedZ = (Fixed64.One - z) / (Fixed64.One + z);
 18473                adjustedResult = PiOver4 - Atan(transformedZ);
 18474            }
 475            else
 22476            {
 477                // Use extended Taylor series directly for better precision on small z.
 22478                Fixed64 zSq = z * z;
 479
 22480                Fixed64 result = z;
 22481                Fixed64 term = z;
 22482                int sign = -1;
 483
 198484                for (int i = 3; i < 15; i += 2)
 89485                {
 89486                    term *= zSq;
 89487                    Fixed64 nextTerm = term / i;
 89488                    if (nextTerm.Abs() < Fixed64.Precision)
 12489                        break;
 490
 77491                    result += nextTerm * sign;
 77492                    sign = -sign;
 77493                }
 494
 22495                adjustedResult = result;
 22496            }
 497
 40498            return neg ? -adjustedResult : adjustedResult;
 70499        }
 500
 501        /// <summary>
 502        /// Computes the angle whose tangent is the quotient of two specified numbers.
 503        /// </summary>
 504        /// <remarks>
 505        /// Uses a fixed-point arithmetic approximation for the arc tangent function, which is more efficient than using
 506        /// especially on systems where floating-point operations are expensive.
 507        /// </remarks>
 508        /// <param name="y">The y-coordinate of the point to which the angle is measured.</param>
 509        /// <param name="x">The x-coordinate of the point to which the angle is measured.</param>
 510        /// <returns>An angle, θ, measured in radians, such that -π ≤ θ ≤ π, and tan(θ) = y / x,
 511        /// taking into account the quadrants of the inputs to determine the sign of the result.</returns>
 512        public static Fixed64 Atan2(Fixed64 y, Fixed64 x)
 29513        {
 29514            if (x == Fixed64.Zero)
 7515            {
 7516                if (y > Fixed64.Zero)
 3517                    return PiOver2;
 4518                if (y == Fixed64.Zero)
 3519                    return Fixed64.Zero;
 1520                return -PiOver2;
 521            }
 522
 22523            Fixed64 atan = Atan(y / x);
 524
 525            // Adjust based on the quadrant
 22526            if (x < Fixed64.Zero)
 7527            {
 7528                if (y >= Fixed64.Zero)
 3529                {
 530                    // Second quadrant
 3531                    return atan + PI;
 532                }
 533                else
 4534                {
 535                    // Third quadrant
 4536                    return atan - PI;
 537                }
 538            }
 539
 540            // First or fourth quadrant
 15541            return atan;
 29542        }
 543
 544        #endregion
 545    }
 546}

Methods/Properties

.cctor()
CopySign(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
Clamp01(FixedMathSharp.Fixed64)
Clamp(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
Clamp(T,T,T)
ClampOne(FixedMathSharp.Fixed64)
Abs(FixedMathSharp.Fixed64)
Ceiling(FixedMathSharp.Fixed64)
Floor(FixedMathSharp.Fixed64)
Max(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
Min(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
Round(FixedMathSharp.Fixed64,System.MidpointRounding)
RoundToPrecision(FixedMathSharp.Fixed64,System.Int32,System.MidpointRounding)
Squared(FixedMathSharp.Fixed64)
FastAdd(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
FastSub(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
FastMul(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
FastMod(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
SmoothStep(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
CubicInterpolate(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
LinearInterpolate(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
MoveTowards(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
AddOverflowHelper(System.Int64,System.Int64,System.Boolean&)
.cctor()
Pow(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
Pow2(FixedMathSharp.Fixed64)
Log2(FixedMathSharp.Fixed64)
Ln(FixedMathSharp.Fixed64)
Sqrt(FixedMathSharp.Fixed64)
RadToDeg(FixedMathSharp.Fixed64)
DegToRad(FixedMathSharp.Fixed64)
Sin(FixedMathSharp.Fixed64)
Cos(FixedMathSharp.Fixed64)
SinToCos(FixedMathSharp.Fixed64)
Tan(FixedMathSharp.Fixed64)
Asin(FixedMathSharp.Fixed64)
Acos(FixedMathSharp.Fixed64)
Atan(FixedMathSharp.Fixed64)
Atan2(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)