UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
PolynomialRootSolver.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
6#include "Containers/Array.h"
9
10namespace UE::Math
11{
12
16template<typename RealType, int32 PolynomialDegree>
18{
19 static_assert(PolynomialDegree >= 2, "PolynomialDegree must be 2 or higher");
20
21 // Holds last-found polynomial roots
23
25
41
54 RealType Tolerance = (RealType)UE_SMALL_NUMBER, int32 MaxNewtonIterations = 20, RealType NearRootTolerance = (RealType)UE_SMALL_NUMBER)
55 {
56 Roots.Reset();
57
58 if (!ensure(PolyCoeffs.Num() >= PolynomialDegree + 1))
59 {
60 return 0;
61 }
62
63 // Helper to evaluate a polynomial
64 auto EvalPolynomial = [](RealType* Coeffs, int32 Degree, RealType Param)
65 {
66 RealType Value = Coeffs[Degree];
67 for (int32 CoeffIdx = Degree - 1; CoeffIdx >= 0; --CoeffIdx)
68 {
69 Value = Value * Param + Coeffs[CoeffIdx];
70 }
71 return Value;
72 };
73
74 // Local arrays to store polynomial coefficients and coefficients of derivatives
77
78 // Local storage for found roots (including incrementally of derivatives)
79 // Note: has an extra element so we can add RangeEnd to end, to make iteration over test intervals easier below
82
83 // Build quadratic derivative, rescaled so that the constant coefficient is the same as the input polynomial (i.e., divided by Factorial(PolynomialDegree - 2))
86 LocalCoeffs[2] = ((RealType).5 * RealType(PolynomialDegree * (PolynomialDegree - 1))) * PolyCoeffs[PolynomialDegree];
87 // Directly solve quadratic roots
88 RealType Discrim = LocalCoeffs[1] * LocalCoeffs[1] - (RealType)4.0 * LocalCoeffs[0] * LocalCoeffs[2];
89 if (Discrim >= 0)
90 {
91 RealType RootDiscrim = FMath::Sqrt(Discrim);
92 RealType BPlusSignBTimesRootDiscrim = -RealType(.5) * (LocalCoeffs[1] + (LocalCoeffs[1] < 0 ? -RootDiscrim : RootDiscrim));
93 // Guard against divide by exact 0 to avoid fast math failure case; otherwise rely on the (RangeStart, RangeEnd) interval test to filter bad results
95 {
97 if (Root0 > RangeStart && Root0 < RangeEnd)
98 {
99 FoundRoots[0] = Root0;
101 }
102 }
103 if (LocalCoeffs[2] != 0)
104 {
106 if (Root1 > RangeStart && Root1 < RangeEnd)
107 {
109 // Order roots and filter exact duplicates
110 if (NumFoundRoots == 2)
111 {
112 if (FoundRoots[1] < FoundRoots[0])
113 {
114 Swap(FoundRoots[0], FoundRoots[1]);
115 }
116 else if (FoundRoots[0] == FoundRoots[1])
117 {
118 NumFoundRoots = 1;
119 }
120 }
121 }
122 }
123 }
124
125 // Note: this constexpr if statement needed to work around compiler warning that the below loop won't execute for quadratic case ("Ill-defined for-loop. Loop body not executed.")
126 if constexpr (PolynomialDegree >= 3)
127 {
128
130 {
131 // Add a fake root at the range end to facilitate iteration below
133
134 // Use last LocalCoeffs as the derivative of the next one
135 // Multiply back scale factor s.t. the constant factor can always be directly copied from the source polynomial
136 RealType DerivScale = RealType(1 + PolynomialDegree - CurDegree);
138 {
140 }
141
142 // Integrate derivative to get next polynomial
144 {
146 {
148 }
149 // Copy constant coefficient from the source polynomial
151 }
152 // Once we're back to the original polynomial, we can just copy its coefficients directly instead
153 else
154 {
156 {
158 }
159 }
160
161 // Check for roots in each interval from (range start, deriv root 0, ..., deriv root N, range end)
162 RealType CurStart = RangeStart;
165 int32 NumNewRoots = 0;
166 for (int32 RootIdx = 0; RootIdx < NumFoundRoots + 1; ++RootIdx)
167 {
168 RealType CurEnd = FoundRoots[RootIdx];
169
170 // expect roots to be in increasing order
172
173 RealType CurEndValue = EvalPolynomial(LocalCoeffs.GetData(), CurDegree, CurEnd);
174
175 // If sign changes, find root inside interval
176 if (CurStartValue * CurEndValue < 0)
177 {
179 RealType BeginSign = FMath::Sign(SearchBeginValue);
180 RealType SearchEnd = CurEnd;
181 RealType SearchParam = (RealType).5 * (SearchBegin + SearchEnd);
183 RealType SearchStepSize;
184 do
185 {
187 // Reduce the search range based on the sign of the search value
188 if (SearchParamValue * BeginSign > 0)
189 {
191 }
192 else
193 {
194 SearchEnd = SearchParam;
195 }
197 RealType NextSearchParam;
198 // Guard against divide by exact 0 to avoid fast math failure case; otherwise rely on the (SearchBegin, SearchEnd) interval test to filter bad results
199 if (SearchParamDerivValue != 0)
200 {
202 if (NewtonApproxRoot > SearchBegin && NewtonApproxRoot < SearchEnd)
203 {
205 }
206 else
207 {
208 NextSearchParam = (RealType).5 * (SearchBegin + SearchEnd);
209 }
210 }
211 else
212 {
213 NextSearchParam = (RealType).5 * (SearchBegin + SearchEnd);
214 }
217 } while (SearchStepSize > Tolerance && --SearchIters > 0);
218
219 // Write the new roots back to the FoundRoots array (since this will always trail behind the index we read from, above)
221 }
222 // No sign change; check for a near-root value at interval boundary
223 else if (RootIdx > 0 && CurStartValue <= NearRootTolerance)
224 {
226 }
227
230 }
232 }
233
234 }
235
236 // Copy out the final roots
237 for (int32 Idx = 0; Idx < NumFoundRoots; ++Idx)
238 {
239 Roots.Add(FoundRoots[Idx]);
240 }
241 return NumFoundRoots;
242 }
243};
244
245} // namespace UE::Math
#define checkSlow(expr)
Definition AssertionMacros.h:332
#define ensure( InExpression)
Definition AssertionMacros.h:464
FPlatformTypes::int32 int32
A 32-bit signed integer.
Definition Platform.h:1125
UE_FORCEINLINE_HINT TSharedRef< CastToType, Mode > StaticCastSharedRef(TSharedRef< CastFromType, Mode > const &InSharedRef)
Definition SharedPointer.h:127
#define UE_SMALL_NUMBER
Definition UnrealMathUtility.h:130
Definition ArrayView.h:139
Definition Array.h:670
Definition StaticArray.h:26
Definition Sphere.cpp:10
Definition PolynomialRootSolver.h:18
TArray< RealType, TInlineAllocator< PolynomialDegree > > Roots
Definition PolynomialRootSolver.h:22
int32 FindRootsInRange(TArrayView< const RealType > PolyCoeffs, RealType RangeStart, RealType RangeEnd, RealType Tolerance=(RealType) UE_SMALL_NUMBER, int32 MaxNewtonIterations=20, RealType NearRootTolerance=(RealType) UE_SMALL_NUMBER)
Definition PolynomialRootSolver.h:53
TPolynomialRootSolver(TArrayView< const RealType > PolyCoeffs, RealType RangeStart, RealType RangeEnd, RealType Tolerance=(RealType) UE_SMALL_NUMBER, int32 MaxNewtonIterations=20, RealType NearRootTolerance=(RealType) UE_SMALL_NUMBER)
Definition PolynomialRootSolver.h:36