Flutter Engine
The Flutter Engine
Classes | Static Public Member Functions | List of all members
SkQuads Class Reference

#include <SkQuads.h>

Classes

struct  RootResult
 

Static Public Member Functions

static double Discriminant (double A, double B, double C)
 
static RootResult Roots (double A, double B, double C)
 
static int RootsReal (double A, double B, double C, double solution[2])
 
static double EvalAt (double A, double B, double C, double t)
 

Detailed Description

Utilities for dealing with quadratic formulas with one variable: f(t) = A*t^2 + B*t + C

Definition at line 15 of file SkQuads.h.

Member Function Documentation

◆ Discriminant()

double SkQuads::Discriminant ( double  A,
double  B,
double  C 
)
static

Calculate a very accurate discriminant. Given A*t^2 -2*B*t + C = 0, calculate B^2 - AC accurate to 2 bits. Note the form of the quadratic is slightly different from the normal formulation.

The method used to calculate the discriminant is from "On the Cost of Floating-Point Computation Without Extra-Precise Arithmetic" by W. Kahan.

Definition at line 47 of file SkQuads.cpp.

47 {
48 const double b2 = b * b;
49 const double ac = a * c;
50
51 // Calculate the rough discriminate which may suffer from a loss in precision due to b2 and
52 // ac being too close.
53 const double roughDiscriminant = b2 - ac;
54
55 // We would like the calculated discriminant to have a relative error of 2-bits or less. For
56 // doubles, this means the relative error is <= E = 3*2^-53. This gives a relative error
57 // bounds of:
58 //
59 // |D - D~| / |D| <= E,
60 //
61 // where D = B*B - AC, and D~ is the floating point approximation of D.
62 // Define the following equations
63 // B2 = B*B,
64 // B2~ = B2(1 + eB2), where eB2 is the floating point round off,
65 // AC = A*C,
66 // AC~ = AC(1 + eAC), where eAC is the floating point round off, and
67 // D~ = B2~ - AC~.
68 // We can now rewrite the above bounds as
69 //
70 // |B2 - AC - (B2~ - AC~)| / |B2 - AC| = |B2 - AC - B2~ + AC~| / |B2 - AC| <= E.
71 //
72 // Substituting B2~ and AC~, and canceling terms gives
73 //
74 // |eAC * AC - eB2 * B2| / |B2 - AC| <= max(|eAC|, |eBC|) * (|AC| + |B2|) / |B2 - AC|.
75 //
76 // We know that B2 is always positive, if AC is negative, then there is no cancellation
77 // problem, and max(|eAC|, |eBC|) <= 2^-53, thus
78 //
79 // 2^-53 * (AC + B2) / |B2 - AC| <= 3 * 2^-53. Leading to
80 // AC + B2 <= 3 * |B2 - AC|.
81 //
82 // If 3 * |B2 - AC| >= AC + B2 holds, then the roughDiscriminant has 2-bits of rounding error
83 // or less and can be used.
84 if (3 * std::abs(roughDiscriminant) >= b2 + ac) {
85 return roughDiscriminant;
86 }
87
88 // Use the extra internal precision afforded by fma to calculate the rounding error for
89 // b^2 and ac.
90 const double b2RoundingError = std::fma(b, b, -b2);
91 const double acRoundingError = std::fma(a, c, -ac);
92
93 // Add the total rounding error back into the discriminant guess.
94 const double discriminant = (b2 - ac) + (b2RoundingError - acRoundingError);
95 return discriminant;
96}
static skvx::float4 fma(const skvx::float4 &f, float m, const skvx::float4 &a)
Definition: SkGeometry.cpp:597
static bool b
struct MyStruct a[10]
SIN Vec< N, float > abs(const Vec< N, float > &x)
Definition: SkVx.h:707

◆ EvalAt()

double SkQuads::EvalAt ( double  A,
double  B,
double  C,
double  t 
)
static

Evaluates the quadratic function with the 3 provided coefficients and the provided variable.

Definition at line 164 of file SkQuads.cpp.

164 {
165 // Use fused-multiply-add to reduce the amount of round-off error between terms.
166 return std::fma(std::fma(A, t, B), t, C);
167}

◆ Roots()

SkQuads::RootResult SkQuads::Roots ( double  A,
double  B,
double  C 
)
static

Calculate the roots of a quadratic. Given A*t^2 -2*B*t + C = 0, calculate the roots.

This does not try to detect a linear configuration of the equation, or detect if the two roots are the same. It returns the discriminant and the two roots.

Not this uses a different form the quadratic equation to reduce rounding error. Give standard A, B, C. You can call this root finder with: Roots(A, -0.5*B, C) to find the roots of A*x^2 + B*x + C.

The method used to calculate the roots is from "On the Cost of Floating-Point Computation Without Extra-Precise Arithmetic" by W. Kahan.

If the roots are imaginary then nan is returned. If the roots can't be represented as double then inf is returned.

Definition at line 98 of file SkQuads.cpp.

98 {
99 const double discriminant = Discriminant(A, B, C);
100
101 if (A == 0) {
102 double root;
103 if (B == 0) {
104 if (C == 0) {
105 root = std::numeric_limits<double>::infinity();
106 } else {
107 root = std::numeric_limits<double>::quiet_NaN();
108 }
109 } else {
110 // Solve -2*B*x + C == 0; x = c/(2*b).
111 root = C / (2 * B);
112 }
113 return {discriminant, root, root};
114 }
115
116 SkASSERT(A != 0);
117 if (discriminant == 0) {
118 return {discriminant, B / A, B / A};
119 }
120
121 if (discriminant > 0) {
122 const double D = sqrt(discriminant);
123 const double R = B > 0 ? B + D : B - D;
124 return {discriminant, R / A, C / R};
125 }
126
127 // The discriminant is negative or is not finite.
128 return {discriminant, NAN, NAN};
129}
#define SkASSERT(cond)
Definition: SkAssert.h:116
static double Discriminant(double A, double B, double C)
Definition: SkQuads.cpp:47
#define R(r)
#define B
string root
Definition: scale_cpu.py:20
SIN Vec< N, float > sqrt(const Vec< N, float > &x)
Definition: SkVx.h:706

◆ RootsReal()

int SkQuads::RootsReal ( double  A,
double  B,
double  C,
double  solution[2] 
)
static

Puts up to 2 real solutions to the equation A*t^2 + B*t + C = 0 in the provided array.

Definition at line 135 of file SkQuads.cpp.

135 {
136
137 if (close_to_linear(A, B)) {
138 return solve_linear(B, C, solution);
139 }
140
141 SkASSERT(A != 0);
142 auto [discriminant, root0, root1] = Roots(A, -0.5 * B, C);
143
144 // Handle invariants to mesh with existing code from here on.
145 if (!std::isfinite(discriminant) || discriminant < 0) {
146 return 0;
147 }
148
149 int roots = 0;
150 if (const double r0 = zero_if_tiny(root0); std::isfinite(r0)) {
151 solution[roots++] = r0;
152 }
153 if (const double r1 = zero_if_tiny(root1); std::isfinite(r1)) {
154 solution[roots++] = r1;
155 }
156
157 if (roots == 2 && sk_doubles_nearly_equal_ulps(solution[0], solution[1])) {
158 roots = 1;
159 }
160
161 return roots;
162}
bool sk_doubles_nearly_equal_ulps(double a, double b, uint8_t maxUlpsDiff=16)
static bool close_to_linear(double A, double B)
Definition: SkQuads.cpp:37
static int solve_linear(const double M, const double B, double solution[2])
Definition: SkQuads.cpp:18
static double zero_if_tiny(double x)
Definition: SkQuads.cpp:131
static RootResult Roots(double A, double B, double C)
Definition: SkQuads.cpp:98
SINT bool isfinite(const Vec< N, T > &v)
Definition: SkVx.h:1003

The documentation for this class was generated from the following files: