// Solve 0 = M * x + B. If M is 0, there are no solutions, unless B is also 0, // in which case there are infinite solutions, so we just return 1 of them. staticint solve_linear(constdouble M, constdouble B, double solution[2]) { if (sk_double_nearly_zero(M)) {
solution[0] = 0; if (sk_double_nearly_zero(B)) { return 1;
} return 0;
}
solution[0] = -B / M; if (!std::isfinite(solution[0])) { return 0;
} return 1;
}
// When B >> A, then the x^2 component doesn't contribute much to the output, so the second root // will be very large, but have massive round off error. Because of the round off error, the // second root will not evaluate to zero when substituted back into the quadratic equation. In // the situation when B >> A, then just treat the quadratic as a linear equation. staticbool close_to_linear(double A, double B) { if (A != 0) { // Return if B is much bigger than A. return std::abs(B / A) >= 1.0e+16;
}
// Otherwise A is zero, and the quadratic is linear. returntrue;
}
double SkQuads::Discriminant(constdouble a, constdouble b, constdouble c) { constdouble b2 = b * b; constdouble ac = a * c;
// Calculate the rough discriminate which may suffer from a loss in precision due to b2 and // ac being too close. constdouble roughDiscriminant = b2 - ac;
// We would like the calculated discriminant to have a relative error of 2-bits or less. For // doubles, this means the relative error is <= E = 3*2^-53. This gives a relative error // bounds of: // // |D - D~| / |D| <= E, // // where D = B*B - AC, and D~ is the floating point approximation of D. // Define the following equations // B2 = B*B, // B2~ = B2(1 + eB2), where eB2 is the floating point round off, // AC = A*C, // AC~ = AC(1 + eAC), where eAC is the floating point round off, and // D~ = B2~ - AC~. // We can now rewrite the above bounds as // // |B2 - AC - (B2~ - AC~)| / |B2 - AC| = |B2 - AC - B2~ + AC~| / |B2 - AC| <= E. // // Substituting B2~ and AC~, and canceling terms gives // // |eAC * AC - eB2 * B2| / |B2 - AC| <= max(|eAC|, |eBC|) * (|AC| + |B2|) / |B2 - AC|. // // We know that B2 is always positive, if AC is negative, then there is no cancellation // problem, and max(|eAC|, |eBC|) <= 2^-53, thus // // 2^-53 * (AC + B2) / |B2 - AC| <= 3 * 2^-53. Leading to // AC + B2 <= 3 * |B2 - AC|. // // If 3 * |B2 - AC| >= AC + B2 holds, then the roughDiscriminant has 2-bits of rounding error // or less and can be used. if (3 * std::abs(roughDiscriminant) >= b2 + ac) { return roughDiscriminant;
}
// Use the extra internal precision afforded by fma to calculate the rounding error for // b^2 and ac. constdouble b2RoundingError = std::fma(b, b, -b2); constdouble acRoundingError = std::fma(a, c, -ac);
// Add the total rounding error back into the discriminant guess. constdouble discriminant = (b2 - ac) + (b2RoundingError - acRoundingError); return discriminant;
}
SkQuads::RootResult SkQuads::Roots(double A, double B, double C) { constdouble discriminant = Discriminant(A, B, C);
if (A == 0) { double root; if (B == 0) { if (C == 0) {
root = std::numeric_limits<double>::infinity();
} else {
root = std::numeric_limits<double>::quiet_NaN();
}
} else { // Solve -2*B*x + C == 0; x = c/(2*b).
root = C / (2 * B);
} return {discriminant, root, root};
}
SkASSERT(A != 0); if (discriminant == 0) { return {discriminant, B / A, B / A};
}
if (discriminant > 0) { constdouble D = sqrt(discriminant); constdouble R = B > 0 ? B + D : B - D; return {discriminant, R / A, C / R};
}
// The discriminant is negative or is not finite. return {discriminant, NAN, NAN};
}
double SkQuads::EvalAt(double A, double B, double C, double t) { // Use fused-multiply-add to reduce the amount of round-off error between terms. return std::fma(std::fma(A, t, B), t, C);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.