< Summary

Line coverage
100%
Covered lines: 1770
Uncovered lines: 0
Coverable lines: 1770
Total lines: 4010
Line coverage: 100%
Branch coverage
100%
Covered branches: 668
Total branches: 668
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%3232100%
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%44100%
File 2: ParseRaw(...)100%22100%
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: TryRoundNonnegativeQ128(...)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%66100%
File 5: MultiplyDivide(...)100%1010100%
File 5: CountTrailingZeroes(...)100%11100%
File 5: CancelCommonPowersOfTwo(...)100%88100%
File 5: CancelCommonPowersOfTwo(...)100%1010100%
File 5: Divide128By64(...)100%1414100%
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%11100%
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%66100%
File 9: TryGetSignedRawRatio(...)100%1212100%
File 9: TryGetSignedRawRatio(...)100%44100%
File 9: TryGetSignedRawRatio(...)100%44100%
File 9: GetNonNegativeRawRatioFloor(...)100%44100%
File 9: TryGetSignedRawRatio(...)100%1818100%
File 9: TryGetBinaryReducedSignedRawRatio(...)100%11100%
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// Fixed64.Arithmetic.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/// Arithmetic operations for <see cref="Fixed64"/>, including overflow-safe
 15/// try-add/try-subtract helpers and related combined operations.
 16/// </content>
 17public partial struct Fixed64
 18{
 19    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 20    private static bool IsAddOrSubtractResultExact(long left, long result, long overflowMask) =>
 335474121        (overflowMask & (left ^ result)) >= 0;
 22
 23    /// <summary>
 24    /// Attempts to add two values without saturation.
 25    /// </summary>
 26    /// <param name="left">The left operand.</param>
 27    /// <param name="right">The right operand.</param>
 28    /// <param name="result">
 29    /// The exact sum when representable; otherwise, <see langword="default"/>.
 30    /// </param>
 31    /// <returns><see langword="true"/> when the exact sum is representable; otherwise, <see langword="false"/>.</return
 32    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 33    public static bool TryAdd(Fixed64 left, Fixed64 right, out Fixed64 result)
 34    {
 73735        long leftRaw = left.m_rawValue;
 73736        long rightRaw = right.m_rawValue;
 73737        long rawResult = unchecked(leftRaw + rightRaw);
 73738        if (!IsAddOrSubtractResultExact(leftRaw, rawResult, ~(leftRaw ^ rightRaw)))
 39        {
 2240            result = default;
 2241            return false;
 42        }
 43
 71544        result = new Fixed64(rawResult);
 71545        return true;
 46    }
 47
 48    /// <summary>
 49    /// Attempts to subtract two values without saturation.
 50    /// </summary>
 51    /// <param name="left">The left operand.</param>
 52    /// <param name="right">The right operand.</param>
 53    /// <param name="result">
 54    /// The exact difference when representable; otherwise, <see langword="default"/>.
 55    /// </param>
 56    /// <returns><see langword="true"/> when the exact difference is representable; otherwise, <see langword="false"/>.<
 57    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 58    public static bool TrySubtract(Fixed64 left, Fixed64 right, out Fixed64 result)
 59    {
 1732360        long leftRaw = left.m_rawValue;
 1732361        long rightRaw = right.m_rawValue;
 1732362        long rawResult = unchecked(leftRaw - rightRaw);
 1732363        if (!IsAddOrSubtractResultExact(leftRaw, rawResult, leftRaw ^ rightRaw))
 64        {
 1365            result = default;
 1366            return false;
 67        }
 68
 1731069        result = new Fixed64(rawResult);
 1731070        return true;
 71    }
 72
 73    /// <summary>
 74    /// Attempts to add two values and subtract a third without intermediate saturation.
 75    /// </summary>
 76    /// <param name="firstAddend">The first addend.</param>
 77    /// <param name="secondAddend">The second addend.</param>
 78    /// <param name="subtrahend">The value to subtract from the exact sum.</param>
 79    /// <param name="result">
 80    /// The exact result when representable; otherwise, <see langword="default"/>.
 81    /// </param>
 82    /// <returns>
 83    /// <see langword="true"/> when the exact result is representable; otherwise, <see langword="false"/>.
 84    /// </returns>
 85    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 86    public static bool TryAddSubtract(
 87        Fixed64 firstAddend,
 88        Fixed64 secondAddend,
 89        Fixed64 subtrahend,
 90        out Fixed64 result)
 91    {
 2725592        long firstRaw = firstAddend.m_rawValue;
 2725593        long secondRaw = secondAddend.m_rawValue;
 2725594        ulong firstLow = unchecked((ulong)firstRaw);
 2725595        ulong secondLow = unchecked((ulong)secondRaw);
 2725596        ulong low = unchecked(firstLow + secondLow);
 2725597        long high = (firstRaw >> 63) + (secondRaw >> 63);
 2725598        if (low < firstLow)
 71099            high++;
 100
 27255101        long subtrahendRaw = subtrahend.m_rawValue;
 27255102        ulong subtrahendLow = unchecked((ulong)subtrahendRaw);
 27255103        ulong finalLow = unchecked(low - subtrahendLow);
 27255104        if (low < subtrahendLow)
 4835105            high--;
 27255106        high -= subtrahendRaw >> 63;
 107
 27255108        long finalRaw = unchecked((long)finalLow);
 27255109        if (high != finalRaw >> 63)
 110        {
 425111            result = default;
 425112            return false;
 113        }
 114
 26830115        result = new Fixed64(finalRaw);
 26830116        return true;
 117    }
 118
 119    /// <summary>
 120    /// Attempts to compute <c>(firstLeft + firstRight) -
 121    /// (secondLeft + secondRight)</c> without intermediate saturation.
 122    /// </summary>
 123    /// <param name="firstLeft">The first value in the minuend sum.</param>
 124    /// <param name="firstRight">The second value in the minuend sum.</param>
 125    /// <param name="secondLeft">The first value in the subtrahend sum.</param>
 126    /// <param name="secondRight">The second value in the subtrahend sum.</param>
 127    /// <param name="result">
 128    /// The exact difference when representable; otherwise,
 129    /// <see langword="default"/>.
 130    /// </param>
 131    /// <returns>
 132    /// <see langword="true"/> when the exact result is representable;
 133    /// otherwise, <see langword="false"/>.
 134    /// </returns>
 135    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 136    public static bool TrySubtractSums(
 137        Fixed64 firstLeft,
 138        Fixed64 firstRight,
 139        Fixed64 secondLeft,
 140        Fixed64 secondRight,
 141        out Fixed64 result)
 142    {
 571143        Signed192 exact = WideArithmetic.SubtractSigned192(
 571144            WideArithmetic.AddSigned192(
 571145                Signed192.Signed(firstLeft.m_rawValue),
 571146                Signed192.Signed(firstRight.m_rawValue)),
 571147            WideArithmetic.AddSigned192(
 571148                Signed192.Signed(secondLeft.m_rawValue),
 571149                Signed192.Signed(secondRight.m_rawValue)));
 571150        long raw = unchecked((long)exact.Low);
 571151        ulong extension = raw < 0L ? ulong.MaxValue : 0UL;
 571152        if (exact.High != extension || exact.Middle != extension)
 153        {
 8154            result = default;
 8155            return false;
 156        }
 157
 563158        result = new Fixed64(raw);
 563159        return true;
 160    }
 161
 162    /// <summary>
 163    /// Returns the absolute value of a signed 64-bit integer as an unsigned 64-bit magnitude,
 164    /// safely handling long.MinValue.
 165    /// </summary>
 166    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 167    internal static ulong AbsToUInt64(long value)
 168    {
 15869586169        return value < 0
 15869586170            ? unchecked((ulong)(~value + 1))
 15869586171            : (ulong)value;
 172    }
 173
 174    /// <summary>
 175    /// Computes the exact unsigned 128-bit product of two 64-bit unsigned integers.
 176    /// The result is returned as hi:lo, where product = (hi &lt;&lt; 64) | lo.
 177    /// </summary>
 178    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 179    internal static void Multiply64To128(ulong a, ulong b, out ulong hi, out ulong lo)
 180    {
 181#if NET10_0_OR_GREATER
 2147483647182        hi = Math.BigMul(a, b, out lo);
 183#else
 184        ulong aLo = (uint)a;
 185        ulong aHi = a >> 32;
 186        ulong bLo = (uint)b;
 187        ulong bHi = b >> 32;
 188
 189        ulong p0 = aLo * bLo;
 190        ulong p1 = aLo * bHi;
 191        ulong p2 = aHi * bLo;
 192        ulong p3 = aHi * bHi;
 193
 194        ulong middle = (p0 >> 32) + (uint)p1 + (uint)p2;
 195
 196        lo = (p0 & 0xFFFFFFFFUL) | (middle << 32);
 197        hi = p3 + (p1 >> 32) + (p2 >> 32) + (middle >> 32);
 198#endif
 2147483647199    }
 200
 201    /// <summary>
 202    /// Determines whether the Euclidean magnitude of up to four fixed-point components
 203    /// fits in the positive <see cref="Fixed64"/> range.
 204    /// </summary>
 205    internal static bool IsMagnitudeRepresentable(Fixed64 x, Fixed64 y, Fixed64 z, Fixed64 w)
 206    {
 76207        GetMagnitudeSquaredWords(x, y, z, w, out ulong overflow, out ulong sumHi, out ulong sumLo);
 208
 209        // (long.MaxValue)^2 = 0x3FFF_FFFF_FFFF_FFFF_0000_0000_0000_0001.
 76210        return overflow == 0UL
 76211            && (sumHi < 0x3FFF_FFFF_FFFF_FFFFUL
 76212                || (sumHi == 0x3FFF_FFFF_FFFF_FFFFUL && sumLo <= 1UL));
 213    }
 214
 215    /// <summary>
 216    /// Returns the exactly rounded Euclidean magnitude of up to four Q32.32
 217    /// components, saturating only when the rounded positive result does not fit.
 218    /// </summary>
 219    internal static Fixed64 GetRoundedMagnitude(Fixed64 x, Fixed64 y, Fixed64 z, Fixed64 w)
 220    {
 20028221        GetMagnitudeSquaredWords(x, y, z, w, out ulong overflow, out ulong sumHi, out ulong sumLo);
 20028222        if (overflow != 0UL)
 1223            return MaxValue;
 20027224        if ((sumHi | sumLo) == 0UL)
 1225            return Zero;
 226
 20026227        ulong root = GetFloorSquareRoot(sumHi, sumLo, out _, out ulong remainderLo);
 20026228        if (root > long.MaxValue)
 6911229            return MaxValue;
 230
 13115231        if (remainderLo > root)
 232        {
 6630233            if (root == long.MaxValue)
 1234                return MaxValue;
 235
 6629236            root++;
 237        }
 238
 13114239        return FromRaw((long)root);
 240    }
 241
 242    /// <summary>
 243    /// Compares exact squared magnitudes without projecting their sums back into Q32.32.
 244    /// </summary>
 245    internal static int CompareMagnitudeSquared(
 246        Fixed64 leftX,
 247        Fixed64 leftY,
 248        Fixed64 leftZ,
 249        Fixed64 leftW,
 250        Fixed64 rightX,
 251        Fixed64 rightY,
 252        Fixed64 rightZ,
 253        Fixed64 rightW)
 254    {
 11255        GetMagnitudeSquaredWords(leftX, leftY, leftZ, leftW, out ulong leftOverflow, out ulong leftHi, out ulong leftLo)
 11256        GetMagnitudeSquaredWords(rightX, rightY, rightZ, rightW, out ulong rightOverflow, out ulong rightHi, out ulong r
 257
 11258        if (leftOverflow != rightOverflow)
 2259            return leftOverflow < rightOverflow ? -1 : 1;
 9260        if (leftHi != rightHi)
 4261            return leftHi < rightHi ? -1 : 1;
 5262        if (leftLo != rightLo)
 2263            return leftLo < rightLo ? -1 : 1;
 3264        return 0;
 265    }
 266
 267    private static void GetMagnitudeSquaredWords(
 268        Fixed64 x,
 269        Fixed64 y,
 270        Fixed64 z,
 271        Fixed64 w,
 272        out ulong overflow,
 273        out ulong sumHi,
 274        out ulong sumLo)
 275    {
 20126276        overflow = 0UL;
 20126277        sumHi = 0UL;
 20126278        sumLo = 0UL;
 20126279        AddMagnitudeSquare(x.m_rawValue, ref overflow, ref sumHi, ref sumLo);
 20126280        AddMagnitudeSquare(y.m_rawValue, ref overflow, ref sumHi, ref sumLo);
 20126281        AddMagnitudeSquare(z.m_rawValue, ref overflow, ref sumHi, ref sumLo);
 20126282        AddMagnitudeSquare(w.m_rawValue, ref overflow, ref sumHi, ref sumLo);
 20126283    }
 284
 285    private static ulong GetFloorSquareRoot(
 286        ulong valueHi,
 287        ulong valueLo,
 288        out ulong remainderHi,
 289        out ulong remainderLo)
 290    {
 20026291        int highestBit = valueHi != 0UL
 20026292            ? 127 - CountLeadingZeroes(valueHi)
 20026293            : 63 - CountLeadingZeroes(valueLo);
 20026294        int pairIndex = highestBit >> 1;
 20026295        ulong root = 0UL;
 20026296        remainderHi = 0UL;
 20026297        remainderLo = 0UL;
 298
 2013380299        for (; pairIndex >= 0; pairIndex--)
 300        {
 996677301            ulong pair = pairIndex >= 32
 996677302                ? (valueHi >> ((pairIndex - 32) << 1)) & 3UL
 996677303                : (valueLo >> (pairIndex << 1)) & 3UL;
 304
 996677305            remainderHi = (remainderHi << 2) | (remainderLo >> 62);
 996677306            remainderLo = (remainderLo << 2) | pair;
 996677307            root <<= 1;
 308
 996677309            ulong candidateHi = root >> 63;
 996677310            ulong candidateLo = (root << 1) | 1UL;
 996677311            if (remainderHi < candidateHi
 996677312                || (remainderHi == candidateHi && remainderLo < candidateLo))
 313            {
 314                continue;
 315            }
 316
 504782317            ulong previousRemainderLo = remainderLo;
 504782318            remainderLo -= candidateLo;
 504782319            remainderHi -= candidateHi + (previousRemainderLo < candidateLo ? 1UL : 0UL);
 504782320            root++;
 321        }
 322
 20026323        return root;
 324    }
 325
 326    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 327    private static void AddMagnitudeSquare(
 328        long component,
 329        ref ulong overflow,
 330        ref ulong sumHi,
 331        ref ulong sumLo)
 332    {
 80504333        ulong magnitude = AbsToUInt64(component);
 80504334        Multiply64To128(magnitude, magnitude, out ulong squareHi, out ulong squareLo);
 335
 80504336        ulong previousLo = sumLo;
 80504337        sumLo += squareLo;
 80504338        ulong addHi = squareHi + (sumLo < previousLo ? 1UL : 0UL);
 80504339        ulong previousHi = sumHi;
 80504340        sumHi += addHi;
 80504341        if (sumHi < previousHi)
 6342            overflow++;
 80504343    }
 344
 345    /// <summary>
 346    /// Shifts the unsigned 128-bit value (hi:lo) right by <paramref name="shift"/> bits,
 347    /// applying round-half-to-even to the discarded bits.
 348    /// </summary>
 349    /// <param name="hi">Upper 64 bits of the 128-bit value.</param>
 350    /// <param name="lo">Lower 64 bits of the 128-bit value.</param>
 351    /// <param name="shift">Number of bits to shift right. Must be in the range 1..63.</param>
 352    /// <param name="overflowed">
 353    /// True if the shifted or rounded result exceeded 64 bits.
 354    /// </param>
 355    /// <returns>
 356    /// The rounded 64-bit result of ((hi:lo) >> shift).
 357    /// </returns>
 358    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 359    private static ulong ShiftRightRoundedToEven(ulong hi, ulong lo, int shift, out bool overflowed)
 360    {
 361        // Preconditions: 1 <= shift <= 63
 362
 363        // Bits above the low 64 result bits must be preserved as saturation state
 364        // before the lower projection can wrap back into range.
 5575563365        overflowed = (hi >> shift) != 0UL;
 366
 367        // Integer part after shifting right by 'shift':
 368        // result = ((hi << (64 - shift)) | (lo >> shift))
 5575563369        ulong result = (hi << (64 - shift)) | (lo >> shift);
 370
 371        // Discarded remainder bits are the low 'shift' bits of lo.
 5575563372        ulong remainderMask = (1UL << shift) - 1UL;
 5575563373        ulong remainder = lo & remainderMask;
 374
 375        // Halfway value among the discarded bits.
 5575563376        ulong half = 1UL << (shift - 1);
 377
 378        // Round-half-to-even:
 379        // - round up if remainder > half
 380        // - if exactly half, round so final result is even
 5575563381        bool shouldRoundUp =
 5575563382            remainder > half ||
 5575563383            (remainder == half && (result & 1UL) != 0);
 384
 5575563385        if (shouldRoundUp)
 386        {
 465970387            ulong incremented = result + 1UL;
 465970388            overflowed |= incremented < result;
 465970389            result = incremented;
 390        }
 391
 5575563392        return result;
 393    }
 394
 395    /// <summary>
 396    /// Divides unsigned raw magnitudes and applies the requested result sign.
 397    /// </summary>
 398    /// <remarks>
 399    /// The divisor magnitude must be in the range 1 through 2^63, inclusive, matching the
 400    /// unsigned magnitude of any signed raw value. This bound keeps remainder doubling within
 401    /// <see cref="ulong"/>. Binary-power divisors use direct shifts; other divisors use a guarded
 402    /// quotient. Both paths round to the nearest even raw value before final saturation.
 403    /// </remarks>
 404    internal static Fixed64 DivideMagnitude(
 405        ulong dividendMagnitude,
 406        ulong divisorMagnitude,
 407        bool negative)
 408    {
 1028017409        if ((divisorMagnitude & (divisorMagnitude - 1UL)) == 0UL)
 410        {
 411            // For divisor 2^k, the raw quotient is dividend * 2^(32-k).
 892851412            int shift = CountLeadingZeroes(divisorMagnitude) - 31;
 413            ulong powerMagnitude;
 892851414            if (shift >= 0)
 415            {
 164732416                ulong limit = negative ? 1UL << 63 : (ulong)long.MaxValue;
 164732417                if (dividendMagnitude > (limit >> shift))
 1341418                    return negative ? MinValue : MaxValue;
 419
 163391420                powerMagnitude = dividendMagnitude << shift;
 421            }
 422            else
 423            {
 424                // A full-width dividend can round up to the negative limit.
 728119425                powerMagnitude = ShiftRightRoundedToEven(0UL, dividendMagnitude, -shift, out _);
 426            }
 427
 891510428            if (powerMagnitude > long.MaxValue)
 270429                return negative ? MinValue : MaxValue;
 430
 891240431            return new Fixed64(negative ? -(long)powerMagnitude : (long)powerMagnitude);
 432        }
 433
 135166434        ulong remainder = dividendMagnitude;
 135166435        ulong divider = divisorMagnitude;
 135166436        ulong quotient = 0UL;
 135166437        int bitPos = FixedMath.SHIFT_AMOUNT_I + 1;
 438
 439        // If the divider is divisible by 2^n, take advantage of it.
 204414440        while ((divider & 0xF) == 0 && bitPos >= 4)
 441        {
 69248442            divider >>= 4;
 69248443            bitPos -= 4;
 444        }
 445
 417502446        while (remainder != 0 && bitPos >= 0)
 447        {
 283314448            int shift = CountLeadingZeroes(remainder);
 283314449            if (shift > bitPos)
 89704450                shift = bitPos;
 451
 283314452            remainder <<= shift;
 283314453            bitPos -= shift;
 454
 283314455            ulong div = remainder / divider;
 283314456            remainder %= divider;
 283314457            quotient += div << bitPos;
 458
 283314459            if ((div & ~(ulong.MaxValue >> bitPos)) != 0)
 978460                return negative ? MinValue : MaxValue;
 461
 282336462            remainder <<= 1;
 282336463            --bitPos;
 464        }
 465
 466        // The sole final rounding carry, (ulong.MaxValue / 2^33), uses the binary-power path above.
 134188467        ulong magnitude = RoundGuardedQuotientToEven(
 134188468            quotient,
 134188469            remainder != 0UL,
 134188470            out _);
 471
 134188472        long result = (long)magnitude;
 134188473        return new Fixed64(negative ? -result : result);
 474    }
 475
 476    /// <summary>
 477    /// Removes the low guard bit and rounds the retained quotient to even.
 478    /// </summary>
 479    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 480    internal static ulong RoundGuardedQuotientToEven(
 481        ulong guardedQuotient,
 482        bool hasTrailingRemainder,
 483        out bool overflowed)
 484    {
 194761485        bool shouldRoundUp = (guardedQuotient & 1UL) != 0UL
 194761486            && (hasTrailingRemainder || (guardedQuotient & 2UL) != 0UL);
 487
 194761488        if (!shouldRoundUp)
 489        {
 143784490            overflowed = false;
 143784491            return guardedQuotient >> 1;
 492        }
 493
 50977494        ulong roundedGuardedQuotient = guardedQuotient + 1UL;
 50977495        overflowed = roundedGuardedQuotient == 0UL;
 50977496        return overflowed ? 1UL << 63 : roundedGuardedQuotient >> 1;
 497    }
 498}

/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 Chronicler.Diagnostics;
 10using System.Globalization;
 11using System.Runtime.CompilerServices;
 12
 13namespace FixedMathSharp;
 14
 15/// <content>
 16/// Conversion operators between Fixed64 and other numeric types (int, long, float, double, decimal, etc.).
 17/// </content>
 18public partial struct Fixed64
 19{
 20    #region Explicit and Implicit Conversions
 21
 22    /// <summary>
 23    /// Converts a 64-bit signed integer to a Fixed64 value using explicit casting.
 24    /// </summary>
 25    /// <remarks>
 26    /// The conversion interprets the input value as the integer part of the fixed-point number and saturates
 27    /// to <see cref="MinValue"/> or <see cref="MaxValue"/> when the integer cannot fit in Q32.32 value space.
 28    /// Use <see cref="FromRaw(long)"/> when a raw fixed-point payload is required.
 29    /// </remarks>
 30    /// <param name="value">The 64-bit signed integer to convert to a Fixed64 value.</param>
 31    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 32    public static explicit operator Fixed64(long value)
 33    {
 2334        if (value > int.MaxValue)
 235            return MaxValue;
 36
 2137        if (value < int.MinValue)
 238            return MinValue;
 39
 1940        return FromRaw(value << FixedMath.SHIFT_AMOUNT_I);
 41    }
 42
 43    /// <summary>
 44    /// Converts a Fixed64 value to a 64-bit signed integer by discarding the fractional part.
 45    /// </summary>
 46    /// <remarks>
 47    /// The conversion truncates any fractional component.
 48    /// The result represents the integer portion of the Fixed64 value.</remarks>
 49    /// <param name="value">The Fixed64 value to convert to a long integer.</param>
 50    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 51    public static explicit operator long(Fixed64 value)
 52    {
 653        return value > Zero
 654            ? (long)(FixedMath.Floor(value).m_rawValue >> FixedMath.SHIFT_AMOUNT_I)
 655            : (long)(FixedMath.Ceil(value).m_rawValue >> FixedMath.SHIFT_AMOUNT_I);
 56    }
 57
 58    /// <summary>
 59    /// Defines an explicit conversion from a 32-bit signed integer to a Fixed64 value.
 60    /// </summary>
 61    /// <param name="value">The 32-bit signed integer to convert to a Fixed64 value.</param>
 62    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 63    public static explicit operator Fixed64(int value)
 64    {
 289056165        return new Fixed64(value);
 66    }
 67
 68    /// <summary>
 69    /// Converts a Fixed64 value to a 32-bit signed integer by discarding the fractional part.
 70    /// </summary>
 71    /// <remarks>
 72    /// The conversion truncates any fractional component.
 73    /// The result is equivalent to rounding toward zero.
 74    /// </remarks>
 75    /// <param name="value">The Fixed64 value to convert to an integer.</param>
 76    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 77    public static explicit operator int(Fixed64 value)
 78    {
 8279        return value > Zero
 8280            ? (int)(FixedMath.Floor(value).m_rawValue >> FixedMath.SHIFT_AMOUNT_I)
 8281            : (int)(FixedMath.Ceil(value).m_rawValue >> FixedMath.SHIFT_AMOUNT_I);
 82    }
 83
 84    /// <summary>
 85    /// Defines an explicit conversion from a single-precision floating-point value to a Fixed64 instance.
 86    /// </summary>
 87    /// <param name="value">The single-precision floating-point value to convert to Fixed64.</param>
 88    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 89    public static explicit operator Fixed64(float value)
 90    {
 591        return FromDouble(value);
 92    }
 93
 94    /// <summary>
 95    /// Converts a Fixed64 value to its equivalent single-precision floating-point representation.
 96    /// </summary>
 97    /// <remarks>
 98    /// This conversion may result in a loss of precision if the Fixed64 value cannot be exactly  represented as a float
 99    /// </remarks>
 100    /// <param name="value">The Fixed64 value to convert to a float.</param>
 101    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 102    public static explicit operator float(Fixed64 value)
 103    {
 1104        return value.m_rawValue * FixedMath.SCALE_FACTOR_F;
 105    }
 106
 107    /// <summary>
 108    /// Defines an explicit conversion from a double-precision floating-point number to a Fixed64 value.
 109    /// </summary>
 110    /// <remarks>
 111    /// This conversion may result in loss of precision if the double value cannot be exactly represented as a Fixed64.
 112    /// </remarks>
 113    /// <param name="value">The double-precision floating-point number to convert to a Fixed64 value.</param>
 114    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 115    public static explicit operator Fixed64(double value)
 116    {
 241117        return FromDouble(value);
 118    }
 119
 120    /// <summary>
 121    /// Converts a Fixed64 value to its equivalent double-precision floating-point representation.
 122    /// </summary>
 123    /// <remarks>
 124    /// This conversion may result in a loss of precision if the Fixed64 value cannot be exactly represented as a double
 125    /// </remarks>
 126    /// <param name="value">The Fixed64 value to convert to a double.</param>
 127    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 128    public static explicit operator double(Fixed64 value)
 129    {
 146187130        return value.m_rawValue * FixedMath.SCALE_FACTOR_D;
 131    }
 132
 133    /// <summary>
 134    /// Defines an explicit conversion from a decimal value to a Fixed64 instance.
 135    /// </summary>
 136    /// <param name="value">The decimal value to convert to a Fixed64 instance.</param>
 137    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 138    public static explicit operator Fixed64(decimal value)
 139    {
 281140        return FromDecimal(value);
 141    }
 142
 143    /// <summary>
 144    /// Converts a Fixed64 value to its decimal representation.
 145    /// </summary>
 146    /// <remarks>
 147    /// This operator provides an explicit conversion from Fixed64 to decimal, preserving the numeric value as closely a
 148    /// Use this conversion when precise decimal arithmetic is required.
 149    /// </remarks>
 150    /// <param name="value">The Fixed64 value to convert to decimal.</param>
 151    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 152    public static explicit operator decimal(Fixed64 value)
 153    {
 1154        return value.m_rawValue * FixedMath.SCALE_FACTOR_M;
 155    }
 156
 157    #endregion
 158    #region Conversion
 159
 160    /// <summary>
 161    /// Returns the string representation of this Fixed64 instance.
 162    /// </summary>
 163    /// <remarks>
 164    /// Up to 10 decimal places.
 165    /// </remarks>
 166    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 162167    public override string ToString() => ((double)this).ToString(CultureInfo.InvariantCulture);
 168
 169    /// <summary>
 170    /// Converts the numeric value of the current Fixed64 object to its equivalent string representation.
 171    /// </summary>
 172    /// <param name="format">A format specification that governs how the current Fixed64 object is converted.</param>
 173    /// <returns>The string representation of the value of the current Fixed64 object.</returns>
 174    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1175    public string ToString(string? format) => ToString(format, CultureInfo.InvariantCulture);
 176
 177    /// <summary>
 178    /// Converts the numeric value of the current Fixed64 object to its equivalent string representation.
 179    /// </summary>
 180    /// <param name="format">A format specification that governs how the current Fixed64 object is converted.</param>
 181    /// <param name="formatProvider">The provider to use for culture-specific formatting information.</param>
 182    /// <returns>The string representation of the value of the current Fixed64 object.</returns>
 183    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 184    public string ToString(string? format, IFormatProvider? formatProvider) =>
 7185        ((double)this).ToString(format, formatProvider ?? CultureInfo.InvariantCulture);
 186
 187    /// <summary>
 188    /// Formats the value into the provided destination buffer.
 189    /// </summary>
 190    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 191    public bool TryFormat(
 192        Span<char> destination,
 193        out int charsWritten,
 194        ReadOnlySpan<char> format,
 195        IFormatProvider? provider)
 196    {
 91072197        return FixedDiagnosticsFormatter.TryFormat(this, destination, out charsWritten, format, provider);
 198    }
 199
 200    /// <summary>
 201    /// Parses a decimal string to create a Fixed64 instance.
 202    /// </summary>
 203    /// <param name="s">The string representation of the Fixed64 value.</param>
 204    /// <returns>The parsed Fixed64 value.</returns>
 6205    public static Fixed64 Parse(string s) => Parse(s, CultureInfo.InvariantCulture);
 206
 207    /// <summary>
 208    /// Parses a decimal string to create a Fixed64 instance.
 209    /// </summary>
 210    /// <param name="s">The string representation of the Fixed64 value.</param>
 211    /// <param name="formatProvider">The provider to use for culture-specific parsing information.</param>
 212    /// <returns>The parsed Fixed64 value.</returns>
 213    public static Fixed64 Parse(string s, IFormatProvider? formatProvider)
 214    {
 9215        ChronicleGuard.ThrowIfNull(s, 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        ChronicleGuard.ThrowIfNull(s, nameof(s));
 231
 4232        if (!long.TryParse(s, NumberStyles.Integer, CultureInfo.InvariantCulture, out long rawValue))
 2233            throw new FormatException($"Invalid format: {s}");
 234
 2235        return FromRaw(rawValue);
 236    }
 237
 238    /// <summary>
 239    /// Tries to parse a decimal string to create a Fixed64 instance.
 240    /// </summary>
 241    /// <param name="s">The string representation of the Fixed64 value.</param>
 242    /// <param name="result">The parsed Fixed64 value.</param>
 243    /// <returns>True if parsing succeeded; otherwise, false.</returns>
 5244    public static bool TryParse(string? s, out Fixed64 result) => TryParse(s, CultureInfo.InvariantCulture, out result);
 245
 246    /// <summary>
 247    /// Tries to parse a decimal string to create a Fixed64 instance.
 248    /// </summary>
 249    /// <param name="s">The string representation of the Fixed64 value.</param>
 250    /// <param name="formatProvider">The provider to use for culture-specific parsing information.</param>
 251    /// <param name="result">The parsed Fixed64 value.</param>
 252    /// <returns>True if parsing succeeded; otherwise, false.</returns>
 253    public static bool TryParse(string? s, IFormatProvider? formatProvider, out Fixed64 result)
 254    {
 7255        result = Zero;
 7256        if (string.IsNullOrEmpty(s))
 1257            return false;
 258
 6259        if (!decimal.TryParse(s, NumberStyles.Float | NumberStyles.AllowThousands, formatProvider ?? CultureInfo.Invaria
 2260            return false;
 261
 262        try
 263        {
 4264            result = FromDecimal(value);
 3265            return true;
 266        }
 1267        catch (OverflowException)
 268        {
 1269            return false;
 270        }
 4271    }
 272
 273    /// <summary>
 274    /// Tries to parse a raw Q32.32 payload string to create a Fixed64 instance.
 275    /// </summary>
 276    /// <param name="s">The raw fixed-point payload.</param>
 277    /// <param name="result">The parsed Fixed64 value.</param>
 278    /// <returns>True if parsing succeeded; otherwise, false.</returns>
 279    public static bool TryParseRaw(string? s, out Fixed64 result)
 280    {
 4281        result = Zero;
 4282        if (string.IsNullOrEmpty(s))
 1283            return false;
 284
 3285        if (!long.TryParse(s, NumberStyles.Integer, CultureInfo.InvariantCulture, out long rawValue))
 1286            return false;
 287
 2288        result = FromRaw(rawValue);
 2289        return true;
 290    }
 291
 292    /// <summary>
 293    /// Creates a Fixed64 from a raw long value.
 294    /// </summary>
 295    /// <remarks>
 296    /// This method interprets <paramref name="rawValue"/> as an already-scaled Q32.32 payload.
 297    /// Use the integer constructors or explicit numeric conversions for user-facing integer values.
 298    /// </remarks>
 299    /// <param name="rawValue">The raw fixed-point payload.</param>
 300    /// <returns>A Fixed64 representing the raw value.</returns>
 301    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1663638302    public static Fixed64 FromRaw(long rawValue) => new(rawValue);
 303
 304
 305    /// <summary>
 306    /// Converts a Fixed64's RawValue (Int64) into an integer by discarding the fractional part.
 307    /// </summary>
 308    /// <param name="value">The Fixed64 value to convert.</param>
 309    /// <returns>The integer representation of the Fixed64 value.</returns>
 310    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1311    public static int ToInt(Fixed64 value) => (int)(value.m_rawValue >> FixedMath.SHIFT_AMOUNT_I);
 312
 313    /// <summary>
 314    /// Constructs a Fixed64 from a double-precision floating-point value.
 315    /// </summary>
 316    /// <remarks>
 317    /// The value is multiplied by the scaling factor (2^SHIFT_AMOUNT) and rounded to the nearest
 318    /// raw integer using midpoint-to-even rounding. Non-finite values throw an
 319    /// <see cref="ArgumentOutOfRangeException"/>. Finite values outside the Q32.32 range throw an
 320    /// <see cref="OverflowException"/>.
 321    /// </remarks>
 322    /// <param name="value">Double value to convert.</param>
 323    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 324    public static Fixed64 FromDouble(double value)
 325    {
 54001326        if (double.IsNaN(value) || double.IsInfinity(value))
 10327            throw new ArgumentOutOfRangeException(nameof(value), value, "Value must be finite.");
 328
 53991329        double rawValue = Math.Round(value * FixedMath.ONE_L, MidpointRounding.ToEven);
 53991330        if (rawValue < FixedMath.MIN_RAW_D || rawValue >= FixedMath.MAX_RAW_EXCLUSIVE_D)
 7331            throw new OverflowException($"{value} is outside the representable range of {nameof(Fixed64)}.");
 332
 53984333        return FromRaw((long)rawValue);
 334    }
 335
 336    /// <summary>
 337    /// Constructs a Fixed64 from a decimal value.
 338    /// </summary>
 339    /// <remarks>
 340    /// The value is multiplied by the scaling factor (2^SHIFT_AMOUNT) and rounded to the nearest
 341    /// raw integer using midpoint-to-even rounding. Values outside the Q32.32 range throw an
 342    /// <see cref="OverflowException"/>.
 343    /// </remarks>
 344    /// <param name="value">Decimal value to convert.</param>
 345    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 346    public static Fixed64 FromDecimal(decimal value)
 347    {
 296348        decimal rawValue = decimal.Round(value * FixedMath.ONE_L, 0, MidpointRounding.ToEven);
 349
 296350        if (rawValue < long.MinValue || rawValue > long.MaxValue)
 4351            throw new OverflowException($"{value} is outside the representable range of {nameof(Fixed64)}.");
 352
 292353        return FromRaw((long)rawValue);
 354    }
 355
 356    /// <summary>
 357    /// Creates a Fixed64 from a fractional number.
 358    /// </summary>
 359    /// <remarks>
 360    /// The quotient is converted through <see cref="FromDouble(double)"/>, so non-finite results
 361    /// throw <see cref="ArgumentOutOfRangeException"/> and finite results outside the Q32.32 range
 362    /// throw <see cref="OverflowException"/>.
 363    /// </remarks>
 364    /// <param name="numerator">The numerator of the fraction.</param>
 365    /// <param name="denominator">The denominator of the fraction.</param>
 366    /// <returns>A Fixed64 representing the fraction.</returns>
 367    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 14272368    public static Fixed64 FromFraction(double numerator, double denominator) => FromDouble(numerator / denominator);
 369
 370    /// <summary>
 371    /// Converts a Fixed64s RawValue (Int64) into a double
 372    /// </summary>
 373    /// <param name="f1"></param>
 374    /// <returns></returns>
 19163375    public static double ToDouble(long f1) => f1 * FixedMath.SCALE_FACTOR_D;
 376
 377    /// <summary>
 378    /// Converts a Fixed64s RawValue (Int64) into a float
 379    /// </summary>
 380    /// <param name="f1"></param>
 381    /// <returns></returns>
 1382    public static float ToFloat(long f1) => f1 * FixedMath.SCALE_FACTOR_F;
 383
 384    /// <summary>
 385    /// Converts a Fixed64s RawValue (Int64) into a decimal
 386    /// </summary>
 387    /// <param name="f1"></param>
 388    /// <returns></returns>
 1389    public static decimal ToDecimal(long f1) => f1 * FixedMath.SCALE_FACTOR_M;
 390
 391    #endregion
 392}

/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 NET10_0_OR_GREATER
 27    , ISpanFormattable
 28#endif
 29{
 30    #region Static Fields
 31
 32    /// <inheritdoc cref="FixedMath.MAX_VALUE_L" />
 6923433    public static Fixed64 MaxValue => new(FixedMath.MAX_VALUE_L);
 34    /// <inheritdoc cref="FixedMath.MIN_VALUE_L" />
 749135    public static Fixed64 MinValue => new(FixedMath.MIN_VALUE_L);
 36
 37    /// <inheritdoc cref="FixedMath.ONE_L" />
 326844938    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>
 35681146    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>
 4450    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>
 5162054    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>
 283258    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>
 332260566    public static Fixed64 Zero => new(0);
 67
 68    /// <inheritdoc cref="FixedMath.PI_LONG" />
 107110369    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>
 18131774    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>
 57326379    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>
 5527887    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>
 19491    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>
 301799    public static Fixed64 OneEighty => new(180);
 100    /// <summary>
 101    /// Degrees to radians conversion factor (Ï€ / 180)
 102    /// </summary>
 21103    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
 28553131    internal static Fixed64 SinCoeff3 => new(FixedMath.SIN_COEFF_3_LONG); // Tuned minimax coefficient.
 28553132    internal static Fixed64 SinCoeff5 => new(FixedMath.SIN_COEFF_5_LONG); // Tuned minimax coefficient.
 28553133    internal static Fixed64 SinCoeff7 => new(FixedMath.SIN_COEFF_7_LONG); // Tuned minimax coefficient.
 26230134    internal static Fixed64 CosCoeff2 => new(FixedMath.COS_COEFF_2_LONG); // 1/2!
 26230135    internal static Fixed64 CosCoeff4 => new(FixedMath.COS_COEFF_4_LONG); // 1/4!
 26230136    internal static Fixed64 CosCoeff6 => new(FixedMath.COS_COEFF_6_LONG); // 1/6!
 26230137    internal static Fixed64 CosCoeff8 => new(FixedMath.COS_COEFF_8_LONG); // 1/8!
 138
 139    /// <inheritdoc cref="FixedMath.MIN_INCREMENT_L" />
 1625140    public static Fixed64 MinIncrement => new(FixedMath.MIN_INCREMENT_L);
 141    /// <inheritdoc cref="FixedMath.DEFAULT_TOLERANCE_L" />
 363794142    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]
 23944719162    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>
 13695796168    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)]
 30121    public override bool Equals(object? obj) => obj is Fixed64 other && Equals(other);
 22
 23    /// <inheritdoc/>
 24    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 291918225    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)]
 21133    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>
 24744    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.FusedArithmetic.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    {
 4408340        result = MultiplyAdd(left, right, addend, out bool representable);
 4408341        if (representable)
 4371842            return true;
 43
 36544        result = default;
 36545        return false;
 46    }
 47
 48    private static Fixed64 MultiplyAdd(
 49        Fixed64 left,
 50        Fixed64 right,
 51        Fixed64 addend,
 52        out bool representable)
 53    {
 4409954        long leftRaw = left.m_rawValue;
 4409955        long rightRaw = right.m_rawValue;
 4409956        Multiply64To128(
 4409957            AbsToUInt64(leftRaw),
 4409958            AbsToUInt64(rightRaw),
 4409959            out ulong productHigh,
 4409960            out ulong productLow);
 4409961        Signed192 product = new(0UL, productHigh, productLow);
 4409962        if ((leftRaw ^ rightRaw) < 0L)
 605763            product = WideArithmetic.SubtractSigned192(default, product);
 64
 4409965        long addendRaw = addend.m_rawValue;
 4409966        ulong extension = addendRaw < 0L ? ulong.MaxValue : 0UL;
 4409967        Signed192 scaledAddend = new(
 4409968            extension,
 4409969            unchecked((ulong)(addendRaw >> FixedMath.SHIFT_AMOUNT_I)),
 4409970            unchecked((ulong)addendRaw << FixedMath.SHIFT_AMOUNT_I));
 4409971        Signed192 numerator = WideArithmetic.AddSigned192(product, scaledAddend);
 4409972        bool negative = numerator.Sign < 0;
 4409973        WideArithmetic.GetMagnitude(
 4409974            numerator,
 4409975            out _,
 4409976            out ulong numeratorMiddle,
 4409977            out ulong numeratorLow);
 78
 4409979        if ((numeratorMiddle >> FixedMath.SHIFT_AMOUNT_I) != 0UL)
 80        {
 17981            representable = false;
 17982            return negative ? MinValue : MaxValue;
 83        }
 84
 4392085        ulong quotient = (numeratorMiddle << FixedMath.SHIFT_AMOUNT_I)
 4392086            | (numeratorLow >> FixedMath.SHIFT_AMOUNT_I);
 4392087        ulong guardMask = 1UL << (FixedMath.SHIFT_AMOUNT_I - 1);
 4392088        bool guard = (numeratorLow & guardMask) != 0UL;
 4392089        bool sticky = (numeratorLow & (guardMask - 1UL)) != 0UL;
 4392090        return RoundAndApplySign(
 4392091            quotient,
 4392092            guard,
 4392093            sticky,
 4392094            negative,
 4392095            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 TryRoundNonnegativeQ128(difference, out result);
 203    }
 204
 205    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 206    internal static Signed192 GetExactRawProduct(Fixed64 left, Fixed64 right)
 207    {
 1411413208        long leftRaw = left.m_rawValue;
 1411413209        long rightRaw = right.m_rawValue;
 1411413210        Multiply64To128(
 1411413211            AbsToUInt64(leftRaw),
 1411413212            AbsToUInt64(rightRaw),
 1411413213            out ulong productHigh,
 1411413214            out ulong productLow);
 1411413215        Signed192 product = new(0UL, productHigh, productLow);
 1411413216        return (leftRaw ^ rightRaw) < 0L
 1411413217            ? WideArithmetic.SubtractSigned192(default, product)
 1411413218            : 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    /// <summary>
 276    /// Rounds an exact Q128 value to Q32.32 once, with round-half-to-even.
 277    /// Positive inputs must have magnitude below 2^256; nonpositive inputs
 278    /// produce zero. Returns false with a default result on final overflow.
 279    /// </summary>
 280    internal static bool TryRoundNonnegativeQ128(
 281        Signed320 difference,
 282        out Fixed64 result)
 283    {
 423284        if (difference.Sign <= 0)
 285        {
 171286            result = Zero;
 171287            return true;
 288        }
 289
 252290        WideArithmetic.GetMagnitude(
 252291            difference,
 252292            out _,
 252293            out ulong word3,
 252294            out ulong word2,
 252295            out ulong word1,
 252296            out ulong word0);
 252297        if (word3 != 0UL || (word2 >> FixedMath.SHIFT_AMOUNT_I) != 0UL)
 298        {
 37299            result = default;
 37300            return false;
 301        }
 302
 215303        return TryRoundNonnegative(
 215304            (word2 << FixedMath.SHIFT_AMOUNT_I) | (word1 >> FixedMath.SHIFT_AMOUNT_I),
 215305            (word1 & (1UL << (FixedMath.SHIFT_AMOUNT_I - 1))) != 0UL,
 215306            word0 != 0UL || (word1 & ((1UL << (FixedMath.SHIFT_AMOUNT_I - 1)) - 1UL)) != 0UL,
 215307            out result);
 308    }
 309
 310    private static bool TryRoundNonnegative(
 311        ulong quotient,
 312        bool guard,
 313        bool sticky,
 314        out Fixed64 result)
 315    {
 416316        result = RoundAndApplySign(quotient, guard, sticky, negative: false, out bool representable);
 416317        if (representable)
 196318            return true;
 319
 220320        result = default;
 220321        return false;
 322    }
 323
 324    /// <summary>
 325    /// Attempts to evaluate
 326    /// <c>(value - subtrahend) * firstMultiplier * secondMultiplier</c> with
 327    /// one final round-half-to-even conversion.
 328    /// </summary>
 329    /// <remarks>
 330    /// The subtraction and both products remain exact even when an intermediate
 331    /// value lies outside the public Q32.32 domain.
 332    /// </remarks>
 333    public static bool TryMultiplyDifference(
 334        Fixed64 value,
 335        Fixed64 subtrahend,
 336        Fixed64 firstMultiplier,
 337        Fixed64 secondMultiplier,
 338        out Fixed64 result)
 339    {
 12340        long valueRaw = value.m_rawValue;
 12341        long subtrahendRaw = subtrahend.m_rawValue;
 12342        bool negativeDifference = valueRaw < subtrahendRaw;
 12343        ulong differenceMagnitude = negativeDifference
 12344            ? unchecked((ulong)subtrahendRaw - (ulong)valueRaw)
 12345            : unchecked((ulong)valueRaw - (ulong)subtrahendRaw);
 12346        long firstRaw = firstMultiplier.m_rawValue;
 12347        long secondRaw = secondMultiplier.m_rawValue;
 12348        bool negative = negativeDifference
 12349            ^ (firstRaw < 0L)
 12350            ^ (secondRaw < 0L);
 351
 12352        Multiply64To128(
 12353            differenceMagnitude,
 12354            AbsToUInt64(firstRaw),
 12355            out ulong productHigh,
 12356            out ulong productLow);
 12357        Multiply64To128(
 12358            productLow,
 12359            AbsToUInt64(secondRaw),
 12360            out ulong lowProductHigh,
 12361            out ulong numeratorLow);
 12362        Multiply64To128(
 12363            productHigh,
 12364            AbsToUInt64(secondRaw),
 12365            out ulong numeratorHigh,
 12366            out ulong highProductLow);
 12367        ulong numeratorMiddle = unchecked(lowProductHigh + highProductLow);
 12368        if (numeratorMiddle < lowProductHigh)
 1369            numeratorHigh++;
 370
 12371        if (numeratorHigh != 0UL)
 372        {
 1373            result = default;
 1374            return false;
 375        }
 376
 377        const ulong GuardMask = 1UL << 63;
 11378        bool guard = (numeratorLow & GuardMask) != 0UL;
 11379        bool sticky = (numeratorLow & (GuardMask - 1UL)) != 0UL;
 11380        result = RoundAndApplySign(
 11381            numeratorMiddle,
 11382            guard,
 11383            sticky,
 11384            negative,
 11385            out bool representable);
 11386        if (representable)
 9387            return true;
 388
 2389        result = default;
 2390        return false;
 391    }
 392
 393    /// <summary>
 394    /// Attempts to multiply two values and divide by a third with one final
 395    /// round-half-to-even operation and no intermediate saturation.
 396    /// </summary>
 397    /// <param name="left">The first factor.</param>
 398    /// <param name="right">The second factor.</param>
 399    /// <param name="divisor">The divisor.</param>
 400    /// <param name="result">
 401    /// The fused result when representable; otherwise, <see langword="default"/>.
 402    /// </param>
 403    /// <returns>
 404    /// <see langword="true"/> when the divisor is nonzero and the final result is
 405    /// representable; otherwise, <see langword="false"/>.
 406    /// </returns>
 407    public static bool TryMultiplyDivide(
 408        Fixed64 left,
 409        Fixed64 right,
 410        Fixed64 divisor,
 411        out Fixed64 result)
 412    {
 2108413        result = MultiplyDivide(left, right, divisor, out bool representable);
 2108414        if (representable)
 1576415            return true;
 416
 532417        result = default;
 532418        return false;
 419    }
 420
 421    /// <summary>
 422    /// Computes a fused two-factor multiply-divide and returns a saturated value
 423    /// when the exact result is not representable.
 424    /// </summary>
 425    internal static Fixed64 MultiplyDivide(
 426        Fixed64 left,
 427        Fixed64 right,
 428        Fixed64 divisor,
 429        out bool representable)
 430    {
 6168431        long divisorRaw = divisor.m_rawValue;
 6168432        if (divisorRaw == 0L)
 433        {
 2434            representable = false;
 2435            return default;
 436        }
 437
 6166438        long leftRaw = left.m_rawValue;
 6166439        long rightRaw = right.m_rawValue;
 6166440        bool negative = (leftRaw ^ rightRaw ^ divisorRaw) < 0L;
 6166441        ulong divisorMagnitude = AbsToUInt64(divisorRaw);
 442
 6166443        Multiply64To128(
 6166444            AbsToUInt64(leftRaw),
 6166445            AbsToUInt64(rightRaw),
 6166446            out ulong productHigh,
 6166447            out ulong productLow);
 6166448        CancelCommonPowersOfTwo(
 6166449            ref productHigh,
 6166450            ref productLow,
 6166451            ref divisorMagnitude);
 452
 6166453        if (productHigh >= divisorMagnitude)
 454        {
 276455            representable = false;
 276456            return negative ? MinValue : MaxValue;
 457        }
 458
 5890459        ulong quotient = Divide128By64(
 5890460            productHigh,
 5890461            productLow,
 5890462            divisorMagnitude,
 5890463            out ulong remainder);
 5890464        ulong twiceRemainder = remainder << 1;
 5890465        bool guard = twiceRemainder >= divisorMagnitude;
 5890466        bool sticky = guard
 5890467            ? twiceRemainder != divisorMagnitude
 5890468            : twiceRemainder != 0UL;
 469
 5890470        return RoundAndApplySign(quotient, guard, sticky, negative, out representable);
 471    }
 472
 473    /// <summary>
 474    /// Attempts to multiply three values and divide by a fourth with one final
 475    /// round-half-to-even operation and no intermediate saturation.
 476    /// </summary>
 477    /// <param name="first">The first factor.</param>
 478    /// <param name="second">The second factor.</param>
 479    /// <param name="third">The third factor.</param>
 480    /// <param name="divisor">The divisor.</param>
 481    /// <param name="result">
 482    /// The fused result when representable; otherwise, <see langword="default"/>.
 483    /// </param>
 484    /// <returns>
 485    /// <see langword="true"/> when the divisor is nonzero and the final result is
 486    /// representable; otherwise, <see langword="false"/>.
 487    /// </returns>
 488    public static bool TryMultiplyDivide(
 489        Fixed64 first,
 490        Fixed64 second,
 491        Fixed64 third,
 492        Fixed64 divisor,
 493        out Fixed64 result)
 494    {
 2855495        result = MultiplyDivide(first, second, third, divisor, out bool representable);
 2855496        if (representable)
 801497            return true;
 498
 2054499        result = default;
 2054500        return false;
 501    }
 502
 503    /// <summary>
 504    /// Attempts to multiply four values and divide by the exact sum of four
 505    /// values with one final round-half-to-even operation.
 506    /// </summary>
 507    public static bool TryMultiplyDivideBySum(
 508        Fixed64 first,
 509        Fixed64 second,
 510        Fixed64 third,
 511        Fixed64 fourth,
 512        Fixed64 firstDivisorTerm,
 513        Fixed64 secondDivisorTerm,
 514        Fixed64 thirdDivisorTerm,
 515        Fixed64 fourthDivisorTerm,
 516        out Fixed64 result)
 517    {
 17518        Signed192 divisor = WideArithmetic.AddSigned192(
 17519            WideArithmetic.AddSigned192(
 17520                Signed192.Raw(firstDivisorTerm),
 17521                Signed192.Raw(secondDivisorTerm)),
 17522            WideArithmetic.AddSigned192(
 17523                Signed192.Raw(thirdDivisorTerm),
 17524                Signed192.Raw(fourthDivisorTerm)));
 17525        Signed576 numerator = WideArithmetic.MultiplySigned576(
 17526            Signed576.ExtendValue(
 17527                Signed320.ExtendValue(Signed192.Raw(first))),
 17528            Signed192.Raw(second),
 17529            Signed192.Raw(third),
 17530            Signed192.Raw(fourth));
 17531        Signed576 scaledDivisor = WideArithmetic.MultiplySigned576(
 17532            Signed576.ExtendValue(Signed320.ExtendValue(divisor)),
 17533            Signed192.One,
 17534            Signed192.One);
 535        // Scaling the divisor by One twice zeros its low limb. Keep typed
 536        // division unless a nonzero numerator shares that cancellable factor.
 17537        if (numerator.Word0 != 0UL || numerator.IsZero || divisor.IsZero)
 538        {
 5539            return TryGetSignedRawRatio(numerator, scaledDivisor, out result);
 540        }
 541
 12542        return TryGetBinaryReducedSignedRawRatio(numerator, scaledDivisor, out result);
 543    }
 544
 545    /// <summary>
 546    /// Computes a fused three-factor multiply-divide and returns a saturated value
 547    /// when the exact result is not representable.
 548    /// </summary>
 549    internal static Fixed64 MultiplyDivide(
 550        Fixed64 first,
 551        Fixed64 second,
 552        Fixed64 third,
 553        Fixed64 divisor,
 554        out bool representable)
 555    {
 2856556        long divisorRaw = divisor.m_rawValue;
 2856557        if (divisorRaw == 0L)
 558        {
 2559            representable = false;
 2560            return default;
 561        }
 562
 2854563        long firstRaw = first.m_rawValue;
 2854564        long secondRaw = second.m_rawValue;
 2854565        long thirdRaw = third.m_rawValue;
 2854566        bool negative = (firstRaw ^ secondRaw ^ thirdRaw ^ divisorRaw) < 0L;
 2854567        ulong thirdMagnitude = AbsToUInt64(thirdRaw);
 2854568        ulong divisorMagnitude = AbsToUInt64(divisorRaw);
 569
 2854570        Multiply64To128(
 2854571            AbsToUInt64(firstRaw),
 2854572            AbsToUInt64(secondRaw),
 2854573            out ulong productHigh,
 2854574            out ulong productLow);
 2854575        Multiply64To128(
 2854576            productLow,
 2854577            thirdMagnitude,
 2854578            out ulong lowProductHigh,
 2854579            out ulong numeratorLow);
 2854580        Multiply64To128(
 2854581            productHigh,
 2854582            thirdMagnitude,
 2854583            out ulong numeratorHigh,
 2854584            out ulong highProductLow);
 585
 2854586        ulong numeratorMiddle = unchecked(lowProductHigh + highProductLow);
 2854587        if (numeratorMiddle < lowProductHigh)
 262588            numeratorHigh++;
 2854589        CancelCommonPowersOfTwo(
 2854590            ref numeratorHigh,
 2854591            ref numeratorMiddle,
 2854592            ref numeratorLow,
 2854593            ref divisorMagnitude);
 594
 2854595        ulong quotientHigh = numeratorHigh / divisorMagnitude;
 2854596        ulong remainder = numeratorHigh % divisorMagnitude;
 2854597        ulong quotientMiddle = Divide128By64(
 2854598            remainder,
 2854599            numeratorMiddle,
 2854600            divisorMagnitude,
 2854601            out remainder);
 2854602        ulong quotientLow = Divide128By64(
 2854603            remainder,
 2854604            numeratorLow,
 2854605            divisorMagnitude,
 2854606            out remainder);
 607
 2854608        if (quotientHigh != 0UL || (quotientMiddle >> FixedMath.SHIFT_AMOUNT_I) != 0UL)
 609        {
 2051610            representable = false;
 2051611            return negative ? MinValue : MaxValue;
 612        }
 613
 803614        ulong quotient = (quotientMiddle << FixedMath.SHIFT_AMOUNT_I)
 803615            | (quotientLow >> FixedMath.SHIFT_AMOUNT_I);
 803616        ulong discardedMask = (1UL << (FixedMath.SHIFT_AMOUNT_I - 1)) - 1UL;
 803617        bool guard = (quotientLow & (1UL << (FixedMath.SHIFT_AMOUNT_I - 1))) != 0UL;
 803618        bool sticky = ((quotientLow & discardedMask) | remainder) != 0UL;
 619
 803620        return RoundAndApplySign(quotient, guard, sticky, negative, out representable);
 621    }
 622
 623    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 624    private static int CountTrailingZeroes(ulong value) =>
 10932625        63 - CountLeadingZeroes(value & unchecked(0UL - value));
 626
 627    private static void CancelCommonPowersOfTwo(
 628        ref ulong high,
 629        ref ulong low,
 630        ref ulong divisor)
 631    {
 6166632        if ((divisor & 1UL) != 0UL || (high | low) == 0UL)
 2134633            return;
 634
 4032635        int numeratorZeroes = low != 0UL
 4032636            ? CountTrailingZeroes(low)
 4032637            : 64 + CountTrailingZeroes(high);
 4032638        int shift = Math.Min(numeratorZeroes, CountTrailingZeroes(divisor));
 4032639        if (shift == 0)
 314640            return;
 641
 3718642        low = (low >> shift) | (high << (64 - shift));
 3718643        high >>= shift;
 3718644        divisor >>= shift;
 3718645    }
 646
 647    private static void CancelCommonPowersOfTwo(
 648        ref ulong high,
 649        ref ulong middle,
 650        ref ulong low,
 651        ref ulong divisor)
 652    {
 2854653        if ((divisor & 1UL) != 0UL || (high | middle | low) == 0UL)
 1420654            return;
 655
 1434656        int numeratorZeroes = low != 0UL
 1434657            ? CountTrailingZeroes(low)
 1434658            : middle != 0UL
 1434659                ? 64 + CountTrailingZeroes(middle)
 1434660                : 128 + CountTrailingZeroes(high);
 1434661        int shift = Math.Min(numeratorZeroes, CountTrailingZeroes(divisor));
 1434662        if (shift == 0)
 194663            return;
 664
 1240665        low = (low >> shift) | (middle << (64 - shift));
 1240666        middle = (middle >> shift) | (high << (64 - shift));
 1240667        high >>= shift;
 1240668        divisor >>= shift;
 1240669    }
 670
 671    /// <summary>
 672    /// Divides an unsigned 128-bit numerator by a 64-bit divisor when the quotient
 673    /// is known to fit in 64 bits.
 674    /// </summary>
 675    internal static ulong Divide128By64(
 676        ulong high,
 677        ulong low,
 678        ulong divisor,
 679        out ulong remainder)
 680    {
 5646929681        if (high == 0UL)
 682        {
 1186163683            remainder = low % divisor;
 1186163684            return low / divisor;
 685        }
 686
 4460766687        if (divisor <= uint.MaxValue)
 688        {
 312689            ulong upper = (high << 32) | (low >> 32);
 312690            ulong quotientHigh = upper / divisor;
 312691            remainder = upper % divisor;
 312692            ulong lower = (remainder << 32) | (uint)low;
 312693            ulong quotientLow = lower / divisor;
 312694            remainder = lower % divisor;
 312695            return (quotientHigh << 32) | quotientLow;
 696        }
 697
 698        // Two base-2^32 quotient digits (normalized Algorithm D). The
 699        // existing quotient-fit precondition is high < divisor, so shifting
 700        // cannot discard a high numerator bit and each partial remainder
 701        // stays below the normalized divisor.
 702        const ulong radix = 1UL << 32;
 4460454703        int shift = CountLeadingZeroes(divisor);
 4460454704        divisor <<= shift;
 4460454705        remainder = shift == 0 ? high : (high << shift) | (low >> (64 - shift));
 4460454706        ulong normalizedLow = low << shift;
 4460454707        ulong divisorHigh = divisor >> 32;
 4460454708        ulong divisorLow = (uint)divisor;
 4460454709        ulong quotient = 0UL;
 26762724710        for (int digit = 1; digit >= 0; digit--)
 711        {
 8920908712            ulong next = (uint)(normalizedLow >> (digit * 32));
 8920908713            ulong trial = remainder / divisorHigh;
 8920908714            ulong trialRemainder = remainder - trial * divisorHigh;
 715            // Normalization bounds the overestimate by two. Once the trial
 716            // remainder reaches radix, the comparison is necessarily false;
 717            // stop before forming its overflowing radix product.
 9303665718            while (trial >= radix || trial * divisorLow > (trialRemainder << 32) + next)
 719            {
 602278720                trial--;
 602278721                trialRemainder += divisorHigh;
 602278722                if (trialRemainder >= radix)
 723                    break;
 724            }
 725            // The exact difference is in [0, divisor), so retaining this
 726            // 96-bit subtraction modulo 2^64 loses no remainder information.
 8920908727            remainder = unchecked((remainder << 32) + next - trial * divisor);
 8920908728            quotient = (quotient << 32) | trial;
 729        }
 730
 4460454731        remainder >>= shift;
 4460454732        return quotient;
 733    }
 734
 735    /// <summary>
 736    /// Rounds an unsigned quotient and guard/sticky bits to even, applies its
 737    /// sign, and reports representability. An unrepresentable result saturates.
 738    /// </summary>
 739    internal static Fixed64 RoundAndApplySign(
 740        ulong quotient,
 741        bool guard,
 742        bool sticky,
 743        bool negative,
 744        out bool representable)
 745    {
 746        const ulong minValueMagnitude = 1UL << 63;
 54187747        if (quotient >= minValueMagnitude)
 748        {
 740749            if (quotient == minValueMagnitude && guard && sticky)
 2750                quotient++;
 751
 740752            return ApplySignedMagnitude(quotient, negative, out representable);
 753        }
 754
 53447755        ulong guardedQuotient = (quotient << 1) | (guard ? 1UL : 0UL);
 53447756        ulong magnitude = RoundGuardedQuotientToEven(
 53447757            guardedQuotient,
 53447758            sticky,
 53447759            out _);
 53447760        return ApplySignedMagnitude(magnitude, negative, out representable);
 761    }
 762
 763    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 764    private static Fixed64 ApplySignedMagnitude(
 765        ulong magnitude,
 766        bool negative,
 767        out bool representable)
 768    {
 769        const ulong minValueMagnitude = 1UL << 63;
 54187770        ulong limit = negative ? minValueMagnitude : long.MaxValue;
 54187771        representable = magnitude <= limit;
 54187772        if (!representable)
 696773            return negative ? MinValue : MaxValue;
 53491774        if (magnitude == minValueMagnitude)
 57775            return MinValue;
 776
 53434777        long rawResult = (long)magnitude;
 53434778        return new Fixed64(negative ? -rawResult : rawResult);
 779    }
 780}

/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    {
 223029926        long xl = x.m_rawValue;
 223029927        long yl = y.m_rawValue;
 223029928        long sum = unchecked(xl + yl);
 223029929        if (!IsAddOrSubtractResultExact(xl, sum, ~(xl ^ yl)))
 13630            sum = xl < 0 ? FixedMath.MIN_VALUE_L : FixedMath.MAX_VALUE_L;
 223029931        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)]
 5038    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    {
 110638250        long xl = x.m_rawValue;
 110638251        long yl = y.m_rawValue;
 110638252        long diff = unchecked(xl - yl);
 110638253        if (!IsAddOrSubtractResultExact(xl, diff, xl ^ yl))
 3554            diff = xl < 0 ? FixedMath.MIN_VALUE_L : FixedMath.MAX_VALUE_L;
 110638255        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) =>
 2063        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    {
 484744481        long xl = x.m_rawValue;
 484744482        long yl = y.m_rawValue;
 83
 484744484        int shift = FixedMath.SHIFT_AMOUNT_I;
 85
 86        // Determine sign of the final result.
 484744487        bool negative = ((xl ^ yl) < 0);
 88
 89        // Convert to unsigned magnitudes safely, including long.MinValue.
 484744490        ulong ax = AbsToUInt64(xl);
 484744491        ulong ay = AbsToUInt64(yl);
 92
 93        // Compute exact 128-bit unsigned product: (hi << 64) | lo
 484744494        Multiply64To128(ax, ay, out ulong hi, out ulong lo);
 95
 96        // Shift-right with round-half-to-even using the FULL discarded remainder.
 484744497        ulong magnitude = ShiftRightRoundedToEven(hi, lo, shift, out bool roundedOverflow);
 98
 99        // If rounding overflowed the shifted magnitude, carry it into saturation handling.
 4847444100        if (!negative)
 101        {
 4502600102            if (roundedOverflow || magnitude > long.MaxValue)
 238103                return new Fixed64(FixedMath.MAX_VALUE_L);
 104
 4502362105            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
 344844112            if (roundedOverflow || magnitude > minValueMagnitude)
 18113                return new Fixed64(FixedMath.MIN_VALUE_L);
 114
 344826115            if (magnitude == minValueMagnitude)
 17116                return new Fixed64(FixedMath.MIN_VALUE_L);
 117
 344809118            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) =>
 445944127        x * new Fixed64((long)y << FixedMath.SHIFT_AMOUNT_I);
 128
 129    /// <inheritdoc cref="operator *(Fixed64, int)" />
 130    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 82465131    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    {
 1016297138        long xl = x.m_rawValue;
 1016297139        long yl = y.m_rawValue;
 140
 1016297141        if (yl == 0)
 3142            throw new DivideByZeroException($"Attempted to divide {x} by zero.");
 143
 1016294144        return DivideMagnitude(
 1016294145            AbsToUInt64(xl),
 1016294146            AbsToUInt64(yl),
 1016294147            (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) =>
 58082155         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    {
 54783170        if (x.m_rawValue == FixedMath.MIN_VALUE_L && y.m_rawValue == -1)
 1171            return Zero;
 54782172        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) =>
 410054192        x.m_rawValue == FixedMath.MIN_VALUE_L
 410054193            ? new Fixed64(FixedMath.MAX_VALUE_L)
 410054194            : 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)]
 509486237    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)]
 305974255    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)]
 23456273    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)]
 528569291    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)]
 2575167309    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)]
 313492327    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    {
 26#if NET10_0_OR_GREATER
 3654539527        return System.Numerics.BitOperations.LeadingZeroCount(x);
 28#else
 29        if (x == 0UL)
 30            return 64;
 31
 32        int result = 0;
 33        while ((x & 0xF000000000000000) == 0) { result += 4; x <<= 4; }
 34        while ((x & 0x8000000000000000) == 0) { result += 1; x <<= 1; }
 35        return result;
 36#endif
 37    }
 38
 39    /// <summary>
 40    /// Returns a number indicating the sign of a Fix64 number.
 41    /// Returns 1 if the value is positive, 0 if is 0, and -1 if it is negative.
 42    /// </summary>
 43    /// <remarks>
 44    /// Optimized for branchless comparison.
 45    /// </remarks>
 46    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 47    public static int Sign(Fixed64 value) =>
 2522448        value.m_rawValue < 0
 2522449                ? -1
 2522450                : (value.m_rawValue > 0 ? 1 : 0);
 51
 52    /// <summary>
 53    /// Returns true if the number has no decimal part (i.e., if the number is equivalent to an integer) and False other
 54    /// </summary>
 55    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 56    public static bool IsInteger(Fixed64 value)
 57    {
 758        return ((ulong)value.m_rawValue & FixedMath.MAX_SHIFTED_AMOUNT_UI) == 0;
 59    }
 60
 61    /// <summary>
 62    /// Compares the exact mathematical product <paramref name="leftFirst"/> *
 63    /// <paramref name="leftSecond"/> with <paramref name="rightFirst"/> *
 64    /// <paramref name="rightSecond"/> without rounding or saturation.
 65    /// </summary>
 66    /// <returns>
 67    /// A negative value when the left product is smaller, zero when the exact
 68    /// products are equal, or a positive value when the left product is larger.
 69    /// </returns>
 70    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 71    public static int CompareProducts(
 72        Fixed64 leftFirst,
 73        Fixed64 leftSecond,
 74        Fixed64 rightFirst,
 75        Fixed64 rightSecond) =>
 13976        WideArithmetic.SubtractSigned192(
 13977            GetExactRawProduct(leftFirst, leftSecond),
 13978            GetExactRawProduct(rightFirst, rightSecond)).Sign;
 79
 80    /// <summary>
 81    /// Compares the exact four-factor mathematical product
 82    /// <paramref name="leftFirst"/> * <paramref name="leftSecond"/> *
 83    /// <paramref name="leftThird"/> * <paramref name="leftFourth"/> with
 84    /// <paramref name="rightFirst"/> * <paramref name="rightSecond"/> *
 85    /// <paramref name="rightThird"/> * <paramref name="rightFourth"/> without
 86    /// rounding or saturation.
 87    /// </summary>
 88    /// <returns>
 89    /// A negative value when the left product is smaller, zero when the exact
 90    /// products are equal, or a positive value when the left product is larger.
 91    /// </returns>
 92    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 93    public static int CompareProducts(
 94        Fixed64 leftFirst,
 95        Fixed64 leftSecond,
 96        Fixed64 leftThird,
 97        Fixed64 leftFourth,
 98        Fixed64 rightFirst,
 99        Fixed64 rightSecond,
 100        Fixed64 rightThird,
 101        Fixed64 rightFourth) =>
 131102        WideArithmetic.SubtractSigned320(
 131103            WideArithmetic.MultiplySigned192(
 131104                GetExactRawProduct(leftFirst, leftSecond),
 131105                GetExactRawProduct(leftThird, leftFourth)),
 131106            WideArithmetic.MultiplySigned192(
 131107                GetExactRawProduct(rightFirst, rightSecond),
 131108                GetExactRawProduct(rightThird, rightFourth))).Sign;
 109}

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

#LineLine coverage
 1//=======================================================================
 2// Fixed64.WideArithmetic.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    {
 384025        long amountRaw = amount.m_rawValue;
 384026        if (amountRaw <= 0L)
 22427            return from;
 361628        if (amountRaw >= FixedMath.ONE_L)
 13929            return to;
 30
 347731        long fromRaw = from.m_rawValue;
 347732        long toRaw = to.m_rawValue;
 347733        bool negativeDifference = toRaw < fromRaw;
 347734        ulong differenceMagnitude = negativeDifference
 347735            ? unchecked((ulong)fromRaw - (ulong)toRaw)
 347736            : unchecked((ulong)toRaw - (ulong)fromRaw);
 37
 347738        Multiply64To128(
 347739            differenceMagnitude,
 347740            (ulong)amountRaw,
 347741            out ulong productHigh,
 347742            out ulong productLow);
 43
 347744        ulong interpolatedMagnitude = (productHigh << 32) | (productLow >> 32);
 347745        ulong rawResult = negativeDifference
 347746            ? unchecked((ulong)fromRaw - interpolatedMagnitude)
 347747            : unchecked((ulong)fromRaw + interpolatedMagnitude);
 347748        ulong remainder = productLow & uint.MaxValue;
 49        const ulong HalfRawUnit = 1UL << 31;
 347750        if (remainder > HalfRawUnit || (remainder == HalfRawUnit && (rawResult & 1UL) != 0UL))
 52851            rawResult = negativeDifference ? rawResult - 1UL : rawResult + 1UL;
 52
 347753        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    {
 976139        long baseRaw = coordA.m_rawValue;
 976140        ulong extension = baseRaw < 0L ? ulong.MaxValue : 0UL;
 976141        ulong high = extension;
 976142        ulong middle = (extension << FixedMath.SHIFT_AMOUNT_I)
 976143            | (unchecked((ulong)baseRaw) >> FixedMath.SHIFT_AMOUNT_I);
 976144        ulong low = unchecked((ulong)baseRaw << FixedMath.SHIFT_AMOUNT_I);
 976145        WideGeometry.AccumulateDifferenceProduct(
 976146            coordB.m_rawValue,
 976147            baseRaw,
 976148            weightB.m_rawValue,
 976149            0L,
 976150            ref high,
 976151            ref middle,
 976152            ref low);
 976153        WideGeometry.AccumulateDifferenceProduct(
 976154            coordC.m_rawValue,
 976155            baseRaw,
 976156            weightC.m_rawValue,
 976157            0L,
 976158            ref high,
 976159            ref middle,
 976160            ref low);
 976161        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    {
 1042171        bool negative = value.Sign < 0;
 1042172        WideArithmetic.GetMagnitude(value, out ulong high, out ulong middle, out ulong low);
 1042173        if (high != 0UL || (middle >> fractionalBits) != 0UL)
 16174            return negative ? MinValue : MaxValue;
 175
 1026176        ulong magnitude = (middle << (64 - fractionalBits)) | (low >> fractionalBits);
 1026177        ulong limit = negative ? 1UL << 63 : (ulong)long.MaxValue;
 1026178        if (magnitude > limit)
 6179            return negative ? MinValue : MaxValue;
 180
 1020181        ulong remainderMask = (1UL << fractionalBits) - 1UL;
 1020182        ulong remainder = low & remainderMask;
 1020183        ulong half = 1UL << (fractionalBits - 1);
 1020184        if (remainder > half || (remainder == half && (magnitude & 1UL) != 0UL))
 185        {
 205186            if (magnitude == limit)
 2187                return negative ? MinValue : MaxValue;
 203188            magnitude++;
 189        }
 190
 1018191        long raw = negative ? unchecked(-(long)magnitude) : (long)magnitude;
 1018192        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    {
 8624240        int sign = component.Sign;
 8624241        if (sign == 0)
 3081242            return Zero;
 243
 5543244        WideArithmetic.GetMagnitude(
 5543245            ceilingMagnitude,
 5543246            out ulong magnitudeHigh,
 5543247            out ulong magnitudeMiddle,
 5543248            out ulong magnitudeLow);
 5543249        int bitLength = WideArithmetic.GetBitLength(magnitudeHigh, magnitudeMiddle, magnitudeLow);
 5543250        int shift = bitLength > 63 ? bitLength - 63 : 0;
 5543251        ulong numerator = WideArithmetic.ShiftRightToUInt64(component, shift, out _);
 5543252        ulong denominator = WideArithmetic.ShiftRightToUInt64(ceilingMagnitude, shift, out bool discarded);
 5543253        if (discarded)
 1562254            denominator++;
 255
 5543256        ulong candidate = (ulong)DivideMagnitude(numerator, denominator, false).m_rawValue;
 5543257        int midpointComparison = WideArithmetic.CompareNormalizedComponentToMidpoint(
 5543258            componentSquare,
 5543259            squaredMagnitude,
 5543260            candidate);
 5543261        if (midpointComparison > 0 || (midpointComparison == 0 && (candidate & 1UL) != 0UL))
 1262            candidate++;
 263
 5543264        long raw = (long)candidate;
 5543265        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) =>
 861274        TryRoundProductCombination(
 861275            GetExactRawProduct(firstLeft, firstRight),
 861276            GetExactRawProduct(secondLeft, secondRight),
 861277            subtract: false,
 861278            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) =>
 1064353        TryRoundProductCombination(
 1064354            GetExactRawProduct(firstLeft, firstRight),
 1064355            GetExactRawProduct(secondLeft, secondRight),
 1064356            subtract: true,
 1064357            out result);
 358
 359    private static bool TryRoundProductCombination(
 360        Signed192 first,
 361        Signed192 second,
 362        bool subtract,
 363        out Fixed64 result)
 364    {
 2394365        Signed192 numerator = subtract
 2394366            ? WideArithmetic.SubtractSigned192(first, second)
 2394367            : WideArithmetic.AddSigned192(first, second);
 2394368        bool negative = numerator.Sign < 0;
 2394369        WideArithmetic.GetMagnitude(
 2394370            numerator,
 2394371            out _,
 2394372            out ulong middle,
 2394373            out ulong low);
 2394374        if ((middle >> FixedMath.SHIFT_AMOUNT_I) != 0UL)
 375        {
 3376            result = default;
 3377            return false;
 378        }
 379
 2391380        ulong quotient = (middle << FixedMath.SHIFT_AMOUNT_I)
 2391381            | (low >> FixedMath.SHIFT_AMOUNT_I);
 2391382        ulong guardMask = 1UL << (FixedMath.SHIFT_AMOUNT_I - 1);
 2391383        bool guard = (low & guardMask) != 0UL;
 2391384        bool sticky = (low & (guardMask - 1UL)) != 0UL;
 2391385        result = RoundAndApplySign(
 2391386            quotient,
 2391387            guard,
 2391388            sticky,
 2391389            negative,
 2391390            out bool representable);
 2391391        if (representable)
 2385392            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.WideRatio.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    {
 228827        int denominatorSign = denominator.Sign;
 228828        int numeratorSign = numerator.Sign;
 228829        if (denominatorSign == 0 || (numeratorSign != 0 && numeratorSign != denominatorSign))
 30        {
 1331            result = default;
 1332            return false;
 33        }
 34
 227535        if (numeratorSign == 0)
 36        {
 2137            result = Zero;
 2138            return true;
 39        }
 40
 225441        WideArithmetic.GetMagnitude(numerator, out ulong numeratorHigh, out ulong numeratorMiddle, out ulong numeratorLo
 225442        WideArithmetic.GetMagnitude(denominator, out ulong denominatorHigh, out ulong denominatorMiddle, out ulong denom
 225443        int comparison = WideArithmetic.CompareUnsigned(
 225444            numeratorHigh,
 225445            numeratorMiddle,
 225446            numeratorLow,
 225447            denominatorHigh,
 225448            denominatorMiddle,
 225449            denominatorLow);
 225450        if (comparison > 0)
 51        {
 1352            result = default;
 1353            return false;
 54        }
 55
 224156        if (comparison == 0)
 57        {
 1458            result = One;
 1459            return true;
 60        }
 61
 222762        result = GetUnitIntervalRatio(
 222763            numeratorHigh,
 222764            numeratorMiddle,
 222765            numeratorLow,
 222766            denominatorHigh,
 222767            denominatorMiddle,
 222768            denominatorLow);
 222769        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    {
 42381        if (Signed192.TryNarrowSigned(numerator, out Signed192 narrowNumerator)
 42382            && Signed192.TryNarrowSigned(denominator, out Signed192 narrowDenominator))
 83        {
 40284            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    {
 681164        ulong guardedQuotient = 0UL;
 46308165        for (int bit = FixedMath.SHIFT_AMOUNT_I; bit >= 0; bit--)
 166        {
 22473167            WideArithmetic.ShiftLeftOne(
 22473168                ref numeratorWord4,
 22473169                ref numeratorWord3,
 22473170                ref numeratorWord2,
 22473171                ref numeratorWord1,
 22473172                ref numeratorWord0);
 22473173            guardedQuotient <<= 1;
 22473174            if (WideArithmetic.CompareUnsigned(
 22473175                numeratorWord4,
 22473176                numeratorWord3,
 22473177                numeratorWord2,
 22473178                numeratorWord1,
 22473179                numeratorWord0,
 22473180                denominatorWord4,
 22473181                denominatorWord3,
 22473182                denominatorWord2,
 22473183                denominatorWord1,
 22473184                denominatorWord0) < 0)
 185            {
 186                continue;
 187            }
 188
 10192189            WideArithmetic.SubtractUnsigned(
 10192190                ref numeratorWord4,
 10192191                ref numeratorWord3,
 10192192                ref numeratorWord2,
 10192193                ref numeratorWord1,
 10192194                ref numeratorWord0,
 10192195                denominatorWord4,
 10192196                denominatorWord3,
 10192197                denominatorWord2,
 10192198                denominatorWord1,
 10192199                denominatorWord0);
 10192200            guardedQuotient |= 1UL;
 201        }
 202
 681203        ulong rounded = RoundGuardedQuotientToEven(
 681204            guardedQuotient,
 681205            (numeratorWord4 | numeratorWord3 | numeratorWord2 | numeratorWord1 | numeratorWord0) != 0UL,
 681206            out _);
 681207        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    {
 4691219        if ((numeratorHigh | denominatorHigh) == 0UL)
 220        {
 4063221            return GetUnitIntervalRatio128(
 4063222                numeratorMiddle,
 4063223                numeratorLow,
 4063224                denominatorMiddle,
 4063225                denominatorLow);
 226        }
 227
 628228        ulong guardedQuotient = 0UL;
 42704229        for (int bit = FixedMath.SHIFT_AMOUNT_I; bit >= 0; bit--)
 230        {
 20724231            WideArithmetic.ShiftLeftOne(ref numeratorHigh, ref numeratorMiddle, ref numeratorLow);
 20724232            guardedQuotient <<= 1;
 20724233            if (WideArithmetic.CompareUnsigned(
 20724234                numeratorHigh,
 20724235                numeratorMiddle,
 20724236                numeratorLow,
 20724237                denominatorHigh,
 20724238                denominatorMiddle,
 20724239                denominatorLow) < 0)
 240            {
 241                continue;
 242            }
 243
 7592244            WideArithmetic.SubtractUnsigned(
 7592245                ref numeratorHigh,
 7592246                ref numeratorMiddle,
 7592247                ref numeratorLow,
 7592248                denominatorHigh,
 7592249                denominatorMiddle,
 7592250                denominatorLow);
 7592251            guardedQuotient |= 1UL;
 252        }
 253
 628254        ulong rounded = RoundGuardedQuotientToEven(
 628255            guardedQuotient,
 628256            (numeratorHigh | numeratorMiddle | numeratorLow) != 0UL,
 628257            out _);
 628258        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    {
 4063268        ulong guardedQuotient = 0UL;
 276284269        for (int bit = FixedMath.SHIFT_AMOUNT_I; bit >= 0; bit--)
 270        {
 134079271            bool overflow = (numeratorHigh & (1UL << 63)) != 0UL;
 134079272            numeratorHigh = (numeratorHigh << 1) | (numeratorLow >> 63);
 134079273            numeratorLow <<= 1;
 134079274            guardedQuotient <<= 1;
 275
 134079276            if (!overflow
 134079277                && (numeratorHigh < denominatorHigh
 134079278                    || (numeratorHigh == denominatorHigh && numeratorLow < denominatorLow)))
 279            {
 280                continue;
 281            }
 282
 46999283            ulong previousLow = numeratorLow;
 46999284            numeratorLow = unchecked(numeratorLow - denominatorLow);
 46999285            ulong borrow = previousLow < denominatorLow ? 1UL : 0UL;
 46999286            numeratorHigh = unchecked(numeratorHigh - denominatorHigh - borrow);
 46999287            guardedQuotient |= 1UL;
 288        }
 289
 4063290        ulong rounded = RoundGuardedQuotientToEven(
 4063291            guardedQuotient,
 4063292            (numeratorHigh | numeratorLow) != 0UL,
 4063293            out _);
 4063294        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    {
 2304        int length = Math.Max(numerator.Length, denominator.Length) + 1;
 2305        Span<ulong> numeratorMagnitude = stackalloc ulong[length];
 2306        Span<ulong> remainder = stackalloc ulong[length];
 2307        Span<ulong> denominatorMagnitude = stackalloc ulong[length];
 2308        numeratorMagnitude.Clear();
 2309        remainder.Clear();
 2310        denominatorMagnitude.Clear();
 2311        numerator.CopyTo(numeratorMagnitude);
 2312        denominator.CopyTo(denominatorMagnitude);
 2313        ShiftLeftMagnitude(
 2314            numeratorMagnitude,
 2315            FixedMath.SHIFT_AMOUNT_I,
 2316            remainder);
 2317        _ = TryGetSignedRawRatioCore(
 2318            remainder,
 2319            denominatorMagnitude,
 2320            negative: false,
 2321            roundToEven: true,
 2322            out Fixed64 result);
 2323        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    {
 8132332        int numeratorSign = numerator.Sign;
 8132333        int denominatorSign = denominator.Sign;
 8132334        if (numeratorSign == 0)
 2555335            return Zero;
 5577336        if (denominatorSign == 0)
 2337            return numeratorSign < 0 ? MinValue : MaxValue;
 338
 5575339        bool negative = numeratorSign != denominatorSign;
 5575340        WideArithmetic.GetMagnitude(numerator, out ulong numeratorHigh, out ulong numeratorMiddle, out ulong numeratorLo
 5575341        WideArithmetic.GetMagnitude(denominator, out ulong denominatorHigh, out ulong denominatorMiddle, out ulong denom
 342
 5575343        if (!negative)
 344        {
 4257345            int comparison = WideArithmetic.CompareUnsigned(
 4257346                numeratorHigh, numeratorMiddle, numeratorLow,
 4257347                denominatorHigh, denominatorMiddle, denominatorLow);
 4257348            if (comparison <= 0)
 349            {
 3818350                return comparison == 0
 3818351                    ? One
 3818352                    : GetUnitIntervalRatio(
 3818353                        numeratorHigh, numeratorMiddle, numeratorLow,
 3818354                        denominatorHigh, denominatorMiddle, denominatorLow);
 355            }
 356        }
 357
 1757358        int numeratorBits = WideArithmetic.GetBitLength(numeratorHigh, numeratorMiddle, numeratorLow);
 1757359        int denominatorBits = WideArithmetic.GetBitLength(denominatorHigh, denominatorMiddle, denominatorLow);
 1757360        int integerShift = numeratorBits - denominatorBits;
 1757361        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
 1743383        ulong guardedQuotient = 0UL;
 1743384        if (integerShift >= 0)
 385        {
 1340386            WideArithmetic.ShiftLeft(
 1340387                denominatorHigh,
 1340388                denominatorMiddle,
 1340389                denominatorLow,
 1340390                integerShift,
 1340391                out ulong shiftedHigh,
 1340392                out ulong shiftedMiddle,
 1340393                out ulong shiftedLow);
 9714394            for (int bit = integerShift; bit >= 0; bit--)
 395            {
 3517396                if (WideArithmetic.CompareUnsigned(
 3517397                    numeratorHigh, numeratorMiddle, numeratorLow,
 3517398                    shiftedHigh, shiftedMiddle, shiftedLow) >= 0)
 399                {
 2179400                    WideArithmetic.SubtractUnsigned(
 2179401                        ref numeratorHigh,
 2179402                        ref numeratorMiddle,
 2179403                        ref numeratorLow,
 2179404                        shiftedHigh,
 2179405                        shiftedMiddle,
 2179406                        shiftedLow);
 2179407                    guardedQuotient |= 1UL << bit;
 408                }
 409
 3517410                WideArithmetic.ShiftRightOne(ref shiftedHigh, ref shiftedMiddle, ref shiftedLow);
 411            }
 412        }
 413
 118524414        for (int bit = FixedMath.SHIFT_AMOUNT_I; bit >= 0; bit--)
 415        {
 57519416            WideArithmetic.ShiftLeftOne(ref numeratorHigh, ref numeratorMiddle, ref numeratorLow);
 57519417            guardedQuotient <<= 1;
 57519418            if (WideArithmetic.CompareUnsigned(
 57519419                numeratorHigh, numeratorMiddle, numeratorLow,
 57519420                denominatorHigh, denominatorMiddle, denominatorLow) < 0)
 421            {
 422                continue;
 423            }
 424
 13199425            WideArithmetic.SubtractUnsigned(
 13199426                ref numeratorHigh,
 13199427                ref numeratorMiddle,
 13199428                ref numeratorLow,
 13199429                denominatorHigh,
 13199430                denominatorMiddle,
 13199431                denominatorLow);
 13199432            guardedQuotient |= 1UL;
 433        }
 434
 1743435        ulong magnitude = RoundGuardedQuotientToEven(
 1743436            guardedQuotient,
 1743437            (numeratorHigh | numeratorMiddle | numeratorLow) != 0UL,
 1743438            out bool roundedOverflow);
 1743439        ulong limit = negative ? 1UL << 63 : (ulong)long.MaxValue;
 1743440        if (roundedOverflow || magnitude > limit)
 3441            return negative ? MinValue : MaxValue;
 442
 1740443        long raw = negative ? unchecked(-(long)magnitude) : (long)magnitude;
 1740444        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    {
 6490453        if (Signed192.TryNarrowSigned(numerator, out Signed192 narrowNumerator)
 6490454            && Signed192.TryNarrowSigned(denominator, out Signed192 narrowDenominator))
 455        {
 5493456            return GetSignedRatio(narrowNumerator, narrowDenominator);
 457        }
 458
 997459        int numeratorSign = numerator.Sign;
 997460        int denominatorSign = denominator.Sign;
 997461        if (numeratorSign == 0)
 88462            return Zero;
 909463        if (denominatorSign == 0)
 2464            return numeratorSign < 0 ? MinValue : MaxValue;
 465
 907466        bool negative = numeratorSign != denominatorSign;
 907467        WideArithmetic.GetMagnitude(
 907468            numerator,
 907469            out ulong numeratorWord4,
 907470            out ulong numeratorWord3,
 907471            out ulong numeratorWord2,
 907472            out ulong numeratorWord1,
 907473            out ulong numeratorWord0);
 907474        WideArithmetic.GetMagnitude(
 907475            denominator,
 907476            out ulong denominatorWord4,
 907477            out ulong denominatorWord3,
 907478            out ulong denominatorWord2,
 907479            out ulong denominatorWord1,
 907480            out ulong denominatorWord0);
 481
 907482        int comparison = WideArithmetic.CompareUnsigned(
 907483            numeratorWord4,
 907484            numeratorWord3,
 907485            numeratorWord2,
 907486            numeratorWord1,
 907487            numeratorWord0,
 907488            denominatorWord4,
 907489            denominatorWord3,
 907490            denominatorWord2,
 907491            denominatorWord1,
 907492            denominatorWord0);
 907493        if (comparison <= 0)
 494        {
 893495            if (comparison == 0)
 226496                return negative ? -One : One;
 497
 667498            Fixed64 unitRatio = GetUnitIntervalRatio(
 667499                numeratorWord4,
 667500                numeratorWord3,
 667501                numeratorWord2,
 667502                numeratorWord1,
 667503                numeratorWord0,
 667504                denominatorWord4,
 667505                denominatorWord3,
 667506                denominatorWord2,
 667507                denominatorWord1,
 667508                denominatorWord0);
 667509            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    {
 2757728        int numeratorSign = numerator.Sign;
 2757729        if (numeratorSign == 0)
 202730            return Zero;
 731
 2555732        WideArithmetic.GetMagnitude(
 2555733            denominator,
 2555734            out _,
 2555735            out _,
 2555736            out ulong denominatorLow);
 2555737        WideArithmetic.GetMagnitude(
 2555738            numerator,
 2555739            out _,
 2555740            out _,
 2555741            out _,
 2555742            out ulong word1,
 2555743            out ulong word0);
 744
 2555745        ulong quotient = Divide128By64(
 2555746            word1,
 2555747            word0,
 2555748            denominatorLow,
 2555749            out ulong remainder);
 2555750        int midpointComparison = remainder.CompareTo(denominatorLow - remainder);
 2555751        if (midpointComparison > 0 || (midpointComparison == 0 && (quotient & 1UL) != 0UL))
 747752            quotient++;
 753
 2555754        long raw = numeratorSign < 0 ? unchecked(-(long)quotient) : (long)quotient;
 2555755        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    {
 135031768        int numeratorSign = numerator.Sign;
 135031769        int denominatorSign = denominator.Sign;
 135031770        if (denominatorSign == 0)
 771        {
 6772            result = default;
 6773            return false;
 774        }
 135025775        if (numeratorSign == 0)
 776        {
 23989777            result = Zero;
 23989778            return true;
 779        }
 780
 111036781        bool negative = numeratorSign != denominatorSign;
 111036782        if (Signed192.TryNarrowSigned(numerator, out Signed192 numerator192)
 111036783            && Signed192.TryNarrowSigned(denominator, out Signed192 denominator192))
 784        {
 96218785            Span<ulong> narrowRemainder = stackalloc ulong[3];
 96218786            Span<ulong> narrowDenominator = stackalloc ulong[3];
 96218787            WideArithmetic.GetMagnitude(
 96218788                numerator192,
 96218789                out narrowRemainder[2], out narrowRemainder[1], out narrowRemainder[0]);
 96218790            WideArithmetic.GetMagnitude(
 96218791                denominator192,
 96218792                out narrowDenominator[2], out narrowDenominator[1], out narrowDenominator[0]);
 96218793            return TryGetSignedRawRatioCore(
 96218794                narrowRemainder,
 96218795                narrowDenominator,
 96218796                negative,
 96218797                roundToEven: true,
 96218798                out result);
 799        }
 14818800        if (Signed320.TryNarrowSigned(numerator, out Signed320 numerator320)
 14818801            && Signed320.TryNarrowSigned(denominator, out Signed320 denominator320))
 802        {
 14413803            Span<ulong> mediumRemainder = stackalloc ulong[5];
 14413804            Span<ulong> mediumDenominator = stackalloc ulong[5];
 14413805            WideArithmetic.GetMagnitude(
 14413806                numerator320,
 14413807                out mediumRemainder[4], out mediumRemainder[3], out mediumRemainder[2],
 14413808                out mediumRemainder[1], out mediumRemainder[0]);
 14413809            WideArithmetic.GetMagnitude(
 14413810                denominator320,
 14413811                out mediumDenominator[4], out mediumDenominator[3], out mediumDenominator[2],
 14413812                out mediumDenominator[1], out mediumDenominator[0]);
 14413813            return TryGetSignedRawRatioCore(
 14413814                mediumRemainder,
 14413815                mediumDenominator,
 14413816                negative,
 14413817                roundToEven: true,
 14413818                out result);
 819        }
 820
 405821        Span<ulong> remainder = stackalloc ulong[9];
 405822        Span<ulong> denominatorMagnitude = stackalloc ulong[9];
 405823        WideArithmetic.GetMagnitude(numerator, remainder);
 405824        WideArithmetic.GetMagnitude(denominator, denominatorMagnitude);
 405825        return TryGetSignedRawRatioCore(
 405826            remainder,
 405827            denominatorMagnitude,
 405828            negative,
 405829            roundToEven: true,
 405830            out result);
 831    }
 832
 833    /// <summary>
 834    /// Converts an exact signed nine-word numerator over a five-word denominator
 835    /// directly to a raw integer with round-half-to-even. Returns false/default
 836    /// for zero denominator or an unrepresentable final signed result.
 837    /// </summary>
 838    internal static bool TryGetSignedRawRatio(
 839        Signed576 numerator,
 840        Signed320 denominator,
 841        out Fixed64 result)
 842    {
 215843        int numeratorSign = numerator.Sign;
 215844        int denominatorSign = denominator.Sign;
 215845        if (denominatorSign == 0)
 846        {
 8847            result = default;
 8848            return false;
 849        }
 207850        if (numeratorSign == 0)
 851        {
 4852            result = Zero;
 4853            return true;
 854        }
 855
 203856        Span<ulong> numeratorMagnitude = stackalloc ulong[9];
 203857        Span<ulong> denominatorMagnitude = stackalloc ulong[5];
 203858        WideArithmetic.GetMagnitude(numerator, numeratorMagnitude);
 203859        WideArithmetic.GetMagnitude(denominator,
 203860            out denominatorMagnitude[4], out denominatorMagnitude[3],
 203861            out denominatorMagnitude[2], out denominatorMagnitude[1], out denominatorMagnitude[0]);
 203862        Span<ulong> quotient = stackalloc ulong[9];
 203863        Span<ulong> remainder = stackalloc ulong[5];
 203864        Span<ulong> scratch = stackalloc ulong[15];
 203865        WideArithmetic.DivideMagnitudes(
 203866            numeratorMagnitude, denominatorMagnitude, quotient, remainder, scratch);
 203867        if (GetActiveMagnitudeLength(quotient) > 1)
 868        {
 51869            result = default;
 51870            return false;
 871        }
 872
 152873        WideArithmetic.SubtractEqualMagnitudes(
 152874            denominatorMagnitude, remainder, denominatorMagnitude);
 152875        int midpointComparison = WideArithmetic.CompareMagnitudeEqualLength(
 152876            remainder, denominatorMagnitude);
 152877        return TryCreateRawRatioResult(quotient[0], midpointComparison,
 152878            numeratorSign != denominatorSign, out result);
 879    }
 880
 881    /// <summary>
 882    /// Converts an exact signed three-word ratio directly to a raw scalar with
 883    /// round-half-to-even. Returns false/default for a zero denominator or final
 884    /// signed scalar overflow; it does not apply an additional Q32.32 scale.
 885    /// </summary>
 886    internal static bool TryGetSignedRawRatio(Signed192 numerator, Signed192 denominator, out Fixed64 result)
 887    {
 294888        if (denominator.IsZero)
 889        {
 21890            result = default;
 21891            return false;
 892        }
 273893        if (numerator.IsZero)
 894        {
 13895            result = Zero;
 13896            return true;
 897        }
 260898        WideArithmetic.GetMagnitude(numerator, out ulong high, out ulong middle, out ulong low);
 260899        WideArithmetic.GetMagnitude(denominator, out ulong divisorHigh, out ulong divisorMiddle, out ulong divisorLow);
 260900        bool negative = numerator.Sign != denominator.Sign;
 260901        if (divisorHigh == 0 && divisorLow == 0)
 902        {
 903            // With B=2^64 and D=d*B, divide the upper two numerator words by d.
 904            // The exact remainder is r*B+low. Compare it with D-remainder to
 905            // round once without dropping low-word fractions or doubling overflow.
 120906            if (high >= divisorMiddle)
 907            {
 9908                result = default;
 9909                return false;
 910            }
 111911            ulong quotient = Divide128By64(high, middle, divisorMiddle, out ulong remainderHigh);
 111912            ulong complementHigh = divisorMiddle - remainderHigh - (low != 0 ? 1UL : 0UL);
 111913            ulong complementLow = unchecked(0UL - low);
 111914            int midpoint = remainderHigh.CompareTo(complementHigh);
 111915            if (midpoint == 0)
 35916                midpoint = low.CompareTo(complementLow);
 111917            return TryCreateRawRatioResult(quotient, midpoint, negative, out result);
 918        }
 140919        return TryGetSignedRawRatio(
 140920            Signed576.ExtendValue(Signed320.ExtendValue(numerator)),
 140921            Signed320.ExtendValue(denominator), out result);
 922    }
 923
 924    /// <summary>
 925    /// Converts an exact signed eleven-word ratio directly to a raw integer
 926    /// with round-half-to-even.
 927    /// </summary>
 928    internal static bool TryGetSignedRawRatio(
 929        Signed704 numerator,
 930        Signed704 denominator,
 931        out Fixed64 result)
 932    {
 3261933        int numeratorSign = numerator.Sign;
 3261934        int denominatorSign = denominator.Sign;
 3261935        if (denominatorSign == 0)
 936        {
 1937            result = default;
 1938            return false;
 939        }
 3260940        if (numeratorSign == 0)
 941        {
 138942            result = Zero;
 138943            return true;
 944        }
 945
 3122946        Span<ulong> remainder = stackalloc ulong[11];
 3122947        Span<ulong> denominatorMagnitude = stackalloc ulong[11];
 3122948        WideArithmetic.GetMagnitude(numerator, remainder);
 3122949        WideArithmetic.GetMagnitude(denominator, denominatorMagnitude);
 3122950        return TryGetSignedRawRatioCore(
 3122951            remainder,
 3122952            denominatorMagnitude,
 3122953            numeratorSign != denominatorSign,
 3122954            roundToEven: true,
 3122955            out result);
 956    }
 957
 958    internal static bool TryGetSignedRawRatio(
 959        Signed832 numerator,
 960        Signed832 denominator,
 961        int numeratorLeftShift,
 962        out Fixed64 result)
 963    {
 2824964        int numeratorSign = numerator.Sign;
 2824965        int denominatorSign = denominator.Sign;
 2824966        if (denominatorSign == 0)
 967        {
 1968            result = default;
 1969            return false;
 970        }
 2823971        if (numeratorSign == 0)
 972        {
 945973            result = Zero;
 945974            return true;
 975        }
 976
 1878977        Span<ulong> numeratorMagnitude = stackalloc ulong[14];
 1878978        Span<ulong> denominatorMagnitude = stackalloc ulong[14];
 1878979        Span<ulong> shiftedNumerator = stackalloc ulong[14];
 1878980        numeratorMagnitude.Clear();
 1878981        denominatorMagnitude.Clear();
 1878982        shiftedNumerator.Clear();
 1878983        WideArithmetic.GetMagnitude(numerator, numeratorMagnitude);
 1878984        WideArithmetic.GetMagnitude(denominator, denominatorMagnitude);
 1878985        ShiftLeftMagnitude(
 1878986            numeratorMagnitude,
 1878987            numeratorLeftShift,
 1878988            shiftedNumerator);
 1878989        return TryGetSignedRawRatioCore(
 1878990            shiftedNumerator,
 1878991            denominatorMagnitude,
 1878992            numeratorSign != denominatorSign,
 1878993            roundToEven: true,
 1878994            out result);
 995    }
 996
 997    internal static Fixed64 GetNonNegativeRawRatioFloor(
 998        Signed704 numerator,
 999        Signed704 denominator)
 1000    {
 59541001        if (numerator.Sign <= 0)
 12431002            return Zero;
 1003
 47111004        Span<ulong> remainder = stackalloc ulong[11];
 47111005        Span<ulong> denominatorMagnitude = stackalloc ulong[11];
 47111006        WideArithmetic.GetMagnitude(numerator, remainder);
 47111007        WideArithmetic.GetMagnitude(denominator, denominatorMagnitude);
 47111008        return TryGetSignedRawRatioCore(
 47111009            remainder,
 47111010            denominatorMagnitude,
 47111011            negative: false,
 47111012            roundToEven: false,
 47111013            out Fixed64 result)
 47111014            ? result
 47111015            : MaxValue;
 1016    }
 1017
 1018    internal static bool TryGetSignedRawRatio(
 1019        ReadOnlySpan<ulong> numeratorMagnitude,
 1020        ReadOnlySpan<ulong> denominatorMagnitude,
 1021        bool negative,
 1022        out Fixed64 result,
 1023        long parityOffset = 0)
 1024    {
 540341025        int denominatorLength =
 540341026            GetActiveMagnitudeLength(denominatorMagnitude);
 540341027        if (denominatorLength == 1
 540341028            && denominatorMagnitude[0] == 0UL)
 1029        {
 251030            result = default;
 251031            return false;
 1032        }
 1033
 540091034        int numeratorLength =
 540091035            GetActiveMagnitudeLength(numeratorMagnitude);
 540091036        if (numeratorLength == 1
 540091037            && numeratorMagnitude[0] == 0UL)
 1038        {
 153461039            result = Zero;
 153461040            return true;
 1041        }
 1042
 1043        // Immutable views lose the same exact factor 2^(64*k) before copying
 1044        // into local mutable spans. Quotient, half ties and signed overflow
 1045        // are unchanged; typed ratio paths keep their original core cost.
 386631046        if (denominatorLength > 1 && numeratorLength > 1 && (numeratorMagnitude[0] | denominatorMagnitude[0]) == 0UL)
 1047        {
 464581048            int commonWords = 1, commonLimit = Math.Min(numeratorLength, denominatorLength);
 624901049            while (commonWords + 1 < commonLimit && (numeratorMagnitude[commonWords] | denominatorMagnitude[commonWords]
 392611050                commonWords++;
 464581051            numeratorMagnitude = numeratorMagnitude[commonWords..]; denominatorMagnitude = denominatorMagnitude[commonWo
 464581052            numeratorLength -= commonWords; denominatorLength -= commonWords;
 1053        }
 1054
 386631055        int length = Math.Max(numeratorLength, denominatorLength);
 386631056        Span<ulong> remainder = stackalloc ulong[length];
 386631057        Span<ulong> denominator = stackalloc ulong[length];
 386631058        remainder.Clear();
 386631059        denominator.Clear();
 386631060        numeratorMagnitude[..numeratorLength].CopyTo(remainder);
 386631061        denominatorMagnitude[..denominatorLength].CopyTo(denominator);
 386631062        return TryGetSignedRawRatioCore(
 386631063            remainder,
 386631064            denominator,
 386631065            negative,
 386631066            roundToEven: true,
 386631067            out result,
 386631068            parityOffset);
 1069    }
 1070
 1071    // Isolate normalization scratch so typed callers keep their smaller stack frame.
 1072    [MethodImpl(MethodImplOptions.NoInlining)]
 1073    private static bool TryGetBinaryReducedSignedRawRatio(
 1074        Signed576 numerator, Signed576 denominator, out Fixed64 result)
 1075    {
 121076        Span<ulong> numeratorMagnitude = stackalloc ulong[9];
 121077        Span<ulong> denominatorMagnitude = stackalloc ulong[9];
 121078        WideArithmetic.GetMagnitude(numerator, numeratorMagnitude);
 121079        WideArithmetic.GetMagnitude(denominator, denominatorMagnitude);
 121080        return TryGetSignedRawRatio(
 121081            numeratorMagnitude, denominatorMagnitude,
 121082            numerator.Sign != denominator.Sign, out result);
 1083    }
 1084
 1085    private static bool TryGetSignedRawRatioCore(
 1086        Span<ulong> remainder,
 1087        Span<ulong> denominatorMagnitude,
 1088        bool negative,
 1089        bool roundToEven,
 1090        out Fixed64 result,
 1091        long parityOffset = 0)
 1092    {
 1594121093        int remainderLength = GetActiveMagnitudeLength(remainder);
 1594121094        int denominatorLength = GetActiveMagnitudeLength(denominatorMagnitude);
 1594121095        if (denominatorLength == 1
 1594121096            && denominatorMagnitude[0] == 0UL)
 1097        {
 21098            result = default;
 21099            return false;
 1100        }
 1101
 1594101102        int activeLength = Math.Max(remainderLength, denominatorLength);
 1594101103        Span<ulong> activeRemainder = remainder[..activeLength];
 1594101104        Span<ulong> activeDenominator = denominatorMagnitude[..activeLength];
 1594101105        int quotientBit = GetMagnitudeBitLength(remainder[..remainderLength])
 1594101106            - GetMagnitudeBitLength(denominatorMagnitude[..denominatorLength]);
 1594101107        if (quotientBit > 63)
 1108        {
 2891109            result = default;
 2891110            return false;
 1111        }
 1112
 1591211113        if (denominatorLength == 1)
 1114        {
 982871115            ulong denominator = denominatorMagnitude[0];
 982871116            ulong numeratorHigh = remainderLength == 2 ? remainder[1] : 0UL;
 982871117            ulong singleWordQuotient = Divide128By64(
 982871118                numeratorHigh,
 982871119                remainder[0],
 982871120                denominator,
 982871121                out ulong singleWordRemainder);
 982871122            int singleWordMidpointComparison = roundToEven
 982871123                ? singleWordRemainder.CompareTo(denominator - singleWordRemainder)
 982871124                : -1;
 982871125            return TryCreateRawRatioResult(
 982871126                singleWordQuotient,
 982871127                singleWordMidpointComparison,
 982871128                negative,
 982871129                out result,
 982871130                parityOffset);
 1131        }
 1132
 608341133        ulong quotient = 0UL;
 608341134        if (quotientBit >= 0)
 1135        {
 600501136            Span<ulong> shiftedDenominatorStorage =
 600501137                stackalloc ulong[denominatorMagnitude.Length];
 600501138            Span<ulong> shiftedDenominator = shiftedDenominatorStorage[..activeLength];
 600501139            ShiftLeftMagnitude(activeDenominator, quotientBit, shiftedDenominator);
 42073941140            for (int bit = quotientBit; bit >= 0; bit--)
 1141            {
 20436471142                if (WideArithmetic.CompareMagnitudeEqualLength(
 20436471143                        activeRemainder,
 20436471144                        shiftedDenominator) >= 0)
 1145                {
 5627471146                    WideArithmetic.SubtractEqualMagnitudes(
 5627471147                        activeRemainder,
 5627471148                        shiftedDenominator,
 5627471149                        activeRemainder);
 5627471150                    quotient |= 1UL << bit;
 1151                }
 1152
 20436471153                ShiftRightOne(shiftedDenominator);
 1154            }
 1155        }
 1156
 608341157        int midpointComparison = -1;
 608341158        if (roundToEven)
 1159        {
 586441160            Span<ulong> denominatorMinusRemainderStorage =
 586441161                stackalloc ulong[denominatorMagnitude.Length];
 586441162            Span<ulong> denominatorMinusRemainder =
 586441163                denominatorMinusRemainderStorage[..activeLength];
 586441164            activeDenominator.CopyTo(denominatorMinusRemainder);
 586441165            WideArithmetic.SubtractEqualMagnitudes(
 586441166                denominatorMinusRemainder,
 586441167                activeRemainder,
 586441168                denominatorMinusRemainder);
 586441169            midpointComparison = WideArithmetic.CompareMagnitudeEqualLength(
 586441170                activeRemainder,
 586441171                denominatorMinusRemainder);
 1172        }
 608341173        return TryCreateRawRatioResult(
 608341174            quotient,
 608341175            midpointComparison,
 608341176            negative,
 608341177            out result,
 608341178            parityOffset);
 1179    }
 1180
 1181    private static bool TryCreateRawRatioResult(
 1182        ulong quotient,
 1183        int midpointComparison,
 1184        bool negative,
 1185        out Fixed64 result,
 1186        long parityOffset = 0)
 1187    {
 1188        // The caller may retain a rounded world coordinate relative to an
 1189        // integer origin. Only half ties depend on that origin's parity;
 1190        // signed magnitude division and final range checks stay unchanged.
 1593841191        if (midpointComparison > 0 || (midpointComparison == 0 && ((quotient ^ unchecked((ulong)parityOffset)) & 1UL) !=
 1192        {
 317961193            quotient++;
 317961194            if (quotient == 0UL)
 1195            {
 91196                result = default;
 91197                return false;
 1198            }
 1199        }
 1200
 1593751201        ulong limit = negative ? 1UL << 63 : (ulong)long.MaxValue;
 1593751202        if (quotient > limit)
 1203        {
 5801204            result = default;
 5801205            return false;
 1206        }
 1207
 1587951208        long raw = negative ? unchecked(-(long)quotient) : (long)quotient;
 1587951209        result = new Fixed64(raw);
 1587951210        return true;
 1211    }
 1212
 1213    private static int GetMagnitudeBitLength(ReadOnlySpan<ulong> value)
 1214    {
 3188201215        int index = value.Length - 1;
 3188201216        return (index * 64) + 64 - CountLeadingZeroes(value[index]);
 1217    }
 1218
 1219    private static int GetActiveMagnitudeLength(ReadOnlySpan<ulong> value)
 1220    {
 4270701221        int length = value.Length;
 31451081222        while (length > 1 && value[length - 1] == 0UL)
 27180381223            length--;
 4270701224        return length;
 1225    }
 1226
 1227    private static void ShiftLeftMagnitude(
 1228        ReadOnlySpan<ulong> source,
 1229        int bits,
 1230        Span<ulong> destination)
 1231    {
 619301232        if (bits == 0)
 1233        {
 15041234            source.CopyTo(destination);
 15041235            return;
 1236        }
 1237
 604261238        destination[0] = source[0] << bits;
 5025041239        for (int index = 1; index < destination.Length; index++)
 1908261240            destination[index] = (source[index] << bits) | (source[index - 1] >> (64 - bits));
 604261241    }
 1242
 1243    private static void ShiftRightOne(Span<ulong> value)
 1244    {
 20436471245        ulong carry = 0UL;
 212570161246        for (int index = value.Length - 1; index >= 0; index--)
 1247        {
 85848611248            ulong nextCarry = value[index] << 63;
 85848611249            value[index] = (value[index] >> 1) | carry;
 85848611250            carry = nextCarry;
 1251        }
 20436471252    }
 1253}

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&)
TryRoundNonnegativeQ128(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.Signed576,FixedMathSharp.Signed320,FixedMathSharp.Fixed64&)
TryGetSignedRawRatio(FixedMathSharp.Signed192,FixedMathSharp.Signed192,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&,System.Int64)
TryGetBinaryReducedSignedRawRatio(FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Fixed64&)
TryGetSignedRawRatioCore(System.Span`1<System.UInt64>,System.Span`1<System.UInt64>,System.Boolean,System.Boolean,FixedMathSharp.Fixed64&,System.Int64)
TryCreateRawRatioResult(System.UInt64,System.Int32,System.Boolean,FixedMathSharp.Fixed64&,System.Int64)
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>)