< Summary

Line coverage
100%
Covered lines: 1692
Uncovered lines: 0
Coverable lines: 1692
Total lines: 3819
Line coverage: 100%
Branch coverage
100%
Covered branches: 628
Total branches: 628
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: IsAddOrSubtractResultExact(...)100%11100%
File 1: TryAdd(...)100%22100%
File 1: TrySubtract(...)100%22100%
File 1: TryAddSubtract(...)100%66100%
File 1: TrySubtractSums(...)100%66100%
File 1: AbsToUInt64(...)100%22100%
File 1: Multiply64To128(...)100%11100%
File 1: IsMagnitudeRepresentable(...)100%66100%
File 1: GetRoundedMagnitude(...)100%1010100%
File 1: CompareMagnitudeSquared(...)100%1212100%
File 1: GetMagnitudeSquaredWords(...)100%11100%
File 1: GetFloorSquareRoot(...)100%1212100%
File 1: AddMagnitudeSquare(...)100%22100%
File 1: ShiftRightRoundedToEven(...)100%66100%
File 1: DivideMagnitude(...)100%2020100%
File 1: RoundGuardedQuotientToEven(...)100%88100%
File 2: op_Explicit(...)100%44100%
File 2: op_Explicit(...)100%22100%
File 2: op_Explicit(...)100%11100%
File 2: op_Explicit(...)100%22100%
File 2: op_Explicit(...)100%11100%
File 2: op_Explicit(...)100%11100%
File 2: op_Explicit(...)100%11100%
File 2: op_Explicit(...)100%11100%
File 2: op_Explicit(...)100%11100%
File 2: op_Explicit(...)100%11100%
File 2: ToString()100%11100%
File 2: ToString(...)100%11100%
File 2: ToString(...)100%22100%
File 2: TryFormat(...)100%11100%
File 2: Parse(...)100%11100%
File 2: Parse(...)100%66100%
File 2: ParseRaw(...)100%44100%
File 2: TryParse(...)100%11100%
File 2: TryParse(...)100%66100%
File 2: TryParseRaw(...)100%44100%
File 2: FromRaw(...)100%11100%
File 2: ToInt(...)100%11100%
File 2: FromDouble(...)100%88100%
File 2: FromDecimal(...)100%44100%
File 2: FromFraction(...)100%11100%
File 2: ToDouble(...)100%11100%
File 2: ToFloat(...)100%11100%
File 2: ToDecimal(...)100%11100%
File 3: get_MaxValue()100%11100%
File 3: get_MinValue()100%11100%
File 3: get_One()100%11100%
File 3: get_NegOne()100%11100%
File 3: get_Two()100%11100%
File 3: get_Three()100%11100%
File 3: get_Half()100%11100%
File 3: get_Quarter()100%11100%
File 3: get_Eighth()100%11100%
File 3: get_Zero()100%11100%
File 3: get_Pi()100%11100%
File 3: get_TwoPi()100%11100%
File 3: get_HalfPi()100%11100%
File 3: get_PiOver3()100%11100%
File 3: get_PiOver4()100%11100%
File 3: get_PiOver6()100%11100%
File 3: get_InvertedPi()100%11100%
File 3: get_OneEighty()100%11100%
File 3: get_Deg2Rad()100%11100%
File 3: get_Rad2Deg()100%11100%
File 3: get_Ln2()100%11100%
File 3: get_Log2Max()100%11100%
File 3: get_Log2Min()100%11100%
File 3: get_PadeA1()100%11100%
File 3: get_PadeA2()100%11100%
File 3: get_SinCoeff3()100%11100%
File 3: get_SinCoeff5()100%11100%
File 3: get_SinCoeff7()100%11100%
File 3: get_CosCoeff2()100%11100%
File 3: get_CosCoeff4()100%11100%
File 3: get_CosCoeff6()100%11100%
File 3: get_CosCoeff8()100%11100%
File 3: get_MinIncrement()100%11100%
File 3: get_Epsilon()100%11100%
File 3: .ctor(...)100%11100%
File 3: .ctor(...)100%11100%
File 3: Offset(...)100%11100%
File 3: ToRawString()100%11100%
File 4: Equals(...)100%22100%
File 4: Equals(...)100%11100%
File 4: Equals(...)100%11100%
File 4: GetHashCode()100%11100%
File 4: GetHashCode(...)100%11100%
File 4: CompareTo(...)100%11100%
File 5: MultiplyAdd(...)100%11100%
File 5: TryMultiplyAdd(...)100%22100%
File 5: MultiplyAdd(...)100%88100%
File 5: TryMultiplySubtractClamped(...)100%11100%
File 5: TryMultiplySubtractClamped(...)100%11100%
File 5: TryMultiplySubtractClamped(...)100%11100%
File 5: GetExactRawProduct(...)100%22100%
File 5: TryRoundNonnegativeTwoFactorDifference(...)100%44100%
File 5: TryRoundNonnegativeThreeFactorDifference(...)100%44100%
File 5: TryRoundNonnegativeFourFactorDifference(...)100%88100%
File 5: TryRoundNonnegative(...)100%22100%
File 5: TryMultiplyDifference(...)100%88100%
File 5: TryMultiplyDivide(...)100%22100%
File 5: MultiplyDivide(...)100%88100%
File 5: TryMultiplyDivide(...)100%22100%
File 5: TryMultiplyDivideBySum(...)100%11100%
File 5: MultiplyDivide(...)100%1010100%
File 5: CountTrailingZeroes(...)100%11100%
File 5: CancelCommonPowersOfTwo(...)100%88100%
File 5: CancelCommonPowersOfTwo(...)100%1010100%
File 5: Divide128By64(...)100%1010100%
File 5: RoundAndApplySign(...)100%44100%
File 5: ApplySignedMagnitude(...)100%1010100%
File 6: op_Addition(...)100%44100%
File 6: op_Addition(...)100%11100%
File 6: op_Addition(...)100%11100%
File 6: op_Subtraction(...)100%44100%
File 6: op_Subtraction(...)100%11100%
File 6: op_Subtraction(...)100%11100%
File 6: op_Multiply(...)100%1212100%
File 6: op_Multiply(...)100%11100%
File 6: op_Multiply(...)100%11100%
File 6: op_Division(...)100%22100%
File 6: op_Division(...)100%11100%
File 6: op_Division(...)100%11100%
File 6: op_Modulus(...)100%44100%
File 6: op_Modulus(...)100%11100%
File 6: op_Modulus(...)100%11100%
File 6: op_UnaryNegation(...)100%22100%
File 6: op_Increment(...)100%11100%
File 6: op_Decrement(...)100%11100%
File 6: op_LeftShift(...)100%11100%
File 6: op_RightShift(...)100%11100%
File 6: op_GreaterThan(...)100%11100%
File 6: op_GreaterThan(...)100%11100%
File 6: op_GreaterThan(...)100%11100%
File 6: op_LessThan(...)100%11100%
File 6: op_LessThan(...)100%11100%
File 6: op_LessThan(...)100%11100%
File 6: op_GreaterThanOrEqual(...)100%11100%
File 6: op_GreaterThanOrEqual(...)100%11100%
File 6: op_GreaterThanOrEqual(...)100%11100%
File 6: op_LessThanOrEqual(...)100%11100%
File 6: op_LessThanOrEqual(...)100%11100%
File 6: op_LessThanOrEqual(...)100%11100%
File 6: op_Equality(...)100%11100%
File 6: op_Equality(...)100%11100%
File 6: op_Equality(...)100%11100%
File 6: op_Inequality(...)100%11100%
File 6: op_Inequality(...)100%11100%
File 6: op_Inequality(...)100%11100%
File 7: CountLeadingZeroes(...)100%66100%
File 7: Sign(...)100%22100%
File 7: IsInteger(...)100%11100%
File 7: CompareProducts(...)100%11100%
File 7: CompareProducts(...)100%11100%
File 8: LerpFullDomain(...)100%1616100%
File 8: ProjectNonNegativeDifference(...)100%88100%
File 8: RoundSquaredDistance(...)100%1414100%
File 8: BarycentricCoordinateFullDomain(...)100%22100%
File 8: RoundSignedToFixed(...)100%2424100%
File 8: RoundSquareRootToFixed(...)100%1616100%
File 8: NormalizeWideComponent(...)100%1414100%
File 8: TryAddProducts(...)100%11100%
File 8: TryAddProducts(...)100%11100%
File 8: TryAddScaledProducts(...)100%44100%
File 8: TrySubtractProducts(...)100%11100%
File 8: TryRoundProductCombination(...)100%66100%
File 9: TryGetUnitIntervalRatio(...)100%1212100%
File 9: TryGetUnitIntervalRatio(...)100%1616100%
File 9: GetUnitIntervalRatio(...)100%44100%
File 9: GetUnitIntervalRatio(...)100%66100%
File 9: GetUnitIntervalRatio128(...)100%1010100%
File 9: GetUnitIntervalRatio(...)100%11100%
File 9: GetSignedRatio(...)100%4848100%
File 9: GetSignedRatio(...)100%5252100%
File 9: TryGetSignedRatio(...)100%1212100%
File 9: GetNonNegativeProduct(...)100%22100%
File 9: GetSignedRawRatio(...)100%1010100%
File 9: TryGetSignedRawRatio(...)100%1212100%
File 9: TryGetSignedRawRatio(...)100%44100%
File 9: TryGetSignedRawRatio(...)100%44100%
File 9: GetNonNegativeRawRatioFloor(...)100%44100%
File 9: TryGetSignedRawRatio(...)100%88100%
File 9: TryGetSignedRawRatioCore(...)100%2020100%
File 9: TryCreateRawRatioResult(...)100%1414100%
File 9: GetMagnitudeBitLength(...)100%11100%
File 9: GetActiveMagnitudeLength(...)100%44100%
File 9: ShiftLeftMagnitude(...)100%44100%
File 9: ShiftRightOne(...)100%22100%

File(s)

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Numerics/Scalars/Fixed64.Arithmetic.cs

#LineLine coverage
 1
 2
 3using System;
 4using System.Runtime.CompilerServices;
 5
 6namespace FixedMathSharp;
 7
 8/// <content>
 9/// Arithmetic operations for <see cref="Fixed64"/>, including overflow-safe
 10/// try-add/try-subtract helpers and related combined operations.
 11/// </content>
 12public partial struct Fixed64
 13{
 14    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 15    private static bool IsAddOrSubtractResultExact(long left, long result, long overflowMask) =>
 236260616        (overflowMask & (left ^ result)) >= 0;
 17
 18    /// <summary>
 19    /// Attempts to add two values without saturation.
 20    /// </summary>
 21    /// <param name="left">The left operand.</param>
 22    /// <param name="right">The right operand.</param>
 23    /// <param name="result">
 24    /// The exact sum when representable; otherwise, <see langword="default"/>.
 25    /// </param>
 26    /// <returns><see langword="true"/> when the exact sum is representable; otherwise, <see langword="false"/>.</return
 27    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 28    public static bool TryAdd(Fixed64 left, Fixed64 right, out Fixed64 result)
 29    {
 144930        long leftRaw = left.m_rawValue;
 144931        long rightRaw = right.m_rawValue;
 144932        long rawResult = unchecked(leftRaw + rightRaw);
 144933        if (!IsAddOrSubtractResultExact(leftRaw, rawResult, ~(leftRaw ^ rightRaw)))
 34        {
 10135            result = default;
 10136            return false;
 37        }
 38
 134839        result = new Fixed64(rawResult);
 134840        return true;
 41    }
 42
 43    /// <summary>
 44    /// Attempts to subtract two values without saturation.
 45    /// </summary>
 46    /// <param name="left">The left operand.</param>
 47    /// <param name="right">The right operand.</param>
 48    /// <param name="result">
 49    /// The exact difference when representable; otherwise, <see langword="default"/>.
 50    /// </param>
 51    /// <returns><see langword="true"/> when the exact difference is representable; otherwise, <see langword="false"/>.<
 52    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 53    public static bool TrySubtract(Fixed64 left, Fixed64 right, out Fixed64 result)
 54    {
 382555        long leftRaw = left.m_rawValue;
 382556        long rightRaw = right.m_rawValue;
 382557        long rawResult = unchecked(leftRaw - rightRaw);
 382558        if (!IsAddOrSubtractResultExact(leftRaw, rawResult, leftRaw ^ rightRaw))
 59        {
 1260            result = default;
 1261            return false;
 62        }
 63
 381364        result = new Fixed64(rawResult);
 381365        return true;
 66    }
 67
 68    /// <summary>
 69    /// Attempts to add two values and subtract a third without intermediate saturation.
 70    /// </summary>
 71    /// <param name="firstAddend">The first addend.</param>
 72    /// <param name="secondAddend">The second addend.</param>
 73    /// <param name="subtrahend">The value to subtract from the exact sum.</param>
 74    /// <param name="result">
 75    /// The exact result when representable; otherwise, <see langword="default"/>.
 76    /// </param>
 77    /// <returns>
 78    /// <see langword="true"/> when the exact result is representable; otherwise, <see langword="false"/>.
 79    /// </returns>
 80    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 81    public static bool TryAddSubtract(
 82        Fixed64 firstAddend,
 83        Fixed64 secondAddend,
 84        Fixed64 subtrahend,
 85        out Fixed64 result)
 86    {
 113187        long firstRaw = firstAddend.m_rawValue;
 113188        long secondRaw = secondAddend.m_rawValue;
 113189        ulong firstLow = unchecked((ulong)firstRaw);
 113190        ulong secondLow = unchecked((ulong)secondRaw);
 113191        ulong low = unchecked(firstLow + secondLow);
 113192        long high = (firstRaw >> 63) + (secondRaw >> 63);
 113193        if (low < firstLow)
 31394            high++;
 95
 113196        long subtrahendRaw = subtrahend.m_rawValue;
 113197        ulong subtrahendLow = unchecked((ulong)subtrahendRaw);
 113198        ulong finalLow = unchecked(low - subtrahendLow);
 113199        if (low < subtrahendLow)
 364100            high--;
 1131101        high -= subtrahendRaw >> 63;
 102
 1131103        long finalRaw = unchecked((long)finalLow);
 1131104        if (high != finalRaw >> 63)
 105        {
 274106            result = default;
 274107            return false;
 108        }
 109
 857110        result = new Fixed64(finalRaw);
 857111        return true;
 112    }
 113
 114    /// <summary>
 115    /// Attempts to compute <c>(firstLeft + firstRight) -
 116    /// (secondLeft + secondRight)</c> without intermediate saturation.
 117    /// </summary>
 118    /// <param name="firstLeft">The first value in the minuend sum.</param>
 119    /// <param name="firstRight">The second value in the minuend sum.</param>
 120    /// <param name="secondLeft">The first value in the subtrahend sum.</param>
 121    /// <param name="secondRight">The second value in the subtrahend sum.</param>
 122    /// <param name="result">
 123    /// The exact difference when representable; otherwise,
 124    /// <see langword="default"/>.
 125    /// </param>
 126    /// <returns>
 127    /// <see langword="true"/> when the exact result is representable;
 128    /// otherwise, <see langword="false"/>.
 129    /// </returns>
 130    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 131    public static bool TrySubtractSums(
 132        Fixed64 firstLeft,
 133        Fixed64 firstRight,
 134        Fixed64 secondLeft,
 135        Fixed64 secondRight,
 136        out Fixed64 result)
 137    {
 424138        Signed192 exact = WideArithmetic.SubtractSigned192(
 424139            WideArithmetic.AddSigned192(
 424140                Signed192.Signed(firstLeft.m_rawValue),
 424141                Signed192.Signed(firstRight.m_rawValue)),
 424142            WideArithmetic.AddSigned192(
 424143                Signed192.Signed(secondLeft.m_rawValue),
 424144                Signed192.Signed(secondRight.m_rawValue)));
 424145        long raw = unchecked((long)exact.Low);
 424146        ulong extension = raw < 0L ? ulong.MaxValue : 0UL;
 424147        if (exact.High != extension || exact.Middle != extension)
 148        {
 5149            result = default;
 5150            return false;
 151        }
 152
 419153        result = new Fixed64(raw);
 419154        return true;
 155    }
 156
 157    /// <summary>
 158    /// Returns the absolute value of a signed 64-bit integer as an unsigned 64-bit magnitude,
 159    /// safely handling long.MinValue.
 160    /// </summary>
 161    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 162    internal static ulong AbsToUInt64(long value)
 163    {
 10126901164        return value < 0
 10126901165            ? unchecked((ulong)(~value + 1))
 10126901166            : (ulong)value;
 167    }
 168
 169    /// <summary>
 170    /// Computes the exact unsigned 128-bit product of two 64-bit unsigned integers.
 171    /// The result is returned as hi:lo, where product = (hi &lt;&lt; 64) | lo.
 172    /// </summary>
 173    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 174    internal static void Multiply64To128(ulong a, ulong b, out ulong hi, out ulong lo)
 175    {
 176#if NET8_0_OR_GREATER
 42362001177        hi = Math.BigMul(a, b, out lo);
 178#else
 179        ulong aLo = (uint)a;
 180        ulong aHi = a >> 32;
 181        ulong bLo = (uint)b;
 182        ulong bHi = b >> 32;
 183
 184        ulong p0 = aLo * bLo;
 185        ulong p1 = aLo * bHi;
 186        ulong p2 = aHi * bLo;
 187        ulong p3 = aHi * bHi;
 188
 189        ulong middle = (p0 >> 32) + (uint)p1 + (uint)p2;
 190
 191        lo = (p0 & 0xFFFFFFFFUL) | (middle << 32);
 192        hi = p3 + (p1 >> 32) + (p2 >> 32) + (middle >> 32);
 193#endif
 42362001194    }
 195
 196    /// <summary>
 197    /// Determines whether the Euclidean magnitude of up to four fixed-point components
 198    /// fits in the positive <see cref="Fixed64"/> range.
 199    /// </summary>
 200    internal static bool IsMagnitudeRepresentable(Fixed64 x, Fixed64 y, Fixed64 z, Fixed64 w)
 201    {
 67202        GetMagnitudeSquaredWords(x, y, z, w, out ulong overflow, out ulong sumHi, out ulong sumLo);
 203
 204        // (long.MaxValue)^2 = 0x3FFF_FFFF_FFFF_FFFF_0000_0000_0000_0001.
 67205        return overflow == 0UL
 67206            && (sumHi < 0x3FFF_FFFF_FFFF_FFFFUL
 67207                || (sumHi == 0x3FFF_FFFF_FFFF_FFFFUL && sumLo <= 1UL));
 208    }
 209
 210    /// <summary>
 211    /// Returns the exactly rounded Euclidean magnitude of up to four Q32.32
 212    /// components, saturating only when the rounded positive result does not fit.
 213    /// </summary>
 214    internal static Fixed64 GetRoundedMagnitude(Fixed64 x, Fixed64 y, Fixed64 z, Fixed64 w)
 215    {
 20028216        GetMagnitudeSquaredWords(x, y, z, w, out ulong overflow, out ulong sumHi, out ulong sumLo);
 20028217        if (overflow != 0UL)
 1218            return MaxValue;
 20027219        if ((sumHi | sumLo) == 0UL)
 1220            return Zero;
 221
 20026222        ulong root = GetFloorSquareRoot(sumHi, sumLo, out _, out ulong remainderLo);
 20026223        if (root > long.MaxValue)
 6911224            return MaxValue;
 225
 13115226        if (remainderLo > root)
 227        {
 6630228            if (root == long.MaxValue)
 1229                return MaxValue;
 230
 6629231            root++;
 232        }
 233
 13114234        return FromRaw((long)root);
 235    }
 236
 237    /// <summary>
 238    /// Compares exact squared magnitudes without projecting their sums back into Q32.32.
 239    /// </summary>
 240    internal static int CompareMagnitudeSquared(
 241        Fixed64 leftX,
 242        Fixed64 leftY,
 243        Fixed64 leftZ,
 244        Fixed64 leftW,
 245        Fixed64 rightX,
 246        Fixed64 rightY,
 247        Fixed64 rightZ,
 248        Fixed64 rightW)
 249    {
 11250        GetMagnitudeSquaredWords(leftX, leftY, leftZ, leftW, out ulong leftOverflow, out ulong leftHi, out ulong leftLo)
 11251        GetMagnitudeSquaredWords(rightX, rightY, rightZ, rightW, out ulong rightOverflow, out ulong rightHi, out ulong r
 252
 11253        if (leftOverflow != rightOverflow)
 2254            return leftOverflow < rightOverflow ? -1 : 1;
 9255        if (leftHi != rightHi)
 4256            return leftHi < rightHi ? -1 : 1;
 5257        if (leftLo != rightLo)
 2258            return leftLo < rightLo ? -1 : 1;
 3259        return 0;
 260    }
 261
 262    private static void GetMagnitudeSquaredWords(
 263        Fixed64 x,
 264        Fixed64 y,
 265        Fixed64 z,
 266        Fixed64 w,
 267        out ulong overflow,
 268        out ulong sumHi,
 269        out ulong sumLo)
 270    {
 20117271        overflow = 0UL;
 20117272        sumHi = 0UL;
 20117273        sumLo = 0UL;
 20117274        AddMagnitudeSquare(x.m_rawValue, ref overflow, ref sumHi, ref sumLo);
 20117275        AddMagnitudeSquare(y.m_rawValue, ref overflow, ref sumHi, ref sumLo);
 20117276        AddMagnitudeSquare(z.m_rawValue, ref overflow, ref sumHi, ref sumLo);
 20117277        AddMagnitudeSquare(w.m_rawValue, ref overflow, ref sumHi, ref sumLo);
 20117278    }
 279
 280    private static ulong GetFloorSquareRoot(
 281        ulong valueHi,
 282        ulong valueLo,
 283        out ulong remainderHi,
 284        out ulong remainderLo)
 285    {
 20026286        int highestBit = valueHi != 0UL
 20026287            ? 127 - CountLeadingZeroes(valueHi)
 20026288            : 63 - CountLeadingZeroes(valueLo);
 20026289        int pairIndex = highestBit >> 1;
 20026290        ulong root = 0UL;
 20026291        remainderHi = 0UL;
 20026292        remainderLo = 0UL;
 293
 2013380294        for (; pairIndex >= 0; pairIndex--)
 295        {
 996677296            ulong pair = pairIndex >= 32
 996677297                ? (valueHi >> ((pairIndex - 32) << 1)) & 3UL
 996677298                : (valueLo >> (pairIndex << 1)) & 3UL;
 299
 996677300            remainderHi = (remainderHi << 2) | (remainderLo >> 62);
 996677301            remainderLo = (remainderLo << 2) | pair;
 996677302            root <<= 1;
 303
 996677304            ulong candidateHi = root >> 63;
 996677305            ulong candidateLo = (root << 1) | 1UL;
 996677306            if (remainderHi < candidateHi
 996677307                || (remainderHi == candidateHi && remainderLo < candidateLo))
 308            {
 309                continue;
 310            }
 311
 504782312            ulong previousRemainderLo = remainderLo;
 504782313            remainderLo -= candidateLo;
 504782314            remainderHi -= candidateHi + (previousRemainderLo < candidateLo ? 1UL : 0UL);
 504782315            root++;
 316        }
 317
 20026318        return root;
 319    }
 320
 321    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 322    private static void AddMagnitudeSquare(
 323        long component,
 324        ref ulong overflow,
 325        ref ulong sumHi,
 326        ref ulong sumLo)
 327    {
 80468328        ulong magnitude = AbsToUInt64(component);
 80468329        Multiply64To128(magnitude, magnitude, out ulong squareHi, out ulong squareLo);
 330
 80468331        ulong previousLo = sumLo;
 80468332        sumLo += squareLo;
 80468333        ulong addHi = squareHi + (sumLo < previousLo ? 1UL : 0UL);
 80468334        ulong previousHi = sumHi;
 80468335        sumHi += addHi;
 80468336        if (sumHi < previousHi)
 6337            overflow++;
 80468338    }
 339
 340    /// <summary>
 341    /// Shifts the unsigned 128-bit value (hi:lo) right by <paramref name="shift"/> bits,
 342    /// applying round-half-to-even to the discarded bits.
 343    /// </summary>
 344    /// <param name="hi">Upper 64 bits of the 128-bit value.</param>
 345    /// <param name="lo">Lower 64 bits of the 128-bit value.</param>
 346    /// <param name="shift">Number of bits to shift right. Must be in the range 1..63.</param>
 347    /// <param name="overflowed">
 348    /// True if the shifted or rounded result exceeded 64 bits.
 349    /// </param>
 350    /// <returns>
 351    /// The rounded 64-bit result of ((hi:lo) >> shift).
 352    /// </returns>
 353    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 354    private static ulong ShiftRightRoundedToEven(ulong hi, ulong lo, int shift, out bool overflowed)
 355    {
 356        // Preconditions: 1 <= shift <= 63
 357
 358        // Bits above the low 64 result bits must be preserved as saturation state
 359        // before the lower projection can wrap back into range.
 3697833360        overflowed = (hi >> shift) != 0UL;
 361
 362        // Integer part after shifting right by 'shift':
 363        // result = ((hi << (64 - shift)) | (lo >> shift))
 3697833364        ulong result = (hi << (64 - shift)) | (lo >> shift);
 365
 366        // Discarded remainder bits are the low 'shift' bits of lo.
 3697833367        ulong remainderMask = (1UL << shift) - 1UL;
 3697833368        ulong remainder = lo & remainderMask;
 369
 370        // Halfway value among the discarded bits.
 3697833371        ulong half = 1UL << (shift - 1);
 372
 373        // Round-half-to-even:
 374        // - round up if remainder > half
 375        // - if exactly half, round so final result is even
 3697833376        bool shouldRoundUp =
 3697833377            remainder > half ||
 3697833378            (remainder == half && (result & 1UL) != 0);
 379
 3697833380        if (shouldRoundUp)
 381        {
 217498382            ulong incremented = result + 1UL;
 217498383            overflowed |= incremented < result;
 217498384            result = incremented;
 385        }
 386
 3697833387        return result;
 388    }
 389
 390    /// <summary>
 391    /// Divides unsigned raw magnitudes and applies the requested result sign.
 392    /// </summary>
 393    /// <remarks>
 394    /// The divisor magnitude must be in the range 1 through 2^63, inclusive, matching the
 395    /// unsigned magnitude of any signed raw value. This bound keeps remainder doubling within
 396    /// <see cref="ulong"/>. The quotient is calculated with one guard bit and rounded to the
 397    /// nearest even raw value before final saturation.
 398    /// </remarks>
 399    internal static Fixed64 DivideMagnitude(
 400        ulong dividendMagnitude,
 401        ulong divisorMagnitude,
 402        bool negative)
 403    {
 474601404        ulong remainder = dividendMagnitude;
 474601405        ulong divider = divisorMagnitude;
 474601406        ulong quotient = 0UL;
 474601407        int bitPos = FixedMath.SHIFT_AMOUNT_I + 1;
 408
 409        // If the divider is divisible by 2^n, take advantage of it.
 3998980410        while ((divider & 0xF) == 0 && bitPos >= 4)
 411        {
 3524379412            divider >>= 4;
 3524379413            bitPos -= 4;
 414        }
 415
 946906416        while (remainder != 0 && bitPos >= 0)
 417        {
 472323418            int shift = CountLeadingZeroes(remainder);
 472323419            if (shift > bitPos)
 411708420                shift = bitPos;
 421
 472323422            remainder <<= shift;
 472323423            bitPos -= shift;
 424
 472323425            ulong div = remainder / divider;
 472323426            remainder %= divider;
 472323427            quotient += div << bitPos;
 428
 472323429            if ((div & ~(ulong.MaxValue >> bitPos)) != 0)
 18430                return negative ? MinValue : MaxValue;
 431
 472305432            remainder <<= 1;
 472305433            --bitPos;
 434        }
 435
 474583436        ulong magnitude = RoundGuardedQuotientToEven(
 474583437            quotient,
 474583438            remainder != 0UL,
 474583439            out bool roundedOverflow);
 440
 474583441        if (roundedOverflow)
 2442            return negative ? MinValue : MaxValue;
 443
 474581444        long result = (long)magnitude;
 474581445        return new Fixed64(negative ? -result : result);
 446    }
 447
 448    /// <summary>
 449    /// Removes the low guard bit and rounds the retained quotient to even.
 450    /// </summary>
 451    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 452    internal static ulong RoundGuardedQuotientToEven(
 453        ulong guardedQuotient,
 454        bool hasTrailingRemainder,
 455        out bool overflowed)
 456    {
 523430457        bool shouldRoundUp = (guardedQuotient & 1UL) != 0UL
 523430458            && (hasTrailingRemainder || (guardedQuotient & 2UL) != 0UL);
 459
 523430460        if (!shouldRoundUp)
 461        {
 502377462            overflowed = false;
 502377463            return guardedQuotient >> 1;
 464        }
 465
 21053466        ulong roundedGuardedQuotient = guardedQuotient + 1UL;
 21053467        overflowed = roundedGuardedQuotient == 0UL;
 21053468        return overflowed ? 1UL << 63 : roundedGuardedQuotient >> 1;
 469    }
 470}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Numerics/Scalars/Fixed64.Conversions.cs

#LineLine coverage
 1//=======================================================================
 2// Fixed64.Conversions.cs
 3//=======================================================================
 4// MIT License, Copyright (c) 2024–present David Oravsky (mrdav30)
 5// See LICENSE file in the project root for full license information.
 6//=======================================================================
 7
 8using System;
 9using System.Globalization;
 10using System.Runtime.CompilerServices;
 11
 12namespace FixedMathSharp;
 13
 14/// <content>
 15/// Conversion operators between Fixed64 and other numeric types (int, long, float, double, decimal, etc.).
 16/// </content>
 17public partial struct Fixed64
 18{
 19    #region Explicit and Implicit Conversions
 20
 21    /// <summary>
 22    /// Converts a 64-bit signed integer to a Fixed64 value using explicit casting.
 23    /// </summary>
 24    /// <remarks>
 25    /// The conversion interprets the input value as the integer part of the fixed-point number and saturates
 26    /// to <see cref="MinValue"/> or <see cref="MaxValue"/> when the integer cannot fit in Q32.32 value space.
 27    /// Use <see cref="FromRaw(long)"/> when a raw fixed-point payload is required.
 28    /// </remarks>
 29    /// <param name="value">The 64-bit signed integer to convert to a Fixed64 value.</param>
 30    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 31    public static explicit operator Fixed64(long value)
 32    {
 1633        if (value > int.MaxValue)
 234            return MaxValue;
 35
 1436        if (value < int.MinValue)
 237            return MinValue;
 38
 1239        return FromRaw(value << FixedMath.SHIFT_AMOUNT_I);
 40    }
 41
 42    /// <summary>
 43    /// Converts a Fixed64 value to a 64-bit signed integer by discarding the fractional part.
 44    /// </summary>
 45    /// <remarks>
 46    /// The conversion truncates any fractional component.
 47    /// The result represents the integer portion of the Fixed64 value.</remarks>
 48    /// <param name="value">The Fixed64 value to convert to a long integer.</param>
 49    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 50    public static explicit operator long(Fixed64 value)
 51    {
 652        return value > Zero
 653            ? (long)(FixedMath.Floor(value).m_rawValue >> FixedMath.SHIFT_AMOUNT_I)
 654            : (long)(FixedMath.Ceil(value).m_rawValue >> FixedMath.SHIFT_AMOUNT_I);
 55    }
 56
 57    /// <summary>
 58    /// Defines an explicit conversion from a 32-bit signed integer to a Fixed64 value.
 59    /// </summary>
 60    /// <param name="value">The 32-bit signed integer to convert to a Fixed64 value.</param>
 61    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 62    public static explicit operator Fixed64(int value)
 63    {
 75766664        return new Fixed64(value);
 65    }
 66
 67    /// <summary>
 68    /// Converts a Fixed64 value to a 32-bit signed integer by discarding the fractional part.
 69    /// </summary>
 70    /// <remarks>
 71    /// The conversion truncates any fractional component.
 72    /// The result is equivalent to rounding toward zero.
 73    /// </remarks>
 74    /// <param name="value">The Fixed64 value to convert to an integer.</param>
 75    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 76    public static explicit operator int(Fixed64 value)
 77    {
 1878        return value > Zero
 1879            ? (int)(FixedMath.Floor(value).m_rawValue >> FixedMath.SHIFT_AMOUNT_I)
 1880            : (int)(FixedMath.Ceil(value).m_rawValue >> FixedMath.SHIFT_AMOUNT_I);
 81    }
 82
 83    /// <summary>
 84    /// Defines an explicit conversion from a single-precision floating-point value to a Fixed64 instance.
 85    /// </summary>
 86    /// <param name="value">The single-precision floating-point value to convert to Fixed64.</param>
 87    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 88    public static explicit operator Fixed64(float value)
 89    {
 590        return FromDouble(value);
 91    }
 92
 93    /// <summary>
 94    /// Converts a Fixed64 value to its equivalent single-precision floating-point representation.
 95    /// </summary>
 96    /// <remarks>
 97    /// This conversion may result in a loss of precision if the Fixed64 value cannot be exactly  represented as a float
 98    /// </remarks>
 99    /// <param name="value">The Fixed64 value to convert to a float.</param>
 100    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 101    public static explicit operator float(Fixed64 value)
 102    {
 1103        return value.m_rawValue * FixedMath.SCALE_FACTOR_F;
 104    }
 105
 106    /// <summary>
 107    /// Defines an explicit conversion from a double-precision floating-point number to a Fixed64 value.
 108    /// </summary>
 109    /// <remarks>
 110    /// This conversion may result in loss of precision if the double value cannot be exactly represented as a Fixed64.
 111    /// </remarks>
 112    /// <param name="value">The double-precision floating-point number to convert to a Fixed64 value.</param>
 113    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 114    public static explicit operator Fixed64(double value)
 115    {
 7116        return FromDouble(value);
 117    }
 118
 119    /// <summary>
 120    /// Converts a Fixed64 value to its equivalent double-precision floating-point representation.
 121    /// </summary>
 122    /// <remarks>
 123    /// This conversion may result in a loss of precision if the Fixed64 value cannot be exactly represented as a double
 124    /// </remarks>
 125    /// <param name="value">The Fixed64 value to convert to a double.</param>
 126    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 127    public static explicit operator double(Fixed64 value)
 128    {
 130588129        return value.m_rawValue * FixedMath.SCALE_FACTOR_D;
 130    }
 131
 132    /// <summary>
 133    /// Defines an explicit conversion from a decimal value to a Fixed64 instance.
 134    /// </summary>
 135    /// <param name="value">The decimal value to convert to a Fixed64 instance.</param>
 136    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 137    public static explicit operator Fixed64(decimal value)
 138    {
 4139        return FromDecimal(value);
 140    }
 141
 142    /// <summary>
 143    /// Converts a Fixed64 value to its decimal representation.
 144    /// </summary>
 145    /// <remarks>
 146    /// This operator provides an explicit conversion from Fixed64 to decimal, preserving the numeric value as closely a
 147    /// Use this conversion when precise decimal arithmetic is required.
 148    /// </remarks>
 149    /// <param name="value">The Fixed64 value to convert to decimal.</param>
 150    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 151    public static explicit operator decimal(Fixed64 value)
 152    {
 1153        return value.m_rawValue * FixedMath.SCALE_FACTOR_M;
 154    }
 155
 156    #endregion
 157    #region Conversion
 158
 159    /// <summary>
 160    /// Returns the string representation of this Fixed64 instance.
 161    /// </summary>
 162    /// <remarks>
 163    /// Up to 10 decimal places.
 164    /// </remarks>
 165    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 162166    public override string ToString() => ((double)this).ToString(CultureInfo.InvariantCulture);
 167
 168    /// <summary>
 169    /// Converts the numeric value of the current Fixed64 object to its equivalent string representation.
 170    /// </summary>
 171    /// <param name="format">A format specification that governs how the current Fixed64 object is converted.</param>
 172    /// <returns>The string representation of the value of the current Fixed64 object.</returns>
 173    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1174    public string ToString(string? format) => ToString(format, CultureInfo.InvariantCulture);
 175
 176    /// <summary>
 177    /// Converts the numeric value of the current Fixed64 object to its equivalent string representation.
 178    /// </summary>
 179    /// <param name="format">A format specification that governs how the current Fixed64 object is converted.</param>
 180    /// <param name="formatProvider">The provider to use for culture-specific formatting information.</param>
 181    /// <returns>The string representation of the value of the current Fixed64 object.</returns>
 182    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 183    public string ToString(string? format, IFormatProvider? formatProvider) =>
 7184        ((double)this).ToString(format, formatProvider ?? CultureInfo.InvariantCulture);
 185
 186    /// <summary>
 187    /// Formats the value into the provided destination buffer.
 188    /// </summary>
 189    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 190    public bool TryFormat(
 191        Span<char> destination,
 192        out int charsWritten,
 193        ReadOnlySpan<char> format,
 194        IFormatProvider? provider)
 195    {
 85586196        return FixedDiagnosticsFormatter.TryFormat(this, destination, out charsWritten, format, provider);
 197    }
 198
 199    /// <summary>
 200    /// Parses a decimal string to create a Fixed64 instance.
 201    /// </summary>
 202    /// <param name="s">The string representation of the Fixed64 value.</param>
 203    /// <returns>The parsed Fixed64 value.</returns>
 6204    public static Fixed64 Parse(string s) => Parse(s, CultureInfo.InvariantCulture);
 205
 206    /// <summary>
 207    /// Parses a decimal string to create a Fixed64 instance.
 208    /// </summary>
 209    /// <param name="s">The string representation of the Fixed64 value.</param>
 210    /// <param name="formatProvider">The provider to use for culture-specific parsing information.</param>
 211    /// <returns>The parsed Fixed64 value.</returns>
 212    public static Fixed64 Parse(string s, IFormatProvider? formatProvider)
 213    {
 9214        if (s is null)
 1215            throw new ArgumentNullException(nameof(s));
 216
 8217        if (!decimal.TryParse(s, NumberStyles.Float | NumberStyles.AllowThousands, formatProvider ?? CultureInfo.Invaria
 3218            throw new FormatException($"Invalid format: {s}");
 219
 5220        return FromDecimal(value);
 221    }
 222
 223    /// <summary>
 224    /// Parses a raw Q32.32 payload string to create a Fixed64 instance.
 225    /// </summary>
 226    /// <param name="s">The raw fixed-point payload.</param>
 227    /// <returns>The parsed Fixed64 value.</returns>
 228    public static Fixed64 ParseRaw(string s)
 229    {
 5230        if (s is null)
 1231            throw new ArgumentNullException(nameof(s));
 232
 4233        if (!long.TryParse(s, NumberStyles.Integer, CultureInfo.InvariantCulture, out long rawValue))
 2234            throw new FormatException($"Invalid format: {s}");
 235
 2236        return FromRaw(rawValue);
 237    }
 238
 239    /// <summary>
 240    /// Tries to parse a decimal string to create a Fixed64 instance.
 241    /// </summary>
 242    /// <param name="s">The string representation of the Fixed64 value.</param>
 243    /// <param name="result">The parsed Fixed64 value.</param>
 244    /// <returns>True if parsing succeeded; otherwise, false.</returns>
 5245    public static bool TryParse(string? s, out Fixed64 result) => TryParse(s, CultureInfo.InvariantCulture, out result);
 246
 247    /// <summary>
 248    /// Tries to parse a decimal string to create a Fixed64 instance.
 249    /// </summary>
 250    /// <param name="s">The string representation of the Fixed64 value.</param>
 251    /// <param name="formatProvider">The provider to use for culture-specific parsing information.</param>
 252    /// <param name="result">The parsed Fixed64 value.</param>
 253    /// <returns>True if parsing succeeded; otherwise, false.</returns>
 254    public static bool TryParse(string? s, IFormatProvider? formatProvider, out Fixed64 result)
 255    {
 7256        result = Zero;
 7257        if (string.IsNullOrEmpty(s))
 1258            return false;
 259
 6260        if (!decimal.TryParse(s, NumberStyles.Float | NumberStyles.AllowThousands, formatProvider ?? CultureInfo.Invaria
 2261            return false;
 262
 263        try
 264        {
 4265            result = FromDecimal(value);
 3266            return true;
 267        }
 1268        catch (OverflowException)
 269        {
 1270            return false;
 271        }
 4272    }
 273
 274    /// <summary>
 275    /// Tries to parse a raw Q32.32 payload string to create a Fixed64 instance.
 276    /// </summary>
 277    /// <param name="s">The raw fixed-point payload.</param>
 278    /// <param name="result">The parsed Fixed64 value.</param>
 279    /// <returns>True if parsing succeeded; otherwise, false.</returns>
 280    public static bool TryParseRaw(string? s, out Fixed64 result)
 281    {
 4282        result = Zero;
 4283        if (string.IsNullOrEmpty(s))
 1284            return false;
 285
 3286        if (!long.TryParse(s, NumberStyles.Integer, CultureInfo.InvariantCulture, out long rawValue))
 1287            return false;
 288
 2289        result = FromRaw(rawValue);
 2290        return true;
 291    }
 292
 293    /// <summary>
 294    /// Creates a Fixed64 from a raw long value.
 295    /// </summary>
 296    /// <remarks>
 297    /// This method interprets <paramref name="rawValue"/> as an already-scaled Q32.32 payload.
 298    /// Use the integer constructors or explicit numeric conversions for user-facing integer values.
 299    /// </remarks>
 300    /// <param name="rawValue">The raw fixed-point payload.</param>
 301    /// <returns>A Fixed64 representing the raw value.</returns>
 302    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1026666303    public static Fixed64 FromRaw(long rawValue) => new(rawValue);
 304
 305
 306    /// <summary>
 307    /// Converts a Fixed64's RawValue (Int64) into an integer by discarding the fractional part.
 308    /// </summary>
 309    /// <param name="value">The Fixed64 value to convert.</param>
 310    /// <returns>The integer representation of the Fixed64 value.</returns>
 311    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1312    public static int ToInt(Fixed64 value) => (int)(value.m_rawValue >> FixedMath.SHIFT_AMOUNT_I);
 313
 314    /// <summary>
 315    /// Constructs a Fixed64 from a double-precision floating-point value.
 316    /// </summary>
 317    /// <remarks>
 318    /// The value is multiplied by the scaling factor (2^SHIFT_AMOUNT) and rounded to the nearest
 319    /// raw integer using midpoint-to-even rounding. Non-finite values throw an
 320    /// <see cref="ArgumentOutOfRangeException"/>. Finite values outside the Q32.32 range throw an
 321    /// <see cref="OverflowException"/>.
 322    /// </remarks>
 323    /// <param name="value">Double value to convert.</param>
 324    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 325    public static Fixed64 FromDouble(double value)
 326    {
 42624327        if (double.IsNaN(value) || double.IsInfinity(value))
 10328            throw new ArgumentOutOfRangeException(nameof(value), value, "Value must be finite.");
 329
 42614330        double rawValue = Math.Round(value * FixedMath.ONE_L, MidpointRounding.ToEven);
 42614331        if (rawValue < FixedMath.MIN_RAW_D || rawValue >= FixedMath.MAX_RAW_EXCLUSIVE_D)
 7332            throw new OverflowException($"{value} is outside the representable range of {nameof(Fixed64)}.");
 333
 42607334        return FromRaw((long)rawValue);
 335    }
 336
 337    /// <summary>
 338    /// Constructs a Fixed64 from a decimal value.
 339    /// </summary>
 340    /// <remarks>
 341    /// The value is multiplied by the scaling factor (2^SHIFT_AMOUNT) and rounded to the nearest
 342    /// raw integer using midpoint-to-even rounding. Values outside the Q32.32 range throw an
 343    /// <see cref="OverflowException"/>.
 344    /// </remarks>
 345    /// <param name="value">Decimal value to convert.</param>
 346    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 347    public static Fixed64 FromDecimal(decimal value)
 348    {
 19349        decimal rawValue = decimal.Round(value * FixedMath.ONE_L, 0, MidpointRounding.ToEven);
 350
 19351        if (rawValue < long.MinValue || rawValue > long.MaxValue)
 4352            throw new OverflowException($"{value} is outside the representable range of {nameof(Fixed64)}.");
 353
 15354        return FromRaw((long)rawValue);
 355    }
 356
 357    /// <summary>
 358    /// Creates a Fixed64 from a fractional number.
 359    /// </summary>
 360    /// <remarks>
 361    /// The quotient is converted through <see cref="FromDouble(double)"/>, so non-finite results
 362    /// throw <see cref="ArgumentOutOfRangeException"/> and finite results outside the Q32.32 range
 363    /// throw <see cref="OverflowException"/>.
 364    /// </remarks>
 365    /// <param name="numerator">The numerator of the fraction.</param>
 366    /// <param name="denominator">The denominator of the fraction.</param>
 367    /// <returns>A Fixed64 representing the fraction.</returns>
 368    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 3129369    public static Fixed64 FromFraction(double numerator, double denominator) => FromDouble(numerator / denominator);
 370
 371    /// <summary>
 372    /// Converts a Fixed64s RawValue (Int64) into a double
 373    /// </summary>
 374    /// <param name="f1"></param>
 375    /// <returns></returns>
 19163376    public static double ToDouble(long f1) => f1 * FixedMath.SCALE_FACTOR_D;
 377
 378    /// <summary>
 379    /// Converts a Fixed64s RawValue (Int64) into a float
 380    /// </summary>
 381    /// <param name="f1"></param>
 382    /// <returns></returns>
 1383    public static float ToFloat(long f1) => f1 * FixedMath.SCALE_FACTOR_F;
 384
 385    /// <summary>
 386    /// Converts a Fixed64s RawValue (Int64) into a decimal
 387    /// </summary>
 388    /// <param name="f1"></param>
 389    /// <returns></returns>
 1390    public static decimal ToDecimal(long f1) => f1 * FixedMath.SCALE_FACTOR_M;
 391
 392    #endregion
 393}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Numerics/Scalars/Fixed64.cs

#LineLine coverage
 1//=======================================================================
 2// Fixed64.cs
 3//=======================================================================
 4// MIT License, Copyright (c) 2024–present David Oravsky (mrdav30)
 5// See LICENSE file in the project root for full license information.
 6//=======================================================================
 7
 8using System;
 9using System.Collections.Generic;
 10using System.Globalization;
 11using System.Runtime.CompilerServices;
 12using System.Text.Json.Serialization;
 13using MemoryPack;
 14
 15namespace FixedMathSharp;
 16
 17/// <summary>
 18/// Represents a Q(64-SHIFT_AMOUNT).SHIFT_AMOUNT fixed-point number.
 19/// Provides high precision for fixed-point arithmetic where SHIFT_AMOUNT bits
 20/// are used for the fractional part and (64 - SHIFT_AMOUNT) bits for the integer part.
 21/// The precision is determined by SHIFT_AMOUNT, which defines the resolution of fractional values.
 22/// </summary>
 23[Serializable]
 24[MemoryPackable]
 25public partial struct Fixed64 : IEquatable<Fixed64>, IComparable<Fixed64>, IEqualityComparer<Fixed64>, IFormattable
 26#if NET8_0_OR_GREATER
 27    , ISpanFormattable
 28#endif
 29{
 30    #region Static Fields
 31
 32    /// <inheritdoc cref="FixedMath.MAX_VALUE_L" />
 5768333    public static Fixed64 MaxValue => new(FixedMath.MAX_VALUE_L);
 34    /// <inheritdoc cref="FixedMath.MIN_VALUE_L" />
 457135    public static Fixed64 MinValue => new(FixedMath.MIN_VALUE_L);
 36
 37    /// <inheritdoc cref="FixedMath.ONE_L" />
 110028438    public static Fixed64 One => new(FixedMath.ONE_L);
 39    /// <summary>
 40    /// Represents the value -1 shifted left by the number of bits specified by SHIFT_AMOUNT_I.
 41    /// </summary>
 142    public static Fixed64 NegOne => -One;
 43    /// <summary>
 44    /// Represents the value 2 shifted left by the number of bits specified by SHIFT_AMOUNT_I.
 45    /// </summary>
 35609946    public static Fixed64 Two => One * 2;
 47    /// <summary>
 48    /// Represents the value 3 shifted left by the number of bits specified by SHIFT_AMOUNT_I.
 49    /// </summary>
 4350    public static Fixed64 Three => One * 3;
 51    /// <summary>
 52    /// Represents the value 0.5 shifted left by the number of bits specified by SHIFT_AMOUNT_I.
 53    /// </summary>
 4458554    public static Fixed64 Half => One / 2;
 55    /// <summary>
 56    /// Represents the value 0.25 shifted left by the number of bits specified by SHIFT_AMOUNT_I.
 57    /// </summary>
 6358    public static Fixed64 Quarter => One / 4;
 59    /// <summary>
 60    /// Represents the value 0.125 shifted left by the number of bits specified by SHIFT_AMOUNT_I.
 61    /// </summary>
 2562    public static Fixed64 Eighth => One / 8;
 63    /// <summary>
 64    /// Represents the value 0 as a fixed-point number.
 65    /// </summary>
 117269666    public static Fixed64 Zero => new(0);
 67
 68    /// <inheritdoc cref="FixedMath.PI_LONG" />
 60896169    public static Fixed64 Pi => new(FixedMath.PI_LONG);
 70    /// <summary>
 71    /// Represents the mathematical constant 2Ï€ as a fixed-point value.
 72    /// </summary>
 73    /// <remarks>This value is commonly used to represent a full rotation in radians.</remarks>
 14694274    public static Fixed64 TwoPi => Pi * Two;
 75    /// <summary>
 76    /// Represents the mathematical constant Ï€ divided by 2 as a fixed-point value.
 77    /// </summary>
 78    /// <remarks>This value is used where Ï€/2 is required.</remarks>
 21346579    public static Fixed64 HalfPi => Pi / new Fixed64(2);
 80    /// <summary>
 81    /// Represents the mathematical constant Ï€ divided by 3 as a fixed-point value.
 82    /// </summary>
 1283    public static Fixed64 PiOver3 => Pi / new Fixed64(3);
 84    /// <summary>
 85    /// Represents the mathematical constant Ï€ divided by 4 as a fixed-point value.
 86    /// </summary>
 4499287    public static Fixed64 PiOver4 => Pi / new Fixed64(4);
 88    /// <summary>
 89    /// Represents the mathematical constant Ï€ divided by 6 as a fixed-point value.
 90    /// </summary>
 11691    public static Fixed64 PiOver6 => Pi / new Fixed64(6);
 92    /// <summary>
 93    /// Represents the multiplicative inverse of the mathematical constant Ï€ (pi) as a fixed-point value.
 94    /// </summary>
 195    public static Fixed64 InvertedPi => One / Pi;
 96    /// <summary>
 97    /// Represents the fixed-point value for 180.
 98    /// </summary>
 122899    public static Fixed64 OneEighty => new(180);
 100    /// <summary>
 101    /// Degrees to radians conversion factor (Ï€ / 180)
 102    /// </summary>
 10103    public static Fixed64 Deg2Rad => Pi / OneEighty;
 104    /// <summary>
 105    /// Radians to degrees conversion factor (180 / Ï€)
 106    /// </summary>
 1107    public static Fixed64 Rad2Deg => OneEighty / Pi;
 108
 109    /// <inheritdoc cref="FixedMath.LN2_LONG" />
 11110    public static Fixed64 Ln2 => new(FixedMath.LN2_LONG);
 111    /// <summary>
 112    /// Represents the maximum value for the base-2 logarithm that can be represented by a Fixed64 instance.
 113    /// </summary>
 114    /// <remarks>
 115    /// This constant is useful when performing logarithmic calculations to ensure results
 116    /// do not exceed the representable range of the Fixed64 type.
 117    /// </remarks>
 2118    public static Fixed64 Log2Max => new(63L * FixedMath.ONE_L);
 119    /// <summary>
 120    /// Represents the minimum base-2 logarithm value supported by the Fixed64 type.
 121    /// </summary>
 122    /// <remarks>
 123    /// This constant can be used as a lower bound when performing logarithmic calculations
 124    /// with Fixed64 values to prevent underflow or invalid results.
 125    /// </remarks>
 1126    public static Fixed64 Log2Min => new(-64L * FixedMath.ONE_L);
 127
 5128    internal static Fixed64 PadeA1 => new(FixedMath.PADE_A1_LONG);
 5129    internal static Fixed64 PadeA2 => new(FixedMath.PADE_A2_LONG);
 130
 23572131    internal static Fixed64 SinCoeff3 => new(FixedMath.SIN_COEFF_3_LONG); // Tuned minimax coefficient.
 23572132    internal static Fixed64 SinCoeff5 => new(FixedMath.SIN_COEFF_5_LONG); // Tuned minimax coefficient.
 23572133    internal static Fixed64 SinCoeff7 => new(FixedMath.SIN_COEFF_7_LONG); // Tuned minimax coefficient.
 20981134    internal static Fixed64 CosCoeff2 => new(FixedMath.COS_COEFF_2_LONG); // 1/2!
 20981135    internal static Fixed64 CosCoeff4 => new(FixedMath.COS_COEFF_4_LONG); // 1/4!
 20981136    internal static Fixed64 CosCoeff6 => new(FixedMath.COS_COEFF_6_LONG); // 1/6!
 20981137    internal static Fixed64 CosCoeff8 => new(FixedMath.COS_COEFF_8_LONG); // 1/8!
 138
 139    /// <inheritdoc cref="FixedMath.MIN_INCREMENT_L" />
 4613140    public static Fixed64 MinIncrement => new(FixedMath.MIN_INCREMENT_L);
 141    /// <inheritdoc cref="FixedMath.DEFAULT_TOLERANCE_L" />
 159626142    public static Fixed64 Epsilon => new(FixedMath.DEFAULT_TOLERANCE_L);
 143
 144    #endregion
 145    #region Fields
 146
 147    /// <summary>
 148    /// The underlying raw long value representing the fixed-point number.
 149    /// </summary>
 150    [JsonInclude]
 151    [MemoryPackInclude]
 152    public long m_rawValue;
 153
 154    #endregion
 155    #region Constructors
 156
 157    /// <summary>
 158    /// Internal constructor for a Fixed64 from a raw long value.
 159    /// </summary>
 160    /// <param name="m_rawValue">Raw long value representing the fixed-point number.</param>
 161    [JsonConstructor]
 12782578162    internal Fixed64(long m_rawValue) => this.m_rawValue = m_rawValue;
 163
 164    /// <summary>
 165    /// Constructs a Fixed64 from an integer, with the fractional part set to zero.
 166    /// </summary>
 167    /// <param name="value">Integer value to convert to </param>
 4386242168    public Fixed64(int value) : this((long)value << FixedMath.SHIFT_AMOUNT_I) { }
 169
 170    #endregion
 171    #region Methods (Instance)
 172
 173    /// <summary>
 174    /// Offsets the current Fixed64 by an integer value.
 175    /// </summary>
 176    /// <param name="x">The integer value to add.</param>
 177    /// <remarks>
 178    /// Unlike the <see cref="operator +(Fixed64, int)"/>, this method performs a simple addition of the integer value
 179    /// to the raw fixed-point representation without any overflow checks or saturation behavior.
 180    /// </remarks>
 181    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 182    public Fixed64 Offset(int x)
 183    {
 1184        return new Fixed64(m_rawValue + ((long)x << FixedMath.SHIFT_AMOUNT_I));
 185    }
 186
 187    /// <summary>
 188    /// Returns the raw value as a string.
 189    /// </summary>
 190    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1191    public string ToRawString() => m_rawValue.ToString(CultureInfo.InvariantCulture);
 192
 193    #endregion
 194}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Numerics/Scalars/Fixed64.Equality.cs

#LineLine coverage
 1//=======================================================================
 2// Fixed64.Equality.cs
 3//=======================================================================
 4// MIT License, Copyright (c) 2024–present David Oravsky (mrdav30)
 5// See LICENSE file in the project root for full license information.
 6//=======================================================================
 7
 8using System.Runtime.CompilerServices;
 9
 10namespace FixedMathSharp;
 11
 12/// <content>
 13/// Provides equality comparison, hash code generation, and ordering logic for <see cref="Fixed64"/>.
 14/// </content>
 15public partial struct Fixed64
 16{
 17    /// <summary>
 18    /// Determines whether this instance equals another object.
 19    /// </summary>
 20    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 13421    public override bool Equals(object? obj) => obj is Fixed64 other && Equals(other);
 22
 23    /// <inheritdoc/>
 24    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 102513025    public bool Equals(Fixed64 other) => m_rawValue == other.m_rawValue;
 26
 27    /// <inheritdoc/>
 28    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 529    public bool Equals(Fixed64 x, Fixed64 y) => x.Equals(y);
 30
 31    /// <inheritdoc/>
 32    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 20733    public override int GetHashCode() => m_rawValue.GetHashCode();
 34
 35    /// <inheritdoc/>
 36    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 137    public int GetHashCode(Fixed64 obj) => obj.GetHashCode();
 38
 39    /// <summary>
 40    /// Compares this instance to another
 41    /// </summary>
 42    /// <param name="other">The Fixed64 to compare with.</param>
 43    /// <returns>-1 if less than, 0 if equal, 1 if greater than other.</returns>
 4144    public int CompareTo(Fixed64 other) => m_rawValue.CompareTo(other.m_rawValue);
 45}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Numerics/Scalars/Fixed64.FusedArithmetic.cs

#LineLine coverage
 1//=======================================================================
 2// Fixed64.MultiplyAdd.cs
 3//=======================================================================
 4// MIT License, Copyright (c) 2024–present David Oravsky (mrdav30)
 5// See LICENSE file in the project root for full license information.
 6//=======================================================================
 7
 8using System;
 9using System.Runtime.CompilerServices;
 10
 11namespace FixedMathSharp;
 12
 13/// <content>
 14/// Provides fused arithmetic operations for <see cref="Fixed64"/> that combine multiple
 15/// multiplications, additions, subtractions, or divisions into a single rounding step for
 16/// improved precision and no intermediate saturation. This includes fused multiply-add
 17/// (<c>(left * right) + addend</c>), clamped multiply-subtract for two, three, and four
 18/// factor products, fused multiply-difference, and fused multiply-divide for two and
 19/// three factor numerators.
 20/// </content>
 21public partial struct Fixed64
 22{
 23    /// <summary>
 24    /// Multiplies two values, adds a third, and performs one final
 25    /// round-half-to-even conversion. An unrepresentable result saturates.
 26    /// </summary>
 27    public static Fixed64 MultiplyAdd(Fixed64 left, Fixed64 right, Fixed64 addend) =>
 1628        MultiplyAdd(left, right, addend, out _);
 29
 30    /// <summary>
 31    /// Attempts to multiply two values and add a third with one final
 32    /// round-half-to-even conversion and no intermediate saturation.
 33    /// </summary>
 34    public static bool TryMultiplyAdd(
 35        Fixed64 left,
 36        Fixed64 right,
 37        Fixed64 addend,
 38        out Fixed64 result)
 39    {
 4103940        result = MultiplyAdd(left, right, addend, out bool representable);
 4103941        if (representable)
 4068042            return true;
 43
 35944        result = default;
 35945        return false;
 46    }
 47
 48    private static Fixed64 MultiplyAdd(
 49        Fixed64 left,
 50        Fixed64 right,
 51        Fixed64 addend,
 52        out bool representable)
 53    {
 4105554        long leftRaw = left.m_rawValue;
 4105555        long rightRaw = right.m_rawValue;
 4105556        Multiply64To128(
 4105557            AbsToUInt64(leftRaw),
 4105558            AbsToUInt64(rightRaw),
 4105559            out ulong productHigh,
 4105560            out ulong productLow);
 4105561        Signed192 product = new(0UL, productHigh, productLow);
 4105562        if ((leftRaw ^ rightRaw) < 0L)
 507863            product = WideArithmetic.SubtractSigned192(default, product);
 64
 4105565        long addendRaw = addend.m_rawValue;
 4105566        ulong extension = addendRaw < 0L ? ulong.MaxValue : 0UL;
 4105567        Signed192 scaledAddend = new(
 4105568            extension,
 4105569            unchecked((ulong)(addendRaw >> FixedMath.SHIFT_AMOUNT_I)),
 4105570            unchecked((ulong)addendRaw << FixedMath.SHIFT_AMOUNT_I));
 4105571        Signed192 numerator = WideArithmetic.AddSigned192(product, scaledAddend);
 4105572        bool negative = numerator.Sign < 0;
 4105573        WideArithmetic.GetMagnitude(
 4105574            numerator,
 4105575            out _,
 4105576            out ulong numeratorMiddle,
 4105577            out ulong numeratorLow);
 78
 4105579        if ((numeratorMiddle >> FixedMath.SHIFT_AMOUNT_I) != 0UL)
 80        {
 17981            representable = false;
 17982            return negative ? MinValue : MaxValue;
 83        }
 84
 4087685        ulong quotient = (numeratorMiddle << FixedMath.SHIFT_AMOUNT_I)
 4087686            | (numeratorLow >> FixedMath.SHIFT_AMOUNT_I);
 4087687        ulong guardMask = 1UL << (FixedMath.SHIFT_AMOUNT_I - 1);
 4087688        bool guard = (numeratorLow & guardMask) != 0UL;
 4087689        bool sticky = (numeratorLow & (guardMask - 1UL)) != 0UL;
 4087690        return RoundAndApplySign(
 4087691            quotient,
 4087692            guard,
 4087693            sticky,
 4087694            negative,
 4087695            out representable);
 96    }
 97
 98    /// <summary>
 99    /// Attempts to compute <c>max(0, minuendFirst * minuendSecond - subtrahendFirst * subtrahendSecond)</c>
 100    /// with no intermediate saturation and one final round-half-to-even conversion.
 101    /// </summary>
 102    /// <param name="minuendFirst">The first factor of the minuend product.</param>
 103    /// <param name="minuendSecond">The second factor of the minuend product.</param>
 104    /// <param name="subtrahendFirst">The first factor of the subtrahend product.</param>
 105    /// <param name="subtrahendSecond">The second factor of the subtrahend product.</param>
 106    /// <param name="result">
 107    /// The nonnegative fused result; otherwise, <see langword="default"/> when a positive
 108    /// rounded result is not representable.
 109    /// </param>
 110    /// <returns>
 111    /// <see langword="true"/> when the exact difference is nonpositive or its final
 112    /// round-half-to-even result is representable; otherwise, <see langword="false"/>.
 113    /// </returns>
 114    public static bool TryMultiplySubtractClamped(
 115        Fixed64 minuendFirst,
 116        Fixed64 minuendSecond,
 117        Fixed64 subtrahendFirst,
 118        Fixed64 subtrahendSecond,
 119        out Fixed64 result)
 120    {
 169121        Signed192 difference = WideArithmetic.SubtractSigned192(
 169122            GetExactRawProduct(minuendFirst, minuendSecond),
 169123            GetExactRawProduct(subtrahendFirst, subtrahendSecond));
 169124        return TryRoundNonnegativeTwoFactorDifference(difference, out result);
 125    }
 126
 127    /// <summary>
 128    /// Attempts to compute <c>max(0, minuendFirst * minuendSecond * minuendThird - subtrahendFirst * subtrahendSecond *
 129    /// with no intermediate saturation and one final round-half-to-even conversion.
 130    /// </summary>
 131    /// <param name="minuendFirst">The first factor of the minuend product.</param>
 132    /// <param name="minuendSecond">The second factor of the minuend product.</param>
 133    /// <param name="minuendThird">The third factor of the minuend product.</param>
 134    /// <param name="subtrahendFirst">The first factor of the subtrahend product.</param>
 135    /// <param name="subtrahendSecond">The second factor of the subtrahend product.</param>
 136    /// <param name="subtrahendThird">The third factor of the subtrahend product.</param>
 137    /// <param name="result">
 138    /// The nonnegative fused result; otherwise, <see langword="default"/> when a positive
 139    /// rounded result is not representable.
 140    /// </param>
 141    /// <returns>
 142    /// <see langword="true"/> when the exact difference is nonpositive or its final
 143    /// round-half-to-even result is representable; otherwise, <see langword="false"/>.
 144    /// </returns>
 145    public static bool TryMultiplySubtractClamped(
 146        Fixed64 minuendFirst,
 147        Fixed64 minuendSecond,
 148        Fixed64 minuendThird,
 149        Fixed64 subtrahendFirst,
 150        Fixed64 subtrahendSecond,
 151        Fixed64 subtrahendThird,
 152        out Fixed64 result)
 153    {
 218154        Signed320 difference = WideArithmetic.SubtractSigned320(
 218155            WideArithmetic.MultiplySigned192(
 218156                GetExactRawProduct(minuendFirst, minuendSecond),
 218157                Signed192.Signed(minuendThird.m_rawValue)),
 218158            WideArithmetic.MultiplySigned192(
 218159                GetExactRawProduct(subtrahendFirst, subtrahendSecond),
 218160                Signed192.Signed(subtrahendThird.m_rawValue)));
 218161        return TryRoundNonnegativeThreeFactorDifference(difference, out result);
 162    }
 163
 164    /// <summary>
 165    /// Attempts to compute <c>max(0, minuendFirst * minuendSecond * minuendThird * minuendFourth - subtrahendFirst * su
 166    /// with no intermediate saturation and one final round-half-to-even conversion.
 167    /// </summary>
 168    /// <param name="minuendFirst">The first factor of the minuend product.</param>
 169    /// <param name="minuendSecond">The second factor of the minuend product.</param>
 170    /// <param name="minuendThird">The third factor of the minuend product.</param>
 171    /// <param name="minuendFourth">The fourth factor of the minuend product.</param>
 172    /// <param name="subtrahendFirst">The first factor of the subtrahend product.</param>
 173    /// <param name="subtrahendSecond">The second factor of the subtrahend product.</param>
 174    /// <param name="subtrahendThird">The third factor of the subtrahend product.</param>
 175    /// <param name="subtrahendFourth">The fourth factor of the subtrahend product.</param>
 176    /// <param name="result">
 177    /// The nonnegative fused result; otherwise, <see langword="default"/> when a positive
 178    /// rounded result is not representable.
 179    /// </param>
 180    /// <returns>
 181    /// <see langword="true"/> when the exact difference is nonpositive or its final
 182    /// round-half-to-even result is representable; otherwise, <see langword="false"/>.
 183    /// </returns>
 184    public static bool TryMultiplySubtractClamped(
 185        Fixed64 minuendFirst,
 186        Fixed64 minuendSecond,
 187        Fixed64 minuendThird,
 188        Fixed64 minuendFourth,
 189        Fixed64 subtrahendFirst,
 190        Fixed64 subtrahendSecond,
 191        Fixed64 subtrahendThird,
 192        Fixed64 subtrahendFourth,
 193        out Fixed64 result)
 194    {
 410195        Signed320 difference = WideArithmetic.SubtractSigned320(
 410196            WideArithmetic.MultiplySigned192(
 410197                GetExactRawProduct(minuendFirst, minuendSecond),
 410198                GetExactRawProduct(minuendThird, minuendFourth)),
 410199            WideArithmetic.MultiplySigned192(
 410200                GetExactRawProduct(subtrahendFirst, subtrahendSecond),
 410201                GetExactRawProduct(subtrahendThird, subtrahendFourth)));
 410202        return TryRoundNonnegativeFourFactorDifference(difference, out result);
 203    }
 204
 205    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 206    internal static Signed192 GetExactRawProduct(Fixed64 left, Fixed64 right)
 207    {
 366135208        long leftRaw = left.m_rawValue;
 366135209        long rightRaw = right.m_rawValue;
 366135210        Multiply64To128(
 366135211            AbsToUInt64(leftRaw),
 366135212            AbsToUInt64(rightRaw),
 366135213            out ulong productHigh,
 366135214            out ulong productLow);
 366135215        Signed192 product = new(0UL, productHigh, productLow);
 366135216        return (leftRaw ^ rightRaw) < 0L
 366135217            ? WideArithmetic.SubtractSigned192(default, product)
 366135218            : product;
 219    }
 220
 221    private static bool TryRoundNonnegativeTwoFactorDifference(
 222        Signed192 difference,
 223        out Fixed64 result)
 224    {
 169225        if (difference.Sign <= 0)
 226        {
 54227            result = Zero;
 54228            return true;
 229        }
 230
 115231        WideArithmetic.GetMagnitude(difference, out _, out ulong middle, out ulong low);
 115232        if ((middle >> FixedMath.SHIFT_AMOUNT_I) != 0UL)
 233        {
 28234            result = default;
 28235            return false;
 236        }
 237
 87238        return TryRoundNonnegative(
 87239            (middle << FixedMath.SHIFT_AMOUNT_I) | (low >> FixedMath.SHIFT_AMOUNT_I),
 87240            (low & (1UL << (FixedMath.SHIFT_AMOUNT_I - 1))) != 0UL,
 87241            (low & ((1UL << (FixedMath.SHIFT_AMOUNT_I - 1)) - 1UL)) != 0UL,
 87242            out result);
 243    }
 244
 245    private static bool TryRoundNonnegativeThreeFactorDifference(
 246        Signed320 difference,
 247        out Fixed64 result)
 248    {
 218249        if (difference.Sign <= 0)
 250        {
 64251            result = Zero;
 64252            return true;
 253        }
 254
 154255        WideArithmetic.GetMagnitude(
 154256            difference,
 154257            out ulong word4,
 154258            out ulong word3,
 154259            out ulong word2,
 154260            out ulong word1,
 154261            out ulong word0);
 154262        if ((word4 | word3 | word2) != 0UL)
 263        {
 40264            result = default;
 40265            return false;
 266        }
 267
 114268        return TryRoundNonnegative(
 114269            word1,
 114270            (word0 & (1UL << 63)) != 0UL,
 114271            (word0 & ((1UL << 63) - 1UL)) != 0UL,
 114272            out result);
 273    }
 274
 275    private static bool TryRoundNonnegativeFourFactorDifference(
 276        Signed320 difference,
 277        out Fixed64 result)
 278    {
 410279        if (difference.Sign <= 0)
 280        {
 169281            result = Zero;
 169282            return true;
 283        }
 284
 241285        WideArithmetic.GetMagnitude(
 241286            difference,
 241287            out _,
 241288            out ulong word3,
 241289            out ulong word2,
 241290            out ulong word1,
 241291            out ulong word0);
 241292        if (word3 != 0UL || (word2 >> FixedMath.SHIFT_AMOUNT_I) != 0UL)
 293        {
 35294            result = default;
 35295            return false;
 296        }
 297
 206298        return TryRoundNonnegative(
 206299            (word2 << FixedMath.SHIFT_AMOUNT_I) | (word1 >> FixedMath.SHIFT_AMOUNT_I),
 206300            (word1 & (1UL << (FixedMath.SHIFT_AMOUNT_I - 1))) != 0UL,
 206301            word0 != 0UL || (word1 & ((1UL << (FixedMath.SHIFT_AMOUNT_I - 1)) - 1UL)) != 0UL,
 206302            out result);
 303    }
 304
 305    private static bool TryRoundNonnegative(
 306        ulong quotient,
 307        bool guard,
 308        bool sticky,
 309        out Fixed64 result)
 310    {
 407311        result = RoundAndApplySign(quotient, guard, sticky, negative: false, out bool representable);
 407312        if (representable)
 188313            return true;
 314
 219315        result = default;
 219316        return false;
 317    }
 318
 319    /// <summary>
 320    /// Attempts to evaluate
 321    /// <c>(value - subtrahend) * firstMultiplier * secondMultiplier</c> with
 322    /// one final round-half-to-even conversion.
 323    /// </summary>
 324    /// <remarks>
 325    /// The subtraction and both products remain exact even when an intermediate
 326    /// value lies outside the public Q32.32 domain.
 327    /// </remarks>
 328    public static bool TryMultiplyDifference(
 329        Fixed64 value,
 330        Fixed64 subtrahend,
 331        Fixed64 firstMultiplier,
 332        Fixed64 secondMultiplier,
 333        out Fixed64 result)
 334    {
 12335        long valueRaw = value.m_rawValue;
 12336        long subtrahendRaw = subtrahend.m_rawValue;
 12337        bool negativeDifference = valueRaw < subtrahendRaw;
 12338        ulong differenceMagnitude = negativeDifference
 12339            ? unchecked((ulong)subtrahendRaw - (ulong)valueRaw)
 12340            : unchecked((ulong)valueRaw - (ulong)subtrahendRaw);
 12341        long firstRaw = firstMultiplier.m_rawValue;
 12342        long secondRaw = secondMultiplier.m_rawValue;
 12343        bool negative = negativeDifference
 12344            ^ (firstRaw < 0L)
 12345            ^ (secondRaw < 0L);
 346
 12347        Multiply64To128(
 12348            differenceMagnitude,
 12349            AbsToUInt64(firstRaw),
 12350            out ulong productHigh,
 12351            out ulong productLow);
 12352        Multiply64To128(
 12353            productLow,
 12354            AbsToUInt64(secondRaw),
 12355            out ulong lowProductHigh,
 12356            out ulong numeratorLow);
 12357        Multiply64To128(
 12358            productHigh,
 12359            AbsToUInt64(secondRaw),
 12360            out ulong numeratorHigh,
 12361            out ulong highProductLow);
 12362        ulong numeratorMiddle = unchecked(lowProductHigh + highProductLow);
 12363        if (numeratorMiddle < lowProductHigh)
 1364            numeratorHigh++;
 365
 12366        if (numeratorHigh != 0UL)
 367        {
 1368            result = default;
 1369            return false;
 370        }
 371
 372        const ulong GuardMask = 1UL << 63;
 11373        bool guard = (numeratorLow & GuardMask) != 0UL;
 11374        bool sticky = (numeratorLow & (GuardMask - 1UL)) != 0UL;
 11375        result = RoundAndApplySign(
 11376            numeratorMiddle,
 11377            guard,
 11378            sticky,
 11379            negative,
 11380            out bool representable);
 11381        if (representable)
 9382            return true;
 383
 2384        result = default;
 2385        return false;
 386    }
 387
 388    /// <summary>
 389    /// Attempts to multiply two values and divide by a third with one final
 390    /// round-half-to-even operation and no intermediate saturation.
 391    /// </summary>
 392    /// <param name="left">The first factor.</param>
 393    /// <param name="right">The second factor.</param>
 394    /// <param name="divisor">The divisor.</param>
 395    /// <param name="result">
 396    /// The fused result when representable; otherwise, <see langword="default"/>.
 397    /// </param>
 398    /// <returns>
 399    /// <see langword="true"/> when the divisor is nonzero and the final result is
 400    /// representable; otherwise, <see langword="false"/>.
 401    /// </returns>
 402    public static bool TryMultiplyDivide(
 403        Fixed64 left,
 404        Fixed64 right,
 405        Fixed64 divisor,
 406        out Fixed64 result)
 407    {
 2108408        result = MultiplyDivide(left, right, divisor, out bool representable);
 2108409        if (representable)
 1576410            return true;
 411
 532412        result = default;
 532413        return false;
 414    }
 415
 416    /// <summary>
 417    /// Computes a fused two-factor multiply-divide and returns a saturated value
 418    /// when the exact result is not representable.
 419    /// </summary>
 420    internal static Fixed64 MultiplyDivide(
 421        Fixed64 left,
 422        Fixed64 right,
 423        Fixed64 divisor,
 424        out bool representable)
 425    {
 4390426        long divisorRaw = divisor.m_rawValue;
 4390427        if (divisorRaw == 0L)
 428        {
 2429            representable = false;
 2430            return default;
 431        }
 432
 4388433        long leftRaw = left.m_rawValue;
 4388434        long rightRaw = right.m_rawValue;
 4388435        bool negative = (leftRaw ^ rightRaw ^ divisorRaw) < 0L;
 4388436        ulong divisorMagnitude = AbsToUInt64(divisorRaw);
 437
 4388438        Multiply64To128(
 4388439            AbsToUInt64(leftRaw),
 4388440            AbsToUInt64(rightRaw),
 4388441            out ulong productHigh,
 4388442            out ulong productLow);
 4388443        CancelCommonPowersOfTwo(
 4388444            ref productHigh,
 4388445            ref productLow,
 4388446            ref divisorMagnitude);
 447
 4388448        if (productHigh >= divisorMagnitude)
 449        {
 276450            representable = false;
 276451            return negative ? MinValue : MaxValue;
 452        }
 453
 4112454        ulong quotient = Divide128By64(
 4112455            productHigh,
 4112456            productLow,
 4112457            divisorMagnitude,
 4112458            out ulong remainder);
 4112459        ulong twiceRemainder = remainder << 1;
 4112460        bool guard = twiceRemainder >= divisorMagnitude;
 4112461        bool sticky = guard
 4112462            ? twiceRemainder != divisorMagnitude
 4112463            : twiceRemainder != 0UL;
 464
 4112465        return RoundAndApplySign(quotient, guard, sticky, negative, out representable);
 466    }
 467
 468    /// <summary>
 469    /// Attempts to multiply three values and divide by a fourth with one final
 470    /// round-half-to-even operation and no intermediate saturation.
 471    /// </summary>
 472    /// <param name="first">The first factor.</param>
 473    /// <param name="second">The second factor.</param>
 474    /// <param name="third">The third factor.</param>
 475    /// <param name="divisor">The divisor.</param>
 476    /// <param name="result">
 477    /// The fused result when representable; otherwise, <see langword="default"/>.
 478    /// </param>
 479    /// <returns>
 480    /// <see langword="true"/> when the divisor is nonzero and the final result is
 481    /// representable; otherwise, <see langword="false"/>.
 482    /// </returns>
 483    public static bool TryMultiplyDivide(
 484        Fixed64 first,
 485        Fixed64 second,
 486        Fixed64 third,
 487        Fixed64 divisor,
 488        out Fixed64 result)
 489    {
 2855490        result = MultiplyDivide(first, second, third, divisor, out bool representable);
 2855491        if (representable)
 801492            return true;
 493
 2054494        result = default;
 2054495        return false;
 496    }
 497
 498    /// <summary>
 499    /// Attempts to multiply four values and divide by the exact sum of four
 500    /// values with one final round-half-to-even operation.
 501    /// </summary>
 502    public static bool TryMultiplyDivideBySum(
 503        Fixed64 first,
 504        Fixed64 second,
 505        Fixed64 third,
 506        Fixed64 fourth,
 507        Fixed64 firstDivisorTerm,
 508        Fixed64 secondDivisorTerm,
 509        Fixed64 thirdDivisorTerm,
 510        Fixed64 fourthDivisorTerm,
 511        out Fixed64 result)
 512    {
 4513        Signed192 divisor = WideArithmetic.AddSigned192(
 4514            WideArithmetic.AddSigned192(
 4515                Signed192.Raw(firstDivisorTerm),
 4516                Signed192.Raw(secondDivisorTerm)),
 4517            WideArithmetic.AddSigned192(
 4518                Signed192.Raw(thirdDivisorTerm),
 4519                Signed192.Raw(fourthDivisorTerm)));
 4520        Signed576 numerator = WideArithmetic.MultiplySigned576(
 4521            Signed576.ExtendValue(
 4522                Signed320.ExtendValue(Signed192.Raw(first))),
 4523            Signed192.Raw(second),
 4524            Signed192.Raw(third),
 4525            Signed192.Raw(fourth));
 4526        Signed576 scaledDivisor = WideArithmetic.MultiplySigned576(
 4527            Signed576.ExtendValue(Signed320.ExtendValue(divisor)),
 4528            Signed192.One,
 4529            Signed192.One);
 4530        return TryGetSignedRawRatio(
 4531            numerator,
 4532            scaledDivisor,
 4533            out result);
 534    }
 535
 536    /// <summary>
 537    /// Computes a fused three-factor multiply-divide and returns a saturated value
 538    /// when the exact result is not representable.
 539    /// </summary>
 540    internal static Fixed64 MultiplyDivide(
 541        Fixed64 first,
 542        Fixed64 second,
 543        Fixed64 third,
 544        Fixed64 divisor,
 545        out bool representable)
 546    {
 2856547        long divisorRaw = divisor.m_rawValue;
 2856548        if (divisorRaw == 0L)
 549        {
 2550            representable = false;
 2551            return default;
 552        }
 553
 2854554        long firstRaw = first.m_rawValue;
 2854555        long secondRaw = second.m_rawValue;
 2854556        long thirdRaw = third.m_rawValue;
 2854557        bool negative = (firstRaw ^ secondRaw ^ thirdRaw ^ divisorRaw) < 0L;
 2854558        ulong thirdMagnitude = AbsToUInt64(thirdRaw);
 2854559        ulong divisorMagnitude = AbsToUInt64(divisorRaw);
 560
 2854561        Multiply64To128(
 2854562            AbsToUInt64(firstRaw),
 2854563            AbsToUInt64(secondRaw),
 2854564            out ulong productHigh,
 2854565            out ulong productLow);
 2854566        Multiply64To128(
 2854567            productLow,
 2854568            thirdMagnitude,
 2854569            out ulong lowProductHigh,
 2854570            out ulong numeratorLow);
 2854571        Multiply64To128(
 2854572            productHigh,
 2854573            thirdMagnitude,
 2854574            out ulong numeratorHigh,
 2854575            out ulong highProductLow);
 576
 2854577        ulong numeratorMiddle = unchecked(lowProductHigh + highProductLow);
 2854578        if (numeratorMiddle < lowProductHigh)
 262579            numeratorHigh++;
 2854580        CancelCommonPowersOfTwo(
 2854581            ref numeratorHigh,
 2854582            ref numeratorMiddle,
 2854583            ref numeratorLow,
 2854584            ref divisorMagnitude);
 585
 2854586        ulong quotientHigh = numeratorHigh / divisorMagnitude;
 2854587        ulong remainder = numeratorHigh % divisorMagnitude;
 2854588        ulong quotientMiddle = Divide128By64(
 2854589            remainder,
 2854590            numeratorMiddle,
 2854591            divisorMagnitude,
 2854592            out remainder);
 2854593        ulong quotientLow = Divide128By64(
 2854594            remainder,
 2854595            numeratorLow,
 2854596            divisorMagnitude,
 2854597            out remainder);
 598
 2854599        if (quotientHigh != 0UL || (quotientMiddle >> FixedMath.SHIFT_AMOUNT_I) != 0UL)
 600        {
 2051601            representable = false;
 2051602            return negative ? MinValue : MaxValue;
 603        }
 604
 803605        ulong quotient = (quotientMiddle << FixedMath.SHIFT_AMOUNT_I)
 803606            | (quotientLow >> FixedMath.SHIFT_AMOUNT_I);
 803607        ulong discardedMask = (1UL << (FixedMath.SHIFT_AMOUNT_I - 1)) - 1UL;
 803608        bool guard = (quotientLow & (1UL << (FixedMath.SHIFT_AMOUNT_I - 1))) != 0UL;
 803609        bool sticky = ((quotientLow & discardedMask) | remainder) != 0UL;
 610
 803611        return RoundAndApplySign(quotient, guard, sticky, negative, out representable);
 612    }
 613
 614    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 615    private static int CountTrailingZeroes(ulong value) =>
 8018616        63 - CountLeadingZeroes(value & unchecked(0UL - value));
 617
 618    private static void CancelCommonPowersOfTwo(
 619        ref ulong high,
 620        ref ulong low,
 621        ref ulong divisor)
 622    {
 4388623        if ((divisor & 1UL) != 0UL || (high | low) == 0UL)
 1813624            return;
 625
 2575626        int numeratorZeroes = low != 0UL
 2575627            ? CountTrailingZeroes(low)
 2575628            : 64 + CountTrailingZeroes(high);
 2575629        int shift = Math.Min(numeratorZeroes, CountTrailingZeroes(divisor));
 2575630        if (shift == 0)
 314631            return;
 632
 2261633        low = (low >> shift) | (high << (64 - shift));
 2261634        high >>= shift;
 2261635        divisor >>= shift;
 2261636    }
 637
 638    private static void CancelCommonPowersOfTwo(
 639        ref ulong high,
 640        ref ulong middle,
 641        ref ulong low,
 642        ref ulong divisor)
 643    {
 2854644        if ((divisor & 1UL) != 0UL || (high | middle | low) == 0UL)
 1420645            return;
 646
 1434647        int numeratorZeroes = low != 0UL
 1434648            ? CountTrailingZeroes(low)
 1434649            : middle != 0UL
 1434650                ? 64 + CountTrailingZeroes(middle)
 1434651                : 128 + CountTrailingZeroes(high);
 1434652        int shift = Math.Min(numeratorZeroes, CountTrailingZeroes(divisor));
 1434653        if (shift == 0)
 194654            return;
 655
 1240656        low = (low >> shift) | (middle << (64 - shift));
 1240657        middle = (middle >> shift) | (high << (64 - shift));
 1240658        high >>= shift;
 1240659        divisor >>= shift;
 1240660    }
 661
 662    /// <summary>
 663    /// Divides an unsigned 128-bit numerator by a 64-bit divisor when the quotient
 664    /// is known to fit in 64 bits.
 665    /// </summary>
 666    private static ulong Divide128By64(
 667        ulong high,
 668        ulong low,
 669        ulong divisor,
 670        out ulong remainder)
 671    {
 61752672        if (high == 0UL)
 673        {
 34450674            remainder = low % divisor;
 34450675            return low / divisor;
 676        }
 677
 27302678        if (divisor <= uint.MaxValue)
 679        {
 58680            ulong upper = (high << 32) | (low >> 32);
 58681            ulong quotientHigh = upper / divisor;
 58682            remainder = upper % divisor;
 58683            ulong lower = (remainder << 32) | (uint)low;
 58684            ulong quotientLow = lower / divisor;
 58685            remainder = lower % divisor;
 58686            return (quotientHigh << 32) | quotientLow;
 687        }
 688
 27244689        ulong quotient = 0UL;
 27244690        remainder = high;
 3541720691        for (int bit = 63; bit >= 0; bit--)
 692        {
 1743616693            bool carry = (remainder & (1UL << 63)) != 0UL;
 1743616694            remainder = (remainder << 1) | ((low >> bit) & 1UL);
 1743616695            if (carry || remainder >= divisor)
 696            {
 514674697                remainder = unchecked(remainder - divisor);
 514674698                quotient |= 1UL << bit;
 699            }
 700        }
 701
 27244702        return quotient;
 703    }
 704
 705    private static Fixed64 RoundAndApplySign(
 706        ulong quotient,
 707        bool guard,
 708        bool sticky,
 709        bool negative,
 710        out bool representable)
 711    {
 712        const ulong minValueMagnitude = 1UL << 63;
 47259713        if (quotient >= minValueMagnitude)
 714        {
 709715            if (quotient == minValueMagnitude && guard && sticky)
 1716                quotient++;
 717
 709718            return ApplySignedMagnitude(quotient, negative, out representable);
 719        }
 720
 46550721        ulong guardedQuotient = (quotient << 1) | (guard ? 1UL : 0UL);
 46550722        ulong magnitude = RoundGuardedQuotientToEven(
 46550723            guardedQuotient,
 46550724            sticky,
 46550725            out _);
 46550726        return ApplySignedMagnitude(magnitude, negative, out representable);
 727    }
 728
 729    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 730    private static Fixed64 ApplySignedMagnitude(
 731        ulong magnitude,
 732        bool negative,
 733        out bool representable)
 734    {
 735        const ulong minValueMagnitude = 1UL << 63;
 47259736        ulong limit = negative ? minValueMagnitude : long.MaxValue;
 47259737        representable = magnitude <= limit;
 47259738        if (!representable)
 669739            return negative ? MinValue : MaxValue;
 46590740        if (magnitude == minValueMagnitude)
 51741            return MinValue;
 742
 46539743        long rawResult = (long)magnitude;
 46539744        return new Fixed64(negative ? -rawResult : rawResult);
 745    }
 746}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Numerics/Scalars/Fixed64.Operators.cs

#LineLine coverage
 1//=======================================================================
 2// Fixed64.Operators.cs
 3//=======================================================================
 4// MIT License, Copyright (c) 2024–present David Oravsky (mrdav30)
 5// See LICENSE file in the project root for full license information.
 6//=======================================================================
 7
 8using System;
 9using System.Runtime.CompilerServices;
 10
 11namespace FixedMathSharp;
 12
 13/// <content>
 14/// Defines arithmetic, comparison, and conversion operators for <see cref="Fixed64"/>.
 15/// </content>
 16public partial struct Fixed64
 17{
 18    #region Arithmetic Operators
 19
 20    /// <summary>
 21    /// Adds two Fixed64 numbers, with saturating behavior in case of overflow.
 22    /// </summary>
 23    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 24    public static Fixed64 operator +(Fixed64 x, Fixed64 y)
 25    {
 154120326        long xl = x.m_rawValue;
 154120327        long yl = y.m_rawValue;
 154120328        long sum = unchecked(xl + yl);
 154120329        if (!IsAddOrSubtractResultExact(xl, sum, ~(xl ^ yl)))
 13030            sum = xl < 0 ? FixedMath.MIN_VALUE_L : FixedMath.MAX_VALUE_L;
 154120331        return new Fixed64(sum);
 32    }
 33
 34    /// <summary>
 35    /// Adds an int to a Fixed64, with saturating behavior in case of overflow.
 36    /// </summary>
 37    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 2438    public static Fixed64 operator +(Fixed64 x, int y) => x + new Fixed64((long)y << FixedMath.SHIFT_AMOUNT_I);
 39
 40    /// <inheritdoc cref="operator +(Fixed64, int)" />
 41    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 142    public static Fixed64 operator +(int x, Fixed64 y) => y + x;
 43
 44    /// <summary>
 45    /// Subtracts one Fixed64 number from another, with saturating behavior in case of overflow.
 46    /// </summary>
 47    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 48    public static Fixed64 operator -(Fixed64 x, Fixed64 y)
 49    {
 81612950        long xl = x.m_rawValue;
 81612951        long yl = y.m_rawValue;
 81612952        long diff = unchecked(xl - yl);
 81612953        if (!IsAddOrSubtractResultExact(xl, diff, xl ^ yl))
 3554            diff = xl < 0 ? FixedMath.MIN_VALUE_L : FixedMath.MAX_VALUE_L;
 81612955        return new Fixed64(diff);
 56    }
 57
 58    /// <summary>
 59    /// Subtracts an int from a Fixed64, with saturating behavior in case of overflow.
 60    /// </summary>
 61    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 62    public static Fixed64 operator -(Fixed64 x, int y) =>
 1063        x - new Fixed64((long)y << FixedMath.SHIFT_AMOUNT_I);
 64
 65    /// <summary>
 66    /// Subtracts a Fixed64 from an int, with saturating behavior in case of overflow.
 67    /// </summary>
 68    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 69    public static Fixed64 operator -(int x, Fixed64 y) =>
 1070         new Fixed64((long)x << FixedMath.SHIFT_AMOUNT_I) - y;
 71
 72    /// <summary>
 73    /// Multiplies two Fixed64 numbers, handling overflow and rounding.
 74    /// </summary>
 75    /// <summary>
 76    /// Multiplies two Fixed64 numbers using full-width 128-bit intermediate precision
 77    /// and round-half-to-even semantics on the discarded fractional bits.
 78    /// </summary>
 79    public static Fixed64 operator *(Fixed64 x, Fixed64 y)
 80    {
 369783381        long xl = x.m_rawValue;
 369783382        long yl = y.m_rawValue;
 83
 369783384        int shift = FixedMath.SHIFT_AMOUNT_I;
 85
 86        // Determine sign of the final result.
 369783387        bool negative = ((xl ^ yl) < 0);
 88
 89        // Convert to unsigned magnitudes safely, including long.MinValue.
 369783390        ulong ax = AbsToUInt64(xl);
 369783391        ulong ay = AbsToUInt64(yl);
 92
 93        // Compute exact 128-bit unsigned product: (hi << 64) | lo
 369783394        Multiply64To128(ax, ay, out ulong hi, out ulong lo);
 95
 96        // Shift-right with round-half-to-even using the FULL discarded remainder.
 369783397        ulong magnitude = ShiftRightRoundedToEven(hi, lo, shift, out bool roundedOverflow);
 98
 99        // If rounding overflowed the shifted magnitude, carry it into saturation handling.
 3697833100        if (!negative)
 101        {
 3426405102            if (roundedOverflow || magnitude > long.MaxValue)
 198103                return new Fixed64(FixedMath.MAX_VALUE_L);
 104
 3426207105            return new Fixed64((long)magnitude);
 106        }
 107        else
 108        {
 109            // For negative results, magnitude may be exactly 2^63, which maps to long.MinValue.
 110            const ulong minValueMagnitude = 0x8000000000000000UL;
 111
 271428112            if (roundedOverflow || magnitude > minValueMagnitude)
 2113                return new Fixed64(FixedMath.MIN_VALUE_L);
 114
 271426115            if (magnitude == minValueMagnitude)
 2116                return new Fixed64(FixedMath.MIN_VALUE_L);
 117
 271424118            return new Fixed64(-(long)magnitude);
 119        }
 120    }
 121
 122    /// <summary>
 123    /// Multiplies a Fixed64 by an integer, with overflow handling.
 124    /// </summary>
 125    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 126    public static Fixed64 operator *(Fixed64 x, int y) =>
 385605127        x * new Fixed64((long)y << FixedMath.SHIFT_AMOUNT_I);
 128
 129    /// <inheritdoc cref="operator *(Fixed64, int)" />
 130    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 26982131    public static Fixed64 operator *(int x, Fixed64 y) => y * x;
 132
 133    /// <summary>
 134    /// Divides one Fixed64 number by another, handling division by zero and overflow.
 135    /// </summary>
 136    public static Fixed64 operator /(Fixed64 x, Fixed64 y)
 137    {
 472382138        long xl = x.m_rawValue;
 472382139        long yl = y.m_rawValue;
 140
 472382141        if (yl == 0)
 3142            throw new DivideByZeroException($"Attempted to divide {x} by zero.");
 143
 472379144        return DivideMagnitude(
 472379145            AbsToUInt64(xl),
 472379146            AbsToUInt64(yl),
 472379147            (xl ^ yl) < 0);
 148    }
 149
 150    /// <summary>
 151    /// Divides a Fixed64 by an integer, handling division by zero and overflow.
 152    /// </summary>
 153    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 154    public static Fixed64 operator /(Fixed64 x, int y) =>
 44935155         x / new Fixed64((long)y << FixedMath.SHIFT_AMOUNT_I);
 156
 157    /// <summary>
 158    /// Divides an integer by a Fixed64, handling division by zero and overflow.
 159    /// </summary>
 160    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 161    public static Fixed64 operator /(int y, Fixed64 x) =>
 1162         new Fixed64((long)y << FixedMath.SHIFT_AMOUNT_I) / x;
 163
 164    /// <summary>
 165    /// Computes the remainder of division of one Fixed64 number by another.
 166    /// </summary>
 167    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 168    public static Fixed64 operator %(Fixed64 x, Fixed64 y)
 169    {
 44553170        if (x.m_rawValue == FixedMath.MIN_VALUE_L && y.m_rawValue == -1)
 1171            return Zero;
 44552172        return new Fixed64(x.m_rawValue % y.m_rawValue);
 173    }
 174
 175    /// <summary>
 176    /// Computes the remainder of division of a Fixed64 by an int.
 177    /// </summary>
 178    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1179    public static Fixed64 operator %(Fixed64 x, int y) => x % new Fixed64((long)y << FixedMath.SHIFT_AMOUNT_I);
 180
 181    /// <summary>
 182    /// Computes the remainder of division of an int by a Fixed64.
 183    /// </summary>
 184    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1185    public static Fixed64 operator %(int x, Fixed64 y) => new Fixed64((long)x << FixedMath.SHIFT_AMOUNT_I) % y;
 186
 187    /// <summary>
 188    /// Unary negation operator.
 189    /// </summary>
 190    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 191    public static Fixed64 operator -(Fixed64 x) =>
 297398192        x.m_rawValue == FixedMath.MIN_VALUE_L
 297398193            ? new Fixed64(FixedMath.MAX_VALUE_L)
 297398194            : new Fixed64(-x.m_rawValue);
 195
 196    /// <summary>
 197    /// Increments a Fixed64 number by one, with saturating behavior in case of overflow.
 198    /// </summary>
 199    /// <param name="a">The Fixed64 number to increment.</param>
 200    /// <returns>The incremented Fixed64 number.</returns>
 201    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 2202    public static Fixed64 operator ++(Fixed64 a) => a + One;
 203
 204    /// <summary>
 205    /// Decrements a Fixed64 number by one, with saturating behavior in case of overflow.
 206    /// </summary>
 207    /// <param name="a">The Fixed64 number to decrement.</param>
 208    /// <returns>The decremented Fixed64 number.</returns>
 209    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 2210    public static Fixed64 operator --(Fixed64 a) => a - One;
 211
 212    /// <summary>
 213    /// Bitwise left shift operator.
 214    /// </summary>
 215    /// <param name="a">Operand to shift.</param>
 216    /// <param name="shift">Number of bits to shift.</param>
 217    /// <returns>The shifted value.</returns>
 218    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1219    public static Fixed64 operator <<(Fixed64 a, int shift) => new(a.m_rawValue << shift);
 220
 221    /// <summary>
 222    /// Bitwise right shift operator.
 223    /// </summary>
 224    /// <param name="a">Operand to shift.</param>
 225    /// <param name="shift">Number of bits to shift.</param>
 226    /// <returns>The shifted value.</returns>
 227    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1228    public static Fixed64 operator >>(Fixed64 a, int shift) => new(a.m_rawValue >> shift);
 229
 230    #endregion
 231    #region Comparison Operators
 232
 233    /// <summary>
 234    /// Determines whether one Fixed64 is greater than another.
 235    /// </summary>
 236    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 384824237    public static bool operator >(Fixed64 x, Fixed64 y) => x.m_rawValue > y.m_rawValue;
 238
 239    /// <summary>
 240    /// Determines whether a Fixed64 is greater than an integer.
 241    /// </summary>
 242    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1243    public static bool operator >(Fixed64 x, int y) => x.m_rawValue > (long)y << FixedMath.SHIFT_AMOUNT_I;
 244
 245    /// <summary>
 246    /// Determines whether an integer is greater than a Fixed64.
 247    /// </summary>
 248    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1249    public static bool operator >(int y, Fixed64 x) => (long)y << FixedMath.SHIFT_AMOUNT_I > x.m_rawValue;
 250
 251    /// <summary>
 252    /// Determines whether one Fixed64 is less than another.
 253    /// </summary>
 254    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 274232255    public static bool operator <(Fixed64 x, Fixed64 y) => x.m_rawValue < y.m_rawValue;
 256
 257    /// <summary>
 258    /// Determines whether one Fixed64 is less than an integer.
 259    /// </summary>
 260    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1261    public static bool operator <(Fixed64 x, int y) => x.m_rawValue < (long)y << FixedMath.SHIFT_AMOUNT_I;
 262
 263    /// <summary>
 264    /// Determines whether an integer is less than a Fixed64.
 265    /// </summary>
 266    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1267    public static bool operator <(int y, Fixed64 x) => (long)y << FixedMath.SHIFT_AMOUNT_I < x.m_rawValue;
 268
 269    /// <summary>
 270    /// Determines whether one Fixed64 is greater than or equal to another.
 271    /// </summary>
 272    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 15899273    public static bool operator >=(Fixed64 x, Fixed64 y) => x.m_rawValue >= y.m_rawValue;
 274
 275    /// <summary>
 276    /// Determines whether Fixed64 is greater than or equal to an integer.
 277    /// </summary>
 278    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1279    public static bool operator >=(Fixed64 x, int y) => x.m_rawValue >= (long)y << FixedMath.SHIFT_AMOUNT_I;
 280
 281    /// <summary>
 282    /// Determines whether an integer is greater than or equal to a Fixed64.
 283    /// </summary>
 284    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1285    public static bool operator >=(int y, Fixed64 x) => (long)y << FixedMath.SHIFT_AMOUNT_I >= x.m_rawValue;
 286
 287    /// <summary>
 288    /// Determines whether one Fixed64 is less than or equal to another.
 289    /// </summary>
 290    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 305602291    public static bool operator <=(Fixed64 x, Fixed64 y) => x.m_rawValue <= y.m_rawValue;
 292
 293    /// <summary>
 294    /// Determines whether a Fixed64 is less than or equal to an integer.
 295    /// </summary>
 296    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1297    public static bool operator <=(Fixed64 x, int y) => x.m_rawValue <= (long)y << FixedMath.SHIFT_AMOUNT_I;
 298
 299    /// <summary>
 300    /// Determines whether an integer is less than or equal to a Fixed64.
 301    /// </summary>
 302    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1303    public static bool operator <=(int y, Fixed64 x) => (long)y << FixedMath.SHIFT_AMOUNT_I <= x.m_rawValue;
 304
 305    /// <summary>
 306    /// Determines whether two Fixed64 instances are equal.
 307    /// </summary>
 308    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 918989309    public static bool operator ==(Fixed64 left, Fixed64 right) => left.Equals(right);
 310
 311    /// <summary>
 312    /// Determines whether a Fixed64 instance is equal to an integer.
 313    /// </summary>
 314    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1315    public static bool operator ==(Fixed64 left, int right) => left.m_rawValue == (long)right << FixedMath.SHIFT_AMOUNT_
 316
 317    /// <summary>
 318    /// Determines whether an integer is equal to a Fixed64 instance.
 319    /// </summary>
 320    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1321    public static bool operator ==(int left, Fixed64 right) => (long)left << FixedMath.SHIFT_AMOUNT_I == right.m_rawValu
 322
 323    /// <summary>
 324    /// Determines whether two Fixed64 instances are not equal.
 325    /// </summary>
 326    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 98252327    public static bool operator !=(Fixed64 left, Fixed64 right) => !left.Equals(right);
 328
 329    /// <summary>
 330    /// Determines whether a Fixed64 instance is not equal to an integer.
 331    /// </summary>
 332    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1333    public static bool operator !=(Fixed64 left, int right) => left.m_rawValue != (long)right << FixedMath.SHIFT_AMOUNT_
 334
 335    /// <summary>
 336    /// Determines whether an integer is equal to a Fixed64 instance.
 337    /// </summary>
 338    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1339    public static bool operator !=(int left, Fixed64 right) => (long)left << FixedMath.SHIFT_AMOUNT_I != right.m_rawValu
 340
 341    #endregion
 342}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Numerics/Scalars/Fixed64.Statics.cs

#LineLine coverage
 1//=======================================================================
 2// Fixed64.Statics.cs
 3//=======================================================================
 4// MIT License, Copyright (c) 2024–present David Oravsky (mrdav30)
 5// See LICENSE file in the project root for full license information.
 6//=======================================================================
 7
 8using System.Runtime.CompilerServices;
 9
 10namespace FixedMathSharp;
 11
 12/// <content>
 13/// Static helper operations for <see cref="Fixed64"/>, including sign/integer checks,
 14/// leading-zero counting, and exact wide-precision product comparisons.
 15/// </content>
 16public partial struct Fixed64
 17{
 18    /// <summary>
 19    /// Counts the leading zeros in a 64-bit unsigned integer.
 20    /// </summary>
 21    /// <param name="x">The number to count leading zeros for.</param>
 22    /// <returns>The number of leading zeros.</returns>
 23    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 24    internal static int CountLeadingZeroes(ulong x)
 25    {
 336659326        if (x == 0UL)
 1000027            return 64;
 28
 335659329        int result = 0;
 9338850130        while ((x & 0xF000000000000000) == 0) { result += 4; x <<= 4; }
 2185908431        while ((x & 0x8000000000000000) == 0) { result += 1; x <<= 1; }
 335659332        return result;
 33    }
 34
 35    /// <summary>
 36    /// Returns a number indicating the sign of a Fix64 number.
 37    /// Returns 1 if the value is positive, 0 if is 0, and -1 if it is negative.
 38    /// </summary>
 39    /// <remarks>
 40    /// Optimized for branchless comparison.
 41    /// </remarks>
 42    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 43    public static int Sign(Fixed64 value) =>
 1844        value.m_rawValue < 0
 1845                ? -1
 1846                : (value.m_rawValue > 0 ? 1 : 0);
 47
 48    /// <summary>
 49    /// Returns true if the number has no decimal part (i.e., if the number is equivalent to an integer) and False other
 50    /// </summary>
 51    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 52    public static bool IsInteger(Fixed64 value)
 53    {
 754        return ((ulong)value.m_rawValue & FixedMath.MAX_SHIFTED_AMOUNT_UI) == 0;
 55    }
 56
 57    /// <summary>
 58    /// Compares the exact mathematical product <paramref name="leftFirst"/> *
 59    /// <paramref name="leftSecond"/> with <paramref name="rightFirst"/> *
 60    /// <paramref name="rightSecond"/> without rounding or saturation.
 61    /// </summary>
 62    /// <returns>
 63    /// A negative value when the left product is smaller, zero when the exact
 64    /// products are equal, or a positive value when the left product is larger.
 65    /// </returns>
 66    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 67    public static int CompareProducts(
 68        Fixed64 leftFirst,
 69        Fixed64 leftSecond,
 70        Fixed64 rightFirst,
 71        Fixed64 rightSecond) =>
 13972        WideArithmetic.SubtractSigned192(
 13973            GetExactRawProduct(leftFirst, leftSecond),
 13974            GetExactRawProduct(rightFirst, rightSecond)).Sign;
 75
 76    /// <summary>
 77    /// Compares the exact four-factor mathematical product
 78    /// <paramref name="leftFirst"/> * <paramref name="leftSecond"/> *
 79    /// <paramref name="leftThird"/> * <paramref name="leftFourth"/> with
 80    /// <paramref name="rightFirst"/> * <paramref name="rightSecond"/> *
 81    /// <paramref name="rightThird"/> * <paramref name="rightFourth"/> without
 82    /// rounding or saturation.
 83    /// </summary>
 84    /// <returns>
 85    /// A negative value when the left product is smaller, zero when the exact
 86    /// products are equal, or a positive value when the left product is larger.
 87    /// </returns>
 88    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 89    public static int CompareProducts(
 90        Fixed64 leftFirst,
 91        Fixed64 leftSecond,
 92        Fixed64 leftThird,
 93        Fixed64 leftFourth,
 94        Fixed64 rightFirst,
 95        Fixed64 rightSecond,
 96        Fixed64 rightThird,
 97        Fixed64 rightFourth) =>
 13198        WideArithmetic.SubtractSigned320(
 13199            WideArithmetic.MultiplySigned192(
 131100                GetExactRawProduct(leftFirst, leftSecond),
 131101                GetExactRawProduct(leftThird, leftFourth)),
 131102            WideArithmetic.MultiplySigned192(
 131103                GetExactRawProduct(rightFirst, rightSecond),
 131104                GetExactRawProduct(rightThird, rightFourth))).Sign;
 105}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Numerics/Scalars/Fixed64.WideArithmetic.cs

#LineLine coverage
 1//=======================================================================
 2// Fixed64.WideConversion.cs
 3//=======================================================================
 4// MIT License, Copyright (c) 2024–present David Oravsky (mrdav30)
 5// See LICENSE file in the project root for full license information.
 6//=======================================================================
 7
 8using System.Runtime.CompilerServices;
 9using FixedMathSharp.Geometry;
 10
 11namespace FixedMathSharp;
 12
 13/// <content>
 14/// Wide (64x64-bit to 128-bit) arithmetic helpers for high-precision
 15/// interpolation and projection operations on <see cref="Fixed64"/> values.
 16/// </content>
 17public partial struct Fixed64
 18{
 19    /// <summary>
 20    /// Interpolates across the complete raw endpoint domain with one final
 21    /// round-half-to-even conversion.
 22    /// </summary>
 23    internal static Fixed64 LerpFullDomain(Fixed64 from, Fixed64 to, Fixed64 amount)
 24    {
 225325        long amountRaw = amount.m_rawValue;
 225326        if (amountRaw <= 0L)
 22427            return from;
 202928        if (amountRaw >= FixedMath.ONE_L)
 13929            return to;
 30
 189031        long fromRaw = from.m_rawValue;
 189032        long toRaw = to.m_rawValue;
 189033        bool negativeDifference = toRaw < fromRaw;
 189034        ulong differenceMagnitude = negativeDifference
 189035            ? unchecked((ulong)fromRaw - (ulong)toRaw)
 189036            : unchecked((ulong)toRaw - (ulong)fromRaw);
 37
 189038        Multiply64To128(
 189039            differenceMagnitude,
 189040            (ulong)amountRaw,
 189041            out ulong productHigh,
 189042            out ulong productLow);
 43
 189044        ulong interpolatedMagnitude = (productHigh << 32) | (productLow >> 32);
 189045        ulong rawResult = negativeDifference
 189046            ? unchecked((ulong)fromRaw - interpolatedMagnitude)
 189047            : unchecked((ulong)fromRaw + interpolatedMagnitude);
 189048        ulong remainder = productLow & uint.MaxValue;
 49        const ulong HalfRawUnit = 1UL << 31;
 189050        if (remainder > HalfRawUnit || (remainder == HalfRawUnit && (rawResult & 1UL) != 0UL))
 10351            rawResult = negativeDifference ? rawResult - 1UL : rawResult + 1UL;
 52
 189053        return new Fixed64(unchecked((long)rawResult));
 54    }
 55
 56    /// <summary>
 57    /// Projects three component differences, clamps negative sums to zero, floors
 58    /// positive Q64.64 remainder, and saturates only the final Q32.32 result.
 59    /// </summary>
 60    internal static Fixed64 ProjectNonNegativeDifference(
 61        Fixed64 targetX,
 62        Fixed64 sourceX,
 63        Fixed64 directionX,
 64        Fixed64 targetY,
 65        Fixed64 sourceY,
 66        Fixed64 directionY,
 67        Fixed64 targetZ,
 68        Fixed64 sourceZ,
 69        Fixed64 directionZ)
 70    {
 871        WideGeometry.GetDifferenceProjectionWords(
 872            targetX,
 873            sourceX,
 874            directionX,
 875            targetY,
 876            sourceY,
 877            directionY,
 878            targetZ,
 879            sourceZ,
 880            directionZ,
 881            out ulong sumHigh,
 882            out ulong sumMiddle,
 883            out ulong sumLow);
 84
 885        if ((sumHigh & (1UL << 63)) != 0UL || (sumHigh | sumMiddle | sumLow) == 0UL)
 286            return Zero;
 87
 88        // After the Q64.64-to-Q32.32 shift, the low 32 bits of this word become
 89        // the result's high 32 bits. long.MaxValue >> 32 is therefore the largest
 90        // positive middle word that can still produce a representable raw result.
 691        ulong positiveRawHighLimit = (ulong)(long.MaxValue >> FixedMath.SHIFT_AMOUNT_I);
 692        if (sumHigh != 0UL || sumMiddle > positiveRawHighLimit)
 293            return MaxValue;
 94
 495        long rawResult = unchecked((long)(
 496            (sumMiddle << FixedMath.SHIFT_AMOUNT_I)
 497            | (sumLow >> FixedMath.SHIFT_AMOUNT_I)));
 498        return new Fixed64(rawResult);
 99    }
 100
 101    /// <summary>
 102    /// Converts an exact Q64.64 squared-distance sum to Q32.32 with one final
 103    /// round-half-to-even step and positive saturation.
 104    /// </summary>
 105    internal static Fixed64 RoundSquaredDistance(Signed192 value)
 106    {
 51107        if (value.Sign <= 0)
 12108            return Zero;
 109
 39110        ulong positiveRawHighLimit = (ulong)(long.MaxValue >> FixedMath.SHIFT_AMOUNT_I);
 39111        if (value.High != 0UL || value.Middle > positiveRawHighLimit)
 6112            return MaxValue;
 113
 33114        ulong raw = (value.Middle << FixedMath.SHIFT_AMOUNT_I)
 33115            | (value.Low >> FixedMath.SHIFT_AMOUNT_I);
 33116        if (raw >= long.MaxValue)
 1117            return MaxValue;
 118
 32119        ulong remainder = value.Low & uint.MaxValue;
 120        const ulong HalfRawUnit = 1UL << (FixedMath.SHIFT_AMOUNT_I - 1);
 32121        if (remainder > HalfRawUnit || (remainder == HalfRawUnit && (raw & 1UL) != 0UL))
 6122            raw++;
 123
 32124        return new Fixed64((long)raw);
 125    }
 126
 127    /// <summary>
 128    /// Evaluates one barycentric coordinate without saturating endpoint
 129    /// differences or weighted intermediates.
 130    /// </summary>
 131    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 132    internal static Fixed64 BarycentricCoordinateFullDomain(
 133        Fixed64 coordA,
 134        Fixed64 coordB,
 135        Fixed64 coordC,
 136        Fixed64 weightB,
 137        Fixed64 weightC)
 138    {
 376139        long baseRaw = coordA.m_rawValue;
 376140        ulong extension = baseRaw < 0L ? ulong.MaxValue : 0UL;
 376141        ulong high = extension;
 376142        ulong middle = (extension << FixedMath.SHIFT_AMOUNT_I)
 376143            | (unchecked((ulong)baseRaw) >> FixedMath.SHIFT_AMOUNT_I);
 376144        ulong low = unchecked((ulong)baseRaw << FixedMath.SHIFT_AMOUNT_I);
 376145        WideGeometry.AccumulateDifferenceProduct(
 376146            coordB.m_rawValue,
 376147            baseRaw,
 376148            weightB.m_rawValue,
 376149            0L,
 376150            ref high,
 376151            ref middle,
 376152            ref low);
 376153        WideGeometry.AccumulateDifferenceProduct(
 376154            coordC.m_rawValue,
 376155            baseRaw,
 376156            weightC.m_rawValue,
 376157            0L,
 376158            ref high,
 376159            ref middle,
 376160            ref low);
 376161        return RoundSignedToFixed(new Signed192(high, middle, low), FixedMath.SHIFT_AMOUNT_I);
 162    }
 163
 164    /// <summary>
 165    /// Converts a signed wide value to Q32.32 with one final
 166    /// round-half-to-even step and signed saturation.
 167    /// </summary>
 168    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 169    internal static Fixed64 RoundSignedToFixed(Signed192 value, int fractionalBits)
 170    {
 442171        bool negative = value.Sign < 0;
 442172        WideArithmetic.GetMagnitude(value, out ulong high, out ulong middle, out ulong low);
 442173        if (high != 0UL || (middle >> fractionalBits) != 0UL)
 16174            return negative ? MinValue : MaxValue;
 175
 426176        ulong magnitude = (middle << (64 - fractionalBits)) | (low >> fractionalBits);
 426177        ulong limit = negative ? 1UL << 63 : (ulong)long.MaxValue;
 426178        if (magnitude > limit)
 6179            return negative ? MinValue : MaxValue;
 180
 420181        ulong remainderMask = (1UL << fractionalBits) - 1UL;
 420182        ulong remainder = low & remainderMask;
 420183        ulong half = 1UL << (fractionalBits - 1);
 420184        if (remainder > half || (remainder == half && (magnitude & 1UL) != 0UL))
 185        {
 13186            if (magnitude == limit)
 2187                return negative ? MinValue : MaxValue;
 11188            magnitude++;
 189        }
 190
 418191        long raw = negative ? unchecked(-(long)magnitude) : (long)magnitude;
 418192        return new Fixed64(raw);
 193    }
 194
 195    /// <summary>
 196    /// Converts an exact nonnegative square root to Q32.32 with one final
 197    /// round-half-to-even step and positive saturation.
 198    /// </summary>
 199    internal static Fixed64 RoundSquareRootToFixed(
 200        Signed192 root,
 201        Signed192 remainder,
 202        int fractionalBits)
 203    {
 19204        WideArithmetic.GetMagnitude(root, out ulong high, out ulong middle, out ulong low);
 19205        if (high != 0UL || (middle >> fractionalBits) != 0UL)
 3206            return MaxValue;
 207
 16208        ulong magnitude = (middle << (64 - fractionalBits)) | (low >> fractionalBits);
 16209        if (magnitude > (ulong)long.MaxValue)
 1210            return MaxValue;
 211
 15212        ulong remainderMask = (1UL << fractionalBits) - 1UL;
 15213        ulong discarded = low & remainderMask;
 15214        ulong half = 1UL << (fractionalBits - 1);
 15215        if (discarded > half
 15216            || (discarded == half && (!remainder.IsZero || (magnitude & 1UL) != 0UL)))
 217        {
 5218            if (magnitude == (ulong)long.MaxValue)
 1219                return MaxValue;
 4220            magnitude++;
 221        }
 222
 14223        return new Fixed64((long)magnitude);
 224    }
 225
 226    /// <summary>
 227    /// Converts one exact cross component to a normalized Q32.32 component.
 228    /// </summary>
 229    /// <remarks>
 230    /// The caller supplies the component's exact square, the positive ceiling
 231    /// of the exact magnitude, and a nondegenerate squared magnitude produced
 232    /// from the same cross product.
 233    /// </remarks>
 234    internal static Fixed64 NormalizeWideComponent(
 235        Signed192 component,
 236        Signed320 componentSquare,
 237        Signed192 ceilingMagnitude,
 238        Signed320 squaredMagnitude)
 239    {
 930240        int sign = component.Sign;
 930241        if (sign == 0)
 355242            return Zero;
 243
 575244        WideArithmetic.GetMagnitude(
 575245            ceilingMagnitude,
 575246            out ulong magnitudeHigh,
 575247            out ulong magnitudeMiddle,
 575248            out ulong magnitudeLow);
 575249        int bitLength = WideArithmetic.GetBitLength(magnitudeHigh, magnitudeMiddle, magnitudeLow);
 575250        int shift = bitLength > 63 ? bitLength - 63 : 0;
 575251        ulong numerator = WideArithmetic.ShiftRightToUInt64(component, shift, out _);
 575252        ulong denominator = WideArithmetic.ShiftRightToUInt64(ceilingMagnitude, shift, out bool discarded);
 575253        if (discarded)
 70254            denominator++;
 255
 575256        ulong candidate = (ulong)DivideMagnitude(numerator, denominator, false).m_rawValue;
 575257        int midpointComparison = WideArithmetic.CompareNormalizedComponentToMidpoint(
 575258            componentSquare,
 575259            squaredMagnitude,
 575260            candidate);
 575261        if (midpointComparison > 0 || (midpointComparison == 0 && (candidate & 1UL) != 0UL))
 1262            candidate++;
 263
 575264        long raw = (long)candidate;
 575265        return new Fixed64(sign < 0 ? -raw : raw);
 266    }
 267
 268    internal static bool TryAddProducts(
 269        Fixed64 firstLeft,
 270        Fixed64 firstRight,
 271        Fixed64 secondLeft,
 272        Fixed64 secondRight,
 273        out Fixed64 result) =>
 185274        TryRoundProductCombination(
 185275            GetExactRawProduct(firstLeft, firstRight),
 185276            GetExactRawProduct(secondLeft, secondRight),
 185277            subtract: false,
 185278            out result);
 279
 280    internal static bool TryAddProducts(
 281        Fixed64 firstLeft,
 282        Fixed64 firstRight,
 283        Fixed64 secondLeft,
 284        Fixed64 secondRight,
 285        Fixed64 thirdLeft,
 286        Fixed64 thirdRight,
 287        out Fixed64 result)
 288    {
 469289        Signed192 numerator = WideArithmetic.AddSigned192(
 469290            WideArithmetic.AddSigned192(
 469291                GetExactRawProduct(firstLeft, firstRight),
 469292                GetExactRawProduct(secondLeft, secondRight)),
 469293            GetExactRawProduct(thirdLeft, thirdRight));
 469294        return TryRoundProductCombination(
 469295            numerator,
 469296            default,
 469297            subtract: false,
 469298            out result);
 299    }
 300
 301    internal static bool TryAddScaledProducts(
 302        Fixed64 firstLeft,
 303        Fixed64 firstRight,
 304        Fixed64 secondLeft,
 305        Fixed64 secondRight,
 306        Fixed64 thirdLeft,
 307        Fixed64 thirdRight,
 308        Fixed64 resultScale,
 309        out Fixed64 result)
 310    {
 12311        Signed192 products = WideArithmetic.AddSigned192(
 12312            WideArithmetic.AddSigned192(
 12313                GetExactRawProduct(firstLeft, firstRight),
 12314                GetExactRawProduct(secondLeft, secondRight)),
 12315            GetExactRawProduct(thirdLeft, thirdRight));
 12316        Signed320 scaled = WideArithmetic.MultiplySigned192(
 12317            products,
 12318            Signed192.Raw(resultScale));
 12319        bool negative = scaled.Sign < 0;
 12320        WideArithmetic.GetMagnitude(
 12321            scaled,
 12322            out ulong word4,
 12323            out ulong word3,
 12324            out ulong word2,
 12325            out ulong word1,
 12326            out ulong word0);
 12327        if ((word4 | word3 | word2) != 0UL)
 328        {
 1329            result = default;
 1330            return false;
 331        }
 332
 333        const ulong guardMask = 1UL << 63;
 11334        result = RoundAndApplySign(
 11335            word1,
 11336            (word0 & guardMask) != 0UL,
 11337            (word0 & (guardMask - 1UL)) != 0UL,
 11338            negative,
 11339            out bool representable);
 11340        if (representable)
 10341            return true;
 342
 1343        result = default;
 1344        return false;
 345    }
 346
 347    internal static bool TrySubtractProducts(
 348        Fixed64 firstLeft,
 349        Fixed64 firstRight,
 350        Fixed64 secondLeft,
 351        Fixed64 secondRight,
 352        out Fixed64 result) =>
 388353        TryRoundProductCombination(
 388354            GetExactRawProduct(firstLeft, firstRight),
 388355            GetExactRawProduct(secondLeft, secondRight),
 388356            subtract: true,
 388357            out result);
 358
 359    private static bool TryRoundProductCombination(
 360        Signed192 first,
 361        Signed192 second,
 362        bool subtract,
 363        out Fixed64 result)
 364    {
 1042365        Signed192 numerator = subtract
 1042366            ? WideArithmetic.SubtractSigned192(first, second)
 1042367            : WideArithmetic.AddSigned192(first, second);
 1042368        bool negative = numerator.Sign < 0;
 1042369        WideArithmetic.GetMagnitude(
 1042370            numerator,
 1042371            out _,
 1042372            out ulong middle,
 1042373            out ulong low);
 1042374        if ((middle >> FixedMath.SHIFT_AMOUNT_I) != 0UL)
 375        {
 3376            result = default;
 3377            return false;
 378        }
 379
 1039380        ulong quotient = (middle << FixedMath.SHIFT_AMOUNT_I)
 1039381            | (low >> FixedMath.SHIFT_AMOUNT_I);
 1039382        ulong guardMask = 1UL << (FixedMath.SHIFT_AMOUNT_I - 1);
 1039383        bool guard = (low & guardMask) != 0UL;
 1039384        bool sticky = (low & (guardMask - 1UL)) != 0UL;
 1039385        result = RoundAndApplySign(
 1039386            quotient,
 1039387            guard,
 1039388            sticky,
 1039389            negative,
 1039390            out bool representable);
 1039391        if (representable)
 1033392            return true;
 393
 6394        result = default;
 6395        return false;
 396    }
 397}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Numerics/Scalars/Fixed64.WideRatio.cs

#LineLine coverage
 1//=======================================================================
 2// Fixed64.WideRawRatio.cs
 3//=======================================================================
 4// MIT License, Copyright (c) 2024–present David Oravsky (mrdav30)
 5// See LICENSE file in the project root for full license information.
 6//=======================================================================
 7
 8using System;
 9using System.Runtime.CompilerServices;
 10
 11namespace FixedMathSharp;
 12
 13/// <content>
 14/// Provides wide-precision (128/192-bit) ratio-to-<see cref="Fixed64"/> conversion helpers,
 15/// used for computing exact division and unit-interval results without precision loss.
 16/// </content>
 17public partial struct Fixed64
 18{
 19    /// <summary>
 20    /// Converts an exact ratio proven by this method to lie in [0, 1] to Q32.32.
 21    /// </summary>
 22    internal static bool TryGetUnitIntervalRatio(
 23        Signed192 numerator,
 24        Signed192 denominator,
 25        out Fixed64 result)
 26    {
 76727        int denominatorSign = denominator.Sign;
 76728        int numeratorSign = numerator.Sign;
 76729        if (denominatorSign == 0 || (numeratorSign != 0 && numeratorSign != denominatorSign))
 30        {
 531            result = default;
 532            return false;
 33        }
 34
 76235        if (numeratorSign == 0)
 36        {
 1937            result = Zero;
 1938            return true;
 39        }
 40
 74341        WideArithmetic.GetMagnitude(numerator, out ulong numeratorHigh, out ulong numeratorMiddle, out ulong numeratorLo
 74342        WideArithmetic.GetMagnitude(denominator, out ulong denominatorHigh, out ulong denominatorMiddle, out ulong denom
 74343        int comparison = WideArithmetic.CompareUnsigned(
 74344            numeratorHigh,
 74345            numeratorMiddle,
 74346            numeratorLow,
 74347            denominatorHigh,
 74348            denominatorMiddle,
 74349            denominatorLow);
 74350        if (comparison > 0)
 51        {
 452            result = default;
 453            return false;
 54        }
 55
 73956        if (comparison == 0)
 57        {
 1258            result = One;
 1259            return true;
 60        }
 61
 72762        result = GetUnitIntervalRatio(
 72763            numeratorHigh,
 72764            numeratorMiddle,
 72765            numeratorLow,
 72766            denominatorHigh,
 72767            denominatorMiddle,
 72768            denominatorLow);
 72769        return true;
 70    }
 71
 72    /// <summary>
 73    /// Converts an exact five-word ratio proven by this method to lie in [0, 1]
 74    /// to Q32.32 using guard/sticky round-half-to-even state.
 75    /// </summary>
 76    internal static bool TryGetUnitIntervalRatio(
 77        Signed320 numerator,
 78        Signed320 denominator,
 79        out Fixed64 result)
 80    {
 9581        if (Signed192.TryNarrowSigned(numerator, out Signed192 narrowNumerator)
 9582            && Signed192.TryNarrowSigned(denominator, out Signed192 narrowDenominator))
 83        {
 7484            return TryGetUnitIntervalRatio(narrowNumerator, narrowDenominator, out result);
 85        }
 86
 2187        int denominatorSign = denominator.Sign;
 2188        int numeratorSign = numerator.Sign;
 2189        if (denominatorSign == 0 || (numeratorSign != 0 && numeratorSign != denominatorSign))
 90        {
 491            result = default;
 492            return false;
 93        }
 94
 1795        if (numeratorSign == 0)
 96        {
 197            result = Zero;
 198            return true;
 99        }
 100
 16101        WideArithmetic.GetMagnitude(
 16102            numerator,
 16103            out ulong numeratorWord4,
 16104            out ulong numeratorWord3,
 16105            out ulong numeratorWord2,
 16106            out ulong numeratorWord1,
 16107            out ulong numeratorWord0);
 16108        WideArithmetic.GetMagnitude(
 16109            denominator,
 16110            out ulong denominatorWord4,
 16111            out ulong denominatorWord3,
 16112            out ulong denominatorWord2,
 16113            out ulong denominatorWord1,
 16114            out ulong denominatorWord0);
 16115        int comparison = WideArithmetic.CompareUnsigned(
 16116            numeratorWord4,
 16117            numeratorWord3,
 16118            numeratorWord2,
 16119            numeratorWord1,
 16120            numeratorWord0,
 16121            denominatorWord4,
 16122            denominatorWord3,
 16123            denominatorWord2,
 16124            denominatorWord1,
 16125            denominatorWord0);
 16126        if (comparison > 0)
 127        {
 1128            result = default;
 1129            return false;
 130        }
 131
 15132        if (comparison == 0)
 133        {
 1134            result = One;
 1135            return true;
 136        }
 137
 14138        result = GetUnitIntervalRatio(
 14139            numeratorWord4,
 14140            numeratorWord3,
 14141            numeratorWord2,
 14142            numeratorWord1,
 14143            numeratorWord0,
 14144            denominatorWord4,
 14145            denominatorWord3,
 14146            denominatorWord2,
 14147            denominatorWord1,
 14148            denominatorWord0);
 14149        return true;
 150    }
 151
 152    private static Fixed64 GetUnitIntervalRatio(
 153        ulong numeratorWord4,
 154        ulong numeratorWord3,
 155        ulong numeratorWord2,
 156        ulong numeratorWord1,
 157        ulong numeratorWord0,
 158        ulong denominatorWord4,
 159        ulong denominatorWord3,
 160        ulong denominatorWord2,
 161        ulong denominatorWord1,
 162        ulong denominatorWord0)
 163    {
 333164        ulong guardedQuotient = 0UL;
 22644165        for (int bit = FixedMath.SHIFT_AMOUNT_I; bit >= 0; bit--)
 166        {
 10989167            WideArithmetic.ShiftLeftOne(
 10989168                ref numeratorWord4,
 10989169                ref numeratorWord3,
 10989170                ref numeratorWord2,
 10989171                ref numeratorWord1,
 10989172                ref numeratorWord0);
 10989173            guardedQuotient <<= 1;
 10989174            if (WideArithmetic.CompareUnsigned(
 10989175                numeratorWord4,
 10989176                numeratorWord3,
 10989177                numeratorWord2,
 10989178                numeratorWord1,
 10989179                numeratorWord0,
 10989180                denominatorWord4,
 10989181                denominatorWord3,
 10989182                denominatorWord2,
 10989183                denominatorWord1,
 10989184                denominatorWord0) < 0)
 185            {
 186                continue;
 187            }
 188
 5243189            WideArithmetic.SubtractUnsigned(
 5243190                ref numeratorWord4,
 5243191                ref numeratorWord3,
 5243192                ref numeratorWord2,
 5243193                ref numeratorWord1,
 5243194                ref numeratorWord0,
 5243195                denominatorWord4,
 5243196                denominatorWord3,
 5243197                denominatorWord2,
 5243198                denominatorWord1,
 5243199                denominatorWord0);
 5243200            guardedQuotient |= 1UL;
 201        }
 202
 333203        ulong rounded = RoundGuardedQuotientToEven(
 333204            guardedQuotient,
 333205            (numeratorWord4 | numeratorWord3 | numeratorWord2 | numeratorWord1 | numeratorWord0) != 0UL,
 333206            out _);
 333207        return new Fixed64((long)rounded);
 208    }
 209
 210    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 211    private static Fixed64 GetUnitIntervalRatio(
 212        ulong numeratorHigh,
 213        ulong numeratorMiddle,
 214        ulong numeratorLow,
 215        ulong denominatorHigh,
 216        ulong denominatorMiddle,
 217        ulong denominatorLow)
 218    {
 1186219        if ((numeratorHigh | denominatorHigh) == 0UL)
 220        {
 940221            return GetUnitIntervalRatio128(
 940222                numeratorMiddle,
 940223                numeratorLow,
 940224                denominatorMiddle,
 940225                denominatorLow);
 226        }
 227
 246228        ulong guardedQuotient = 0UL;
 16728229        for (int bit = FixedMath.SHIFT_AMOUNT_I; bit >= 0; bit--)
 230        {
 8118231            WideArithmetic.ShiftLeftOne(ref numeratorHigh, ref numeratorMiddle, ref numeratorLow);
 8118232            guardedQuotient <<= 1;
 8118233            if (WideArithmetic.CompareUnsigned(
 8118234                numeratorHigh,
 8118235                numeratorMiddle,
 8118236                numeratorLow,
 8118237                denominatorHigh,
 8118238                denominatorMiddle,
 8118239                denominatorLow) < 0)
 240            {
 241                continue;
 242            }
 243
 2991244            WideArithmetic.SubtractUnsigned(
 2991245                ref numeratorHigh,
 2991246                ref numeratorMiddle,
 2991247                ref numeratorLow,
 2991248                denominatorHigh,
 2991249                denominatorMiddle,
 2991250                denominatorLow);
 2991251            guardedQuotient |= 1UL;
 252        }
 253
 246254        ulong rounded = RoundGuardedQuotientToEven(
 246255            guardedQuotient,
 246256            (numeratorHigh | numeratorMiddle | numeratorLow) != 0UL,
 246257            out _);
 246258        return new Fixed64((long)rounded);
 259    }
 260
 261    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 262    private static Fixed64 GetUnitIntervalRatio128(
 263        ulong numeratorHigh,
 264        ulong numeratorLow,
 265        ulong denominatorHigh,
 266        ulong denominatorLow)
 267    {
 940268        ulong guardedQuotient = 0UL;
 63920269        for (int bit = FixedMath.SHIFT_AMOUNT_I; bit >= 0; bit--)
 270        {
 31020271            bool overflow = (numeratorHigh & (1UL << 63)) != 0UL;
 31020272            numeratorHigh = (numeratorHigh << 1) | (numeratorLow >> 63);
 31020273            numeratorLow <<= 1;
 31020274            guardedQuotient <<= 1;
 275
 31020276            if (!overflow
 31020277                && (numeratorHigh < denominatorHigh
 31020278                    || (numeratorHigh == denominatorHigh && numeratorLow < denominatorLow)))
 279            {
 280                continue;
 281            }
 282
 11431283            ulong previousLow = numeratorLow;
 11431284            numeratorLow = unchecked(numeratorLow - denominatorLow);
 11431285            ulong borrow = previousLow < denominatorLow ? 1UL : 0UL;
 11431286            numeratorHigh = unchecked(numeratorHigh - denominatorHigh - borrow);
 11431287            guardedQuotient |= 1UL;
 288        }
 289
 940290        ulong rounded = RoundGuardedQuotientToEven(
 940291            guardedQuotient,
 940292            (numeratorHigh | numeratorLow) != 0UL,
 940293            out _);
 940294        return new Fixed64((long)rounded);
 295    }
 296
 297    /// <summary>
 298    /// Computes the ratio of two non-negative wide integers, returning a Fixed64 value in the unit interval [0, 1].
 299    /// </summary>
 300    internal static Fixed64 GetUnitIntervalRatio(
 301        ReadOnlySpan<ulong> numerator,
 302        ReadOnlySpan<ulong> denominator)
 303    {
 161304        int length = Math.Max(numerator.Length, denominator.Length) + 1;
 161305        Span<ulong> numeratorMagnitude = stackalloc ulong[length];
 161306        Span<ulong> remainder = stackalloc ulong[length];
 161307        Span<ulong> denominatorMagnitude = stackalloc ulong[length];
 161308        numeratorMagnitude.Clear();
 161309        remainder.Clear();
 161310        denominatorMagnitude.Clear();
 161311        numerator.CopyTo(numeratorMagnitude);
 161312        denominator.CopyTo(denominatorMagnitude);
 161313        ShiftLeftMagnitude(
 161314            numeratorMagnitude,
 161315            FixedMath.SHIFT_AMOUNT_I,
 161316            remainder);
 161317        _ = TryGetSignedRawRatioCore(
 161318            remainder,
 161319            denominatorMagnitude,
 161320            negative: false,
 161321            roundToEven: true,
 161322            out Fixed64 result);
 161323        return result;
 324    }
 325
 326    /// <summary>
 327    /// Converts an arbitrary nonzero signed wide ratio to Q32.32 with
 328    /// round-half-to-even and signed saturation.
 329    /// </summary>
 330    internal static Fixed64 GetSignedRatio(Signed192 numerator, Signed192 denominator)
 331    {
 1694332        int numeratorSign = numerator.Sign;
 1694333        int denominatorSign = denominator.Sign;
 1694334        if (numeratorSign == 0)
 220335            return Zero;
 1474336        if (denominatorSign == 0)
 2337            return numeratorSign < 0 ? MinValue : MaxValue;
 338
 1472339        bool negative = numeratorSign != denominatorSign;
 1472340        WideArithmetic.GetMagnitude(numerator, out ulong numeratorHigh, out ulong numeratorMiddle, out ulong numeratorLo
 1472341        WideArithmetic.GetMagnitude(denominator, out ulong denominatorHigh, out ulong denominatorMiddle, out ulong denom
 342
 1472343        if (!negative)
 344        {
 1048345            int comparison = WideArithmetic.CompareUnsigned(
 1048346                numeratorHigh, numeratorMiddle, numeratorLow,
 1048347                denominatorHigh, denominatorMiddle, denominatorLow);
 1048348            if (comparison <= 0)
 349            {
 691350                return comparison == 0
 691351                    ? One
 691352                    : GetUnitIntervalRatio(
 691353                        numeratorHigh, numeratorMiddle, numeratorLow,
 691354                        denominatorHigh, denominatorMiddle, denominatorLow);
 355            }
 356        }
 357
 781358        int numeratorBits = WideArithmetic.GetBitLength(numeratorHigh, numeratorMiddle, numeratorLow);
 781359        int denominatorBits = WideArithmetic.GetBitLength(denominatorHigh, denominatorMiddle, denominatorLow);
 781360        int integerShift = numeratorBits - denominatorBits;
 781361        if (integerShift >= 31)
 362        {
 15363            if (integerShift > 31)
 8364                return negative ? MinValue : MaxValue;
 365
 7366            WideArithmetic.ShiftLeft(
 7367                denominatorHigh,
 7368                denominatorMiddle,
 7369                denominatorLow,
 7370                31,
 7371                out ulong limitHigh,
 7372                out ulong limitMiddle,
 7373                out ulong limitLow);
 7374            int limitComparison = WideArithmetic.CompareUnsigned(
 7375                numeratorHigh, numeratorMiddle, numeratorLow,
 7376                limitHigh, limitMiddle, limitLow);
 7377            if (limitComparison > 0 || (limitComparison == 0 && !negative))
 4378                return negative ? MinValue : MaxValue;
 3379            if (limitComparison == 0)
 2380                return MinValue;
 381        }
 382
 767383        ulong guardedQuotient = 0UL;
 767384        if (integerShift >= 0)
 385        {
 616386            WideArithmetic.ShiftLeft(
 616387                denominatorHigh,
 616388                denominatorMiddle,
 616389                denominatorLow,
 616390                integerShift,
 616391                out ulong shiftedHigh,
 616392                out ulong shiftedMiddle,
 616393                out ulong shiftedLow);
 5812394            for (int bit = integerShift; bit >= 0; bit--)
 395            {
 2290396                if (WideArithmetic.CompareUnsigned(
 2290397                    numeratorHigh, numeratorMiddle, numeratorLow,
 2290398                    shiftedHigh, shiftedMiddle, shiftedLow) >= 0)
 399                {
 1378400                    WideArithmetic.SubtractUnsigned(
 1378401                        ref numeratorHigh,
 1378402                        ref numeratorMiddle,
 1378403                        ref numeratorLow,
 1378404                        shiftedHigh,
 1378405                        shiftedMiddle,
 1378406                        shiftedLow);
 1378407                    guardedQuotient |= 1UL << bit;
 408                }
 409
 2290410                WideArithmetic.ShiftRightOne(ref shiftedHigh, ref shiftedMiddle, ref shiftedLow);
 411            }
 412        }
 413
 52156414        for (int bit = FixedMath.SHIFT_AMOUNT_I; bit >= 0; bit--)
 415        {
 25311416            WideArithmetic.ShiftLeftOne(ref numeratorHigh, ref numeratorMiddle, ref numeratorLow);
 25311417            guardedQuotient <<= 1;
 25311418            if (WideArithmetic.CompareUnsigned(
 25311419                numeratorHigh, numeratorMiddle, numeratorLow,
 25311420                denominatorHigh, denominatorMiddle, denominatorLow) < 0)
 421            {
 422                continue;
 423            }
 424
 7830425            WideArithmetic.SubtractUnsigned(
 7830426                ref numeratorHigh,
 7830427                ref numeratorMiddle,
 7830428                ref numeratorLow,
 7830429                denominatorHigh,
 7830430                denominatorMiddle,
 7830431                denominatorLow);
 7830432            guardedQuotient |= 1UL;
 433        }
 434
 767435        ulong magnitude = RoundGuardedQuotientToEven(
 767436            guardedQuotient,
 767437            (numeratorHigh | numeratorMiddle | numeratorLow) != 0UL,
 767438            out bool roundedOverflow);
 767439        ulong limit = negative ? 1UL << 63 : (ulong)long.MaxValue;
 767440        if (roundedOverflow || magnitude > limit)
 3441            return negative ? MinValue : MaxValue;
 442
 764443        long raw = negative ? unchecked(-(long)magnitude) : (long)magnitude;
 764444        return new Fixed64(raw);
 445    }
 446
 447    /// <summary>
 448    /// Converts an arbitrary nonzero signed five-word ratio to Q32.32 with
 449    /// round-half-to-even and signed saturation.
 450    /// </summary>
 451    internal static Fixed64 GetSignedRatio(Signed320 numerator, Signed320 denominator)
 452    {
 1104453        if (Signed192.TryNarrowSigned(numerator, out Signed192 narrowNumerator)
 1104454            && Signed192.TryNarrowSigned(denominator, out Signed192 narrowDenominator))
 455        {
 675456            return GetSignedRatio(narrowNumerator, narrowDenominator);
 457        }
 458
 429459        int numeratorSign = numerator.Sign;
 429460        int denominatorSign = denominator.Sign;
 429461        if (numeratorSign == 0)
 47462            return Zero;
 382463        if (denominatorSign == 0)
 2464            return numeratorSign < 0 ? MinValue : MaxValue;
 465
 380466        bool negative = numeratorSign != denominatorSign;
 380467        WideArithmetic.GetMagnitude(
 380468            numerator,
 380469            out ulong numeratorWord4,
 380470            out ulong numeratorWord3,
 380471            out ulong numeratorWord2,
 380472            out ulong numeratorWord1,
 380473            out ulong numeratorWord0);
 380474        WideArithmetic.GetMagnitude(
 380475            denominator,
 380476            out ulong denominatorWord4,
 380477            out ulong denominatorWord3,
 380478            out ulong denominatorWord2,
 380479            out ulong denominatorWord1,
 380480            out ulong denominatorWord0);
 481
 380482        int comparison = WideArithmetic.CompareUnsigned(
 380483            numeratorWord4,
 380484            numeratorWord3,
 380485            numeratorWord2,
 380486            numeratorWord1,
 380487            numeratorWord0,
 380488            denominatorWord4,
 380489            denominatorWord3,
 380490            denominatorWord2,
 380491            denominatorWord1,
 380492            denominatorWord0);
 380493        if (comparison <= 0)
 494        {
 366495            if (comparison == 0)
 47496                return negative ? -One : One;
 497
 319498            Fixed64 unitRatio = GetUnitIntervalRatio(
 319499                numeratorWord4,
 319500                numeratorWord3,
 319501                numeratorWord2,
 319502                numeratorWord1,
 319503                numeratorWord0,
 319504                denominatorWord4,
 319505                denominatorWord3,
 319506                denominatorWord2,
 319507                denominatorWord1,
 319508                denominatorWord0);
 319509            return negative ? -unitRatio : unitRatio;
 510        }
 511
 14512        int numeratorBits = WideArithmetic.GetBitLength(
 14513            numeratorWord4, numeratorWord3, numeratorWord2, numeratorWord1, numeratorWord0);
 14514        int denominatorBits = WideArithmetic.GetBitLength(
 14515            denominatorWord4, denominatorWord3, denominatorWord2, denominatorWord1, denominatorWord0);
 14516        int integerShift = numeratorBits - denominatorBits;
 14517        if (integerShift >= 31)
 518        {
 9519            if (integerShift > 31)
 2520                return negative ? MinValue : MaxValue;
 521
 7522            WideArithmetic.ShiftLeft(
 7523                denominatorWord4,
 7524                denominatorWord3,
 7525                denominatorWord2,
 7526                denominatorWord1,
 7527                denominatorWord0,
 7528                31,
 7529                out ulong limitWord4,
 7530                out ulong limitWord3,
 7531                out ulong limitWord2,
 7532                out ulong limitWord1,
 7533                out ulong limitWord0);
 7534            int limitComparison = WideArithmetic.CompareUnsigned(
 7535                numeratorWord4,
 7536                numeratorWord3,
 7537                numeratorWord2,
 7538                numeratorWord1,
 7539                numeratorWord0,
 7540                limitWord4,
 7541                limitWord3,
 7542                limitWord2,
 7543                limitWord1,
 7544                limitWord0);
 7545            if (limitComparison > 0 || (limitComparison == 0 && !negative))
 4546                return negative ? MinValue : MaxValue;
 3547            if (limitComparison == 0)
 2548                return MinValue;
 549        }
 550
 6551        ulong guardedQuotient = 0UL;
 6552        WideArithmetic.ShiftLeft(
 6553            denominatorWord4,
 6554            denominatorWord3,
 6555            denominatorWord2,
 6556            denominatorWord1,
 6557            denominatorWord0,
 6558            integerShift,
 6559            out ulong shiftedWord4,
 6560            out ulong shiftedWord3,
 6561            out ulong shiftedWord2,
 6562            out ulong shiftedWord1,
 6563            out ulong shiftedWord0);
 210564        for (int bit = integerShift; bit >= 0; bit--)
 565        {
 99566            if (WideArithmetic.CompareUnsigned(
 99567                numeratorWord4,
 99568                numeratorWord3,
 99569                numeratorWord2,
 99570                numeratorWord1,
 99571                numeratorWord0,
 99572                shiftedWord4,
 99573                shiftedWord3,
 99574                shiftedWord2,
 99575                shiftedWord1,
 99576                shiftedWord0) >= 0)
 577            {
 96578                WideArithmetic.SubtractUnsigned(
 96579                    ref numeratorWord4,
 96580                    ref numeratorWord3,
 96581                    ref numeratorWord2,
 96582                    ref numeratorWord1,
 96583                    ref numeratorWord0,
 96584                    shiftedWord4,
 96585                    shiftedWord3,
 96586                    shiftedWord2,
 96587                    shiftedWord1,
 96588                    shiftedWord0);
 96589                guardedQuotient |= 1UL << bit;
 590            }
 591
 99592            WideArithmetic.ShiftRightOne(
 99593                ref shiftedWord4,
 99594                ref shiftedWord3,
 99595                ref shiftedWord2,
 99596                ref shiftedWord1,
 99597                ref shiftedWord0);
 598        }
 599
 408600        for (int bit = FixedMath.SHIFT_AMOUNT_I; bit >= 0; bit--)
 601        {
 198602            WideArithmetic.ShiftLeftOne(
 198603                ref numeratorWord4,
 198604                ref numeratorWord3,
 198605                ref numeratorWord2,
 198606                ref numeratorWord1,
 198607                ref numeratorWord0);
 198608            guardedQuotient <<= 1;
 198609            if (WideArithmetic.CompareUnsigned(
 198610                numeratorWord4,
 198611                numeratorWord3,
 198612                numeratorWord2,
 198613                numeratorWord1,
 198614                numeratorWord0,
 198615                denominatorWord4,
 198616                denominatorWord3,
 198617                denominatorWord2,
 198618                denominatorWord1,
 198619                denominatorWord0) < 0)
 620            {
 621                continue;
 622            }
 623
 101624            WideArithmetic.SubtractUnsigned(
 101625                ref numeratorWord4,
 101626                ref numeratorWord3,
 101627                ref numeratorWord2,
 101628                ref numeratorWord1,
 101629                ref numeratorWord0,
 101630                denominatorWord4,
 101631                denominatorWord3,
 101632                denominatorWord2,
 101633                denominatorWord1,
 101634                denominatorWord0);
 101635            guardedQuotient |= 1UL;
 636        }
 637
 6638        ulong magnitude = RoundGuardedQuotientToEven(
 6639            guardedQuotient,
 6640            (numeratorWord4 | numeratorWord3 | numeratorWord2 | numeratorWord1 | numeratorWord0) != 0UL,
 6641            out bool roundedOverflow);
 6642        ulong limit = negative ? 1UL << 63 : (ulong)long.MaxValue;
 6643        if (roundedOverflow || magnitude > limit)
 3644            return negative ? MinValue : MaxValue;
 645
 3646        long raw = negative ? unchecked(-(long)magnitude) : (long)magnitude;
 3647        return new Fixed64(raw);
 648    }
 649
 650    /// <summary>
 651    /// Converts an arbitrary signed five-word ratio to Q32.32 when its final
 652    /// round-half-to-even result is representable.
 653    /// </summary>
 654    internal static bool TryGetSignedRatio(
 655        Signed320 numerator,
 656        Signed320 denominator,
 657        out Fixed64 result)
 658    {
 87659        int numeratorSign = numerator.Sign;
 87660        int denominatorSign = denominator.Sign;
 87661        if (denominatorSign == 0)
 662        {
 1663            result = default;
 1664            return false;
 665        }
 86666        if (numeratorSign == 0)
 667        {
 23668            result = Zero;
 23669            return true;
 670        }
 671
 63672        WideArithmetic.GetMagnitude(
 63673            numerator,
 63674            out ulong numeratorWord4,
 63675            out ulong numeratorWord3,
 63676            out ulong numeratorWord2,
 63677            out ulong numeratorWord1,
 63678            out ulong numeratorWord0);
 63679        WideArithmetic.GetMagnitude(
 63680            denominator,
 63681            out ulong denominatorWord4,
 63682            out ulong denominatorWord3,
 63683            out ulong denominatorWord2,
 63684            out ulong denominatorWord1,
 63685            out ulong denominatorWord0);
 63686        Signed320 numeratorMagnitude = new(
 63687            numeratorWord4, numeratorWord3, numeratorWord2, numeratorWord1, numeratorWord0);
 63688        Signed320 denominatorMagnitude = new(
 63689            denominatorWord4, denominatorWord3, denominatorWord2, denominatorWord1, denominatorWord0);
 63690        Signed320 twiceScale = Signed320.ExtendValue(
 63691            Signed192.Signed(FixedMath.ONE_L * 2L));
 63692        bool negative = numeratorSign != denominatorSign;
 63693        Signed192 roundedLimit = negative
 63694            ? new Signed192(0UL, 1UL, 1UL)             // 2 * 2^63 + 1
 63695            : new Signed192(0UL, 0UL, ulong.MaxValue); // 2 * (2^63 - 1) + 1
 63696        Signed576 scaledNumerator = GetNonNegativeProduct(numeratorMagnitude, twiceScale);
 63697        Signed576 scaledLimit = GetNonNegativeProduct(
 63698            denominatorMagnitude,
 63699            Signed320.ExtendValue(roundedLimit));
 63700        int comparison = WideArithmetic.CompareNonNegative(scaledNumerator, scaledLimit);
 63701        if (comparison > 0 || (!negative && comparison == 0))
 702        {
 18703            result = default;
 18704            return false;
 705        }
 706
 45707        result = GetSignedRatio(numerator, denominator);
 45708        return true;
 709    }
 710
 711    private static Signed576 GetNonNegativeProduct(Signed320 leftMagnitude, Signed320 right)
 712    {
 126713        Signed576 product = WideArithmetic.MultiplySigned320(leftMagnitude, right);
 126714        return product.Sign < 0
 126715            ? WideArithmetic.SubtractSigned576(default, product)
 126716            : product;
 717    }
 718
 719    /// <summary>
 720    /// Converts an exact signed five-word ratio with a positive single-word
 721    /// denominator to a raw integer with round-half-to-even.
 722    /// </summary>
 723    /// <remarks>
 724    /// The caller owns the invariant that the quotient is representable.
 725    /// </remarks>
 726    internal static Fixed64 GetSignedRawRatio(Signed320 numerator, Signed192 denominator)
 727    {
 2503728        int numeratorSign = numerator.Sign;
 2503729        if (numeratorSign == 0)
 200730            return Zero;
 731
 2303732        WideArithmetic.GetMagnitude(
 2303733            denominator,
 2303734            out _,
 2303735            out _,
 2303736            out ulong denominatorLow);
 2303737        WideArithmetic.GetMagnitude(
 2303738            numerator,
 2303739            out _,
 2303740            out _,
 2303741            out _,
 2303742            out ulong word1,
 2303743            out ulong word0);
 744
 2303745        ulong quotient = Divide128By64(
 2303746            word1,
 2303747            word0,
 2303748            denominatorLow,
 2303749            out ulong remainder);
 2303750        int midpointComparison = remainder.CompareTo(denominatorLow - remainder);
 2303751        if (midpointComparison > 0 || (midpointComparison == 0 && (quotient & 1UL) != 0UL))
 629752            quotient++;
 753
 2303754        long raw = numeratorSign < 0 ? unchecked(-(long)quotient) : (long)quotient;
 2303755        return new Fixed64(raw);
 756    }
 757
 758    /// <summary>
 759    /// Converts an exact signed nine-word ratio directly to a raw integer with
 760    /// round-half-to-even. Unlike <c>GetSignedRatio</c>, this method does not
 761    /// apply an additional Q32.32 scale.
 762    /// </summary>
 763    internal static bool TryGetSignedRawRatio(
 764        Signed576 numerator,
 765        Signed576 denominator,
 766        out Fixed64 result)
 767    {
 95930768        int numeratorSign = numerator.Sign;
 95930769        int denominatorSign = denominator.Sign;
 95930770        if (denominatorSign == 0)
 771        {
 3772            result = default;
 3773            return false;
 774        }
 95927775        if (numeratorSign == 0)
 776        {
 21708777            result = Zero;
 21708778            return true;
 779        }
 780
 74219781        bool negative = numeratorSign != denominatorSign;
 74219782        if (Signed192.TryNarrowSigned(numerator, out Signed192 numerator192)
 74219783            && Signed192.TryNarrowSigned(denominator, out Signed192 denominator192))
 784        {
 69669785            Span<ulong> narrowRemainder = stackalloc ulong[3];
 69669786            Span<ulong> narrowDenominator = stackalloc ulong[3];
 69669787            WideArithmetic.GetMagnitude(
 69669788                numerator192,
 69669789                out narrowRemainder[2], out narrowRemainder[1], out narrowRemainder[0]);
 69669790            WideArithmetic.GetMagnitude(
 69669791                denominator192,
 69669792                out narrowDenominator[2], out narrowDenominator[1], out narrowDenominator[0]);
 69669793            return TryGetSignedRawRatioCore(
 69669794                narrowRemainder,
 69669795                narrowDenominator,
 69669796                negative,
 69669797                roundToEven: true,
 69669798                out result);
 799        }
 4550800        if (Signed320.TryNarrowSigned(numerator, out Signed320 numerator320)
 4550801            && Signed320.TryNarrowSigned(denominator, out Signed320 denominator320))
 802        {
 4421803            Span<ulong> mediumRemainder = stackalloc ulong[5];
 4421804            Span<ulong> mediumDenominator = stackalloc ulong[5];
 4421805            WideArithmetic.GetMagnitude(
 4421806                numerator320,
 4421807                out mediumRemainder[4], out mediumRemainder[3], out mediumRemainder[2],
 4421808                out mediumRemainder[1], out mediumRemainder[0]);
 4421809            WideArithmetic.GetMagnitude(
 4421810                denominator320,
 4421811                out mediumDenominator[4], out mediumDenominator[3], out mediumDenominator[2],
 4421812                out mediumDenominator[1], out mediumDenominator[0]);
 4421813            return TryGetSignedRawRatioCore(
 4421814                mediumRemainder,
 4421815                mediumDenominator,
 4421816                negative,
 4421817                roundToEven: true,
 4421818                out result);
 819        }
 820
 129821        Span<ulong> remainder = stackalloc ulong[9];
 129822        Span<ulong> denominatorMagnitude = stackalloc ulong[9];
 129823        WideArithmetic.GetMagnitude(numerator, remainder);
 129824        WideArithmetic.GetMagnitude(denominator, denominatorMagnitude);
 129825        return TryGetSignedRawRatioCore(
 129826            remainder,
 129827            denominatorMagnitude,
 129828            negative,
 129829            roundToEven: true,
 129830            out result);
 831    }
 832
 833    /// <summary>
 834    /// Converts an exact signed eleven-word ratio directly to a raw integer
 835    /// with round-half-to-even.
 836    /// </summary>
 837    internal static bool TryGetSignedRawRatio(
 838        Signed704 numerator,
 839        Signed704 denominator,
 840        out Fixed64 result)
 841    {
 34012842        int numeratorSign = numerator.Sign;
 34012843        int denominatorSign = denominator.Sign;
 34012844        if (denominatorSign == 0)
 845        {
 1846            result = default;
 1847            return false;
 848        }
 34011849        if (numeratorSign == 0)
 850        {
 3905851            result = Zero;
 3905852            return true;
 853        }
 854
 30106855        Span<ulong> remainder = stackalloc ulong[11];
 30106856        Span<ulong> denominatorMagnitude = stackalloc ulong[11];
 30106857        WideArithmetic.GetMagnitude(numerator, remainder);
 30106858        WideArithmetic.GetMagnitude(denominator, denominatorMagnitude);
 30106859        return TryGetSignedRawRatioCore(
 30106860            remainder,
 30106861            denominatorMagnitude,
 30106862            numeratorSign != denominatorSign,
 30106863            roundToEven: true,
 30106864            out result);
 865    }
 866
 867    internal static bool TryGetSignedRawRatio(
 868        Signed832 numerator,
 869        Signed832 denominator,
 870        int numeratorLeftShift,
 871        out Fixed64 result)
 872    {
 592873        int numeratorSign = numerator.Sign;
 592874        int denominatorSign = denominator.Sign;
 592875        if (denominatorSign == 0)
 876        {
 1877            result = default;
 1878            return false;
 879        }
 591880        if (numeratorSign == 0)
 881        {
 10882            result = Zero;
 10883            return true;
 884        }
 885
 581886        Span<ulong> numeratorMagnitude = stackalloc ulong[14];
 581887        Span<ulong> denominatorMagnitude = stackalloc ulong[14];
 581888        Span<ulong> shiftedNumerator = stackalloc ulong[14];
 581889        numeratorMagnitude.Clear();
 581890        denominatorMagnitude.Clear();
 581891        shiftedNumerator.Clear();
 581892        WideArithmetic.GetMagnitude(numerator, numeratorMagnitude);
 581893        WideArithmetic.GetMagnitude(denominator, denominatorMagnitude);
 581894        ShiftLeftMagnitude(
 581895            numeratorMagnitude,
 581896            numeratorLeftShift,
 581897            shiftedNumerator);
 581898        return TryGetSignedRawRatioCore(
 581899            shiftedNumerator,
 581900            denominatorMagnitude,
 581901            numeratorSign != denominatorSign,
 581902            roundToEven: true,
 581903            out result);
 904    }
 905
 906    internal static Fixed64 GetNonNegativeRawRatioFloor(
 907        Signed704 numerator,
 908        Signed704 denominator)
 909    {
 6378910        if (numerator.Sign <= 0)
 1679911            return Zero;
 912
 4699913        Span<ulong> remainder = stackalloc ulong[11];
 4699914        Span<ulong> denominatorMagnitude = stackalloc ulong[11];
 4699915        WideArithmetic.GetMagnitude(numerator, remainder);
 4699916        WideArithmetic.GetMagnitude(denominator, denominatorMagnitude);
 4699917        return TryGetSignedRawRatioCore(
 4699918            remainder,
 4699919            denominatorMagnitude,
 4699920            negative: false,
 4699921            roundToEven: false,
 4699922            out Fixed64 result)
 4699923            ? result
 4699924            : MaxValue;
 925    }
 926
 927    internal static bool TryGetSignedRawRatio(
 928        ReadOnlySpan<ulong> numeratorMagnitude,
 929        ReadOnlySpan<ulong> denominatorMagnitude,
 930        bool negative,
 931        out Fixed64 result)
 932    {
 5933        int denominatorLength =
 5934            GetActiveMagnitudeLength(denominatorMagnitude);
 5935        if (denominatorLength == 1
 5936            && denominatorMagnitude[0] == 0UL)
 937        {
 1938            result = default;
 1939            return false;
 940        }
 941
 4942        int numeratorLength =
 4943            GetActiveMagnitudeLength(numeratorMagnitude);
 4944        if (numeratorLength == 1
 4945            && numeratorMagnitude[0] == 0UL)
 946        {
 1947            result = Zero;
 1948            return true;
 949        }
 950
 3951        int length = Math.Max(numeratorLength, denominatorLength);
 3952        Span<ulong> remainder = stackalloc ulong[length];
 3953        Span<ulong> denominator = stackalloc ulong[length];
 3954        remainder.Clear();
 3955        denominator.Clear();
 3956        numeratorMagnitude[..numeratorLength].CopyTo(remainder);
 3957        denominatorMagnitude[..denominatorLength].CopyTo(denominator);
 3958        return TryGetSignedRawRatioCore(
 3959            remainder,
 3960            denominator,
 3961            negative,
 3962            roundToEven: true,
 3963            out result);
 964    }
 965
 966    private static bool TryGetSignedRawRatioCore(
 967        Span<ulong> remainder,
 968        Span<ulong> denominatorMagnitude,
 969        bool negative,
 970        bool roundToEven,
 971        out Fixed64 result)
 972    {
 109769973        int remainderLength = GetActiveMagnitudeLength(remainder);
 109769974        int denominatorLength = GetActiveMagnitudeLength(denominatorMagnitude);
 109769975        if (denominatorLength == 1
 109769976            && denominatorMagnitude[0] == 0UL)
 977        {
 2978            result = default;
 2979            return false;
 980        }
 981
 109767982        int activeLength = Math.Max(remainderLength, denominatorLength);
 109767983        Span<ulong> activeRemainder = remainder[..activeLength];
 109767984        Span<ulong> activeDenominator = denominatorMagnitude[..activeLength];
 109767985        int quotientBit = GetMagnitudeBitLength(remainder[..remainderLength])
 109767986            - GetMagnitudeBitLength(denominatorMagnitude[..denominatorLength]);
 109767987        if (quotientBit > 63)
 988        {
 153989            result = default;
 153990            return false;
 991        }
 992
 109614993        if (denominatorLength == 1)
 994        {
 49629995            ulong denominator = denominatorMagnitude[0];
 49629996            ulong numeratorHigh = remainderLength == 2 ? remainder[1] : 0UL;
 49629997            ulong singleWordQuotient = Divide128By64(
 49629998                numeratorHigh,
 49629999                remainder[0],
 496291000                denominator,
 496291001                out ulong singleWordRemainder);
 496291002            int singleWordMidpointComparison = roundToEven
 496291003                ? singleWordRemainder.CompareTo(denominator - singleWordRemainder)
 496291004                : -1;
 496291005            return TryCreateRawRatioResult(
 496291006                singleWordQuotient,
 496291007                singleWordMidpointComparison,
 496291008                negative,
 496291009                out result);
 1010        }
 1011
 599851012        ulong quotient = 0UL;
 599851013        if (quotientBit >= 0)
 1014        {
 598971015            Span<ulong> shiftedDenominatorStorage =
 598971016                stackalloc ulong[denominatorMagnitude.Length];
 598971017            Span<ulong> shiftedDenominator = shiftedDenominatorStorage[..activeLength];
 598971018            ShiftLeftMagnitude(activeDenominator, quotientBit, shiftedDenominator);
 40405041019            for (int bit = quotientBit; bit >= 0; bit--)
 1020            {
 19603551021                if (WideArithmetic.CompareMagnitudeEqualLength(
 19603551022                        activeRemainder,
 19603551023                        shiftedDenominator) >= 0)
 1024                {
 5191081025                    WideArithmetic.SubtractEqualMagnitudes(
 5191081026                        activeRemainder,
 5191081027                        shiftedDenominator,
 5191081028                        activeRemainder);
 5191081029                    quotient |= 1UL << bit;
 1030                }
 1031
 19603551032                ShiftRightOne(shiftedDenominator);
 1033            }
 1034        }
 1035
 599851036        int midpointComparison = -1;
 599851037        if (roundToEven)
 1038        {
 577161039            Span<ulong> denominatorMinusRemainderStorage =
 577161040                stackalloc ulong[denominatorMagnitude.Length];
 577161041            Span<ulong> denominatorMinusRemainder =
 577161042                denominatorMinusRemainderStorage[..activeLength];
 577161043            activeDenominator.CopyTo(denominatorMinusRemainder);
 577161044            WideArithmetic.SubtractEqualMagnitudes(
 577161045                denominatorMinusRemainder,
 577161046                activeRemainder,
 577161047                denominatorMinusRemainder);
 577161048            midpointComparison = WideArithmetic.CompareMagnitudeEqualLength(
 577161049                activeRemainder,
 577161050                denominatorMinusRemainder);
 1051        }
 599851052        return TryCreateRawRatioResult(
 599851053            quotient,
 599851054            midpointComparison,
 599851055            negative,
 599851056            out result);
 1057    }
 1058
 1059    private static bool TryCreateRawRatioResult(
 1060        ulong quotient,
 1061        int midpointComparison,
 1062        bool negative,
 1063        out Fixed64 result)
 1064    {
 1096141065        if (midpointComparison > 0 || (midpointComparison == 0 && (quotient & 1UL) != 0UL))
 1066        {
 336841067            quotient++;
 336841068            if (quotient == 0UL)
 1069            {
 11070                result = default;
 11071                return false;
 1072            }
 1073        }
 1074
 1096131075        ulong limit = negative ? 1UL << 63 : (ulong)long.MaxValue;
 1096131076        if (quotient > limit)
 1077        {
 1761078            result = default;
 1761079            return false;
 1080        }
 1081
 1094371082        long raw = negative ? unchecked(-(long)quotient) : (long)quotient;
 1094371083        result = new Fixed64(raw);
 1094371084        return true;
 1085    }
 1086
 1087    private static int GetMagnitudeBitLength(ReadOnlySpan<ulong> value)
 1088    {
 2195341089        int index = value.Length - 1;
 2195341090        return (index * 64) + 64 - CountLeadingZeroes(value[index]);
 1091    }
 1092
 1093    private static int GetActiveMagnitudeLength(ReadOnlySpan<ulong> value)
 1094    {
 2195471095        int length = value.Length;
 7928451096        while (length > 1 && value[length - 1] == 0UL)
 5732981097            length--;
 2195471098        return length;
 1099    }
 1100
 1101    private static void ShiftLeftMagnitude(
 1102        ReadOnlySpan<ulong> source,
 1103        int bits,
 1104        Span<ulong> destination)
 1105    {
 606391106        if (bits == 0)
 1107        {
 4201108            source.CopyTo(destination);
 4201109            return;
 1110        }
 1111
 602191112        destination[0] = source[0] << bits;
 6043081113        for (int index = 1; index < destination.Length; index++)
 2419351114            destination[index] = (source[index] << bits) | (source[index - 1] >> (64 - bits));
 602191115    }
 1116
 1117    private static void ShiftRightOne(Span<ulong> value)
 1118    {
 19603551119        ulong carry = 0UL;
 229478121120        for (int index = value.Length - 1; index >= 0; index--)
 1121        {
 95135511122            ulong nextCarry = value[index] << 63;
 95135511123            value[index] = (value[index] >> 1) | carry;
 95135511124            carry = nextCarry;
 1125        }
 19603551126    }
 1127}

Methods/Properties

IsAddOrSubtractResultExact(System.Int64,System.Int64,System.Int64)
TryAdd(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&)
TrySubtract(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&)
TryAddSubtract(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&)
TrySubtractSums(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&)
AbsToUInt64(System.Int64)
Multiply64To128(System.UInt64,System.UInt64,System.UInt64&,System.UInt64&)
IsMagnitudeRepresentable(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
GetRoundedMagnitude(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
CompareMagnitudeSquared(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
GetMagnitudeSquaredWords(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,System.UInt64&,System.UInt64&,System.UInt64&)
GetFloorSquareRoot(System.UInt64,System.UInt64,System.UInt64&,System.UInt64&)
AddMagnitudeSquare(System.Int64,System.UInt64&,System.UInt64&,System.UInt64&)
ShiftRightRoundedToEven(System.UInt64,System.UInt64,System.Int32,System.Boolean&)
DivideMagnitude(System.UInt64,System.UInt64,System.Boolean)
RoundGuardedQuotientToEven(System.UInt64,System.Boolean,System.Boolean&)
op_Explicit(System.Int64)
op_Explicit(FixedMathSharp.Fixed64)
op_Explicit(System.Int32)
op_Explicit(FixedMathSharp.Fixed64)
op_Explicit(System.Single)
op_Explicit(FixedMathSharp.Fixed64)
op_Explicit(System.Double)
op_Explicit(FixedMathSharp.Fixed64)
op_Explicit(System.Decimal)
op_Explicit(FixedMathSharp.Fixed64)
ToString()
ToString(System.String)
ToString(System.String,System.IFormatProvider)
TryFormat(System.Span`1<System.Char>,System.Int32&,System.ReadOnlySpan`1<System.Char>,System.IFormatProvider)
Parse(System.String)
Parse(System.String,System.IFormatProvider)
ParseRaw(System.String)
TryParse(System.String,FixedMathSharp.Fixed64&)
TryParse(System.String,System.IFormatProvider,FixedMathSharp.Fixed64&)
TryParseRaw(System.String,FixedMathSharp.Fixed64&)
FromRaw(System.Int64)
ToInt(FixedMathSharp.Fixed64)
FromDouble(System.Double)
FromDecimal(System.Decimal)
FromFraction(System.Double,System.Double)
ToDouble(System.Int64)
ToFloat(System.Int64)
ToDecimal(System.Int64)
get_MaxValue()
get_MinValue()
get_One()
get_NegOne()
get_Two()
get_Three()
get_Half()
get_Quarter()
get_Eighth()
get_Zero()
get_Pi()
get_TwoPi()
get_HalfPi()
get_PiOver3()
get_PiOver4()
get_PiOver6()
get_InvertedPi()
get_OneEighty()
get_Deg2Rad()
get_Rad2Deg()
get_Ln2()
get_Log2Max()
get_Log2Min()
get_PadeA1()
get_PadeA2()
get_SinCoeff3()
get_SinCoeff5()
get_SinCoeff7()
get_CosCoeff2()
get_CosCoeff4()
get_CosCoeff6()
get_CosCoeff8()
get_MinIncrement()
get_Epsilon()
.ctor(System.Int64)
.ctor(System.Int32)
Offset(System.Int32)
ToRawString()
Equals(System.Object)
Equals(FixedMathSharp.Fixed64)
Equals(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
GetHashCode()
GetHashCode(FixedMathSharp.Fixed64)
CompareTo(FixedMathSharp.Fixed64)
MultiplyAdd(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
TryMultiplyAdd(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&)
MultiplyAdd(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,System.Boolean&)
TryMultiplySubtractClamped(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&)
TryMultiplySubtractClamped(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&)
TryMultiplySubtractClamped(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&)
GetExactRawProduct(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
TryRoundNonnegativeTwoFactorDifference(FixedMathSharp.Signed192,FixedMathSharp.Fixed64&)
TryRoundNonnegativeThreeFactorDifference(FixedMathSharp.Signed320,FixedMathSharp.Fixed64&)
TryRoundNonnegativeFourFactorDifference(FixedMathSharp.Signed320,FixedMathSharp.Fixed64&)
TryRoundNonnegative(System.UInt64,System.Boolean,System.Boolean,FixedMathSharp.Fixed64&)
TryMultiplyDifference(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&)
TryMultiplyDivide(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&)
MultiplyDivide(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,System.Boolean&)
TryMultiplyDivide(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&)
TryMultiplyDivideBySum(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&)
MultiplyDivide(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,System.Boolean&)
CountTrailingZeroes(System.UInt64)
CancelCommonPowersOfTwo(System.UInt64&,System.UInt64&,System.UInt64&)
CancelCommonPowersOfTwo(System.UInt64&,System.UInt64&,System.UInt64&,System.UInt64&)
Divide128By64(System.UInt64,System.UInt64,System.UInt64,System.UInt64&)
RoundAndApplySign(System.UInt64,System.Boolean,System.Boolean,System.Boolean,System.Boolean&)
ApplySignedMagnitude(System.UInt64,System.Boolean,System.Boolean&)
op_Addition(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
op_Addition(FixedMathSharp.Fixed64,System.Int32)
op_Addition(System.Int32,FixedMathSharp.Fixed64)
op_Subtraction(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
op_Subtraction(FixedMathSharp.Fixed64,System.Int32)
op_Subtraction(System.Int32,FixedMathSharp.Fixed64)
op_Multiply(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
op_Multiply(FixedMathSharp.Fixed64,System.Int32)
op_Multiply(System.Int32,FixedMathSharp.Fixed64)
op_Division(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
op_Division(FixedMathSharp.Fixed64,System.Int32)
op_Division(System.Int32,FixedMathSharp.Fixed64)
op_Modulus(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
op_Modulus(FixedMathSharp.Fixed64,System.Int32)
op_Modulus(System.Int32,FixedMathSharp.Fixed64)
op_UnaryNegation(FixedMathSharp.Fixed64)
op_Increment(FixedMathSharp.Fixed64)
op_Decrement(FixedMathSharp.Fixed64)
op_LeftShift(FixedMathSharp.Fixed64,System.Int32)
op_RightShift(FixedMathSharp.Fixed64,System.Int32)
op_GreaterThan(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
op_GreaterThan(FixedMathSharp.Fixed64,System.Int32)
op_GreaterThan(System.Int32,FixedMathSharp.Fixed64)
op_LessThan(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
op_LessThan(FixedMathSharp.Fixed64,System.Int32)
op_LessThan(System.Int32,FixedMathSharp.Fixed64)
op_GreaterThanOrEqual(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
op_GreaterThanOrEqual(FixedMathSharp.Fixed64,System.Int32)
op_GreaterThanOrEqual(System.Int32,FixedMathSharp.Fixed64)
op_LessThanOrEqual(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
op_LessThanOrEqual(FixedMathSharp.Fixed64,System.Int32)
op_LessThanOrEqual(System.Int32,FixedMathSharp.Fixed64)
op_Equality(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
op_Equality(FixedMathSharp.Fixed64,System.Int32)
op_Equality(System.Int32,FixedMathSharp.Fixed64)
op_Inequality(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
op_Inequality(FixedMathSharp.Fixed64,System.Int32)
op_Inequality(System.Int32,FixedMathSharp.Fixed64)
CountLeadingZeroes(System.UInt64)
Sign(FixedMathSharp.Fixed64)
IsInteger(FixedMathSharp.Fixed64)
CompareProducts(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
CompareProducts(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
LerpFullDomain(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
ProjectNonNegativeDifference(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
RoundSquaredDistance(FixedMathSharp.Signed192)
BarycentricCoordinateFullDomain(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
RoundSignedToFixed(FixedMathSharp.Signed192,System.Int32)
RoundSquareRootToFixed(FixedMathSharp.Signed192,FixedMathSharp.Signed192,System.Int32)
NormalizeWideComponent(FixedMathSharp.Signed192,FixedMathSharp.Signed320,FixedMathSharp.Signed192,FixedMathSharp.Signed320)
TryAddProducts(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&)
TryAddProducts(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&)
TryAddScaledProducts(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&)
TrySubtractProducts(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&)
TryRoundProductCombination(FixedMathSharp.Signed192,FixedMathSharp.Signed192,System.Boolean,FixedMathSharp.Fixed64&)
TryGetUnitIntervalRatio(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Fixed64&)
TryGetUnitIntervalRatio(FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Fixed64&)
GetUnitIntervalRatio(System.UInt64,System.UInt64,System.UInt64,System.UInt64,System.UInt64,System.UInt64,System.UInt64,System.UInt64,System.UInt64,System.UInt64)
GetUnitIntervalRatio(System.UInt64,System.UInt64,System.UInt64,System.UInt64,System.UInt64,System.UInt64)
GetUnitIntervalRatio128(System.UInt64,System.UInt64,System.UInt64,System.UInt64)
GetUnitIntervalRatio(System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.UInt64>)
GetSignedRatio(FixedMathSharp.Signed192,FixedMathSharp.Signed192)
GetSignedRatio(FixedMathSharp.Signed320,FixedMathSharp.Signed320)
TryGetSignedRatio(FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Fixed64&)
GetNonNegativeProduct(FixedMathSharp.Signed320,FixedMathSharp.Signed320)
GetSignedRawRatio(FixedMathSharp.Signed320,FixedMathSharp.Signed192)
TryGetSignedRawRatio(FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Fixed64&)
TryGetSignedRawRatio(FixedMathSharp.Signed704,FixedMathSharp.Signed704,FixedMathSharp.Fixed64&)
TryGetSignedRawRatio(FixedMathSharp.Signed832,FixedMathSharp.Signed832,System.Int32,FixedMathSharp.Fixed64&)
GetNonNegativeRawRatioFloor(FixedMathSharp.Signed704,FixedMathSharp.Signed704)
TryGetSignedRawRatio(System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.UInt64>,System.Boolean,FixedMathSharp.Fixed64&)
TryGetSignedRawRatioCore(System.Span`1<System.UInt64>,System.Span`1<System.UInt64>,System.Boolean,System.Boolean,FixedMathSharp.Fixed64&)
TryCreateRawRatioResult(System.UInt64,System.Int32,System.Boolean,FixedMathSharp.Fixed64&)
GetMagnitudeBitLength(System.ReadOnlySpan`1<System.UInt64>)
GetActiveMagnitudeLength(System.ReadOnlySpan`1<System.UInt64>)
ShiftLeftMagnitude(System.ReadOnlySpan`1<System.UInt64>,System.Int32,System.Span`1<System.UInt64>)
ShiftRightOne(System.Span`1<System.UInt64>)