1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-29 07:13:02 +04:00

Merge pull request #28117 from satyam102006:fix/solveCubic-numerical-instability

core: fix solveCubic numerical instability via coefficient normalization (fixes #27748) #28117

 Summary
This PR fixes numerical instability in `cv::solveCubic` when the leading coefficient `a` is non-zero but extremely small relative to other coefficients (Issue #27748). 

It introduces a **normalization step** that scales all coefficients by their maximum magnitude before solving. This ensures robust detection of when the equation should degenerate to a quadratic solver, without breaking valid cubic equations that happen to have small coefficients (e.g., scaled by 1e-9).

The Problem (Issue #27748)
The previous implementation checked `if (a == 0)` to decide whether to use the cubic or quadratic formula. 
- When `a` is extremely small (e.g., 1e-17) but not exactly zero, and other coefficients are normal (e.g., 5.0), the standard cubic formula suffers from catastrophic cancellation and overflow, producing incorrect roots (e.g., 1e14).

 The Fix
1. Normalization: The solver now finds `max_coeff = max(|a|, |b|, |c|, |d|)` and scales all coefficients by `1.0 / max_coeff`.
2. Relative Threshold: It then checks `if (abs(a) < epsilon)` on the *normalized* coefficients. 

 Why this is better than previous attempts
In a previous attempt (PR #28057), a simple absolute check `abs(a) < epsilon` was proposed. That approach was rejected because it failed for scaled equations. 


Fixes #27748
This commit is contained in:
satyam yadav
2025-12-22 13:31:51 +05:30
committed by GitHub
parent 66bb0a8017
commit 74fbfc2343
2 changed files with 51 additions and 5 deletions
+27
View File
@@ -2618,6 +2618,33 @@ TEST(Core_SolveCubic, regression_27323)
}
}
TEST(Core_SolveCubic, regression_27748)
{
// a is extremely small relative to others (approx 1.8e-19 ratio),
// causing instability in standard cubic formula.
double a = 1.56041e-17;
double b = 84.4504;
double c = -96.795;
double d = 13.6826;
Mat coeffs = (Mat_<double>(1, 4) << a, b, c, d);
Mat roots;
int n = solveCubic(coeffs, roots);
// Expecting quadratic behavior (2 roots)
EXPECT_GE(n, 2);
// Verify roots satisfy the FULL cubic equation
for(int i = 0; i < n; i++)
{
double x = roots.at<double>(i);
double val = a*x*x*x + b*x*x + c*x + d;
// Check residual is small
EXPECT_LE(fabs(val), 1e-6) << "Root " << x << " does not satisfy the equation";
}
}
TEST(Core_SolvePoly, regression_5599)
{
// x^4 - x^2 = 0, roots: 1, -1, 0, 0