[OpenJDK 2D-Dev] X11 uniform scaled wide lines and dashed lines; STROKE_CONTROL in Pisces
Jim Graham
james.graham at oracle.com
Wed Jan 5 02:56:35 UTC 2011
Hi Denis,
What about logic like this:
boolean checkRoots = false;
if (D < 0) {
// 3 solution form is possible, so use it
checkRoots = (D > -TINY); // Check them if we were borderline
// compute 3 roots as before
} else {
double u = ...;
double v = ...;
res[0] = u+v; // should be 2*u if D is near zero
if (u close to v) { // Will be true for D near zero
res[1] = -res[0]/2; // should be -u if D is near zero
checkRoots = true; // Check them if we were borderline
// Note that q=0 case ends up here as well...
}
}
if (checkRoots) {
if (num > 2 && (res[2] == res[1] || res[2] == res[0]) {
num--;
}
if (num > 1 && res[1] == res[0]) {
res[1] = res[--num]; // Copies res[2] to res[1] if needed
}
for (int i = num-1; i >= 0; i--) {
res[i] = refine(res[i]);
for (int j = i+1; j < num; j++) {
if (res[i] == res[j]) {
res[i] = res[--num];
break;
}
}
}
}
Note that we lose the optimization of calculating just 2*u and -u for
the 2 root case, but that only happened in rare circumstances. Also, if
D is near zero and negative, then we generate 3 roots using
transcendentals and potentially refine one away, but that should also be
an uncommon situation and "there but for the grace of being a tiny
negative number would we have gone anyway" so I think it is OK to take
the long way to the answer.
Also, one could argue that if we used the transcendentals to calculate
the 3 roots, it couldn't hurt to refine the answers anyway. The other
solutions should have higher precision, but the transcendental results
will be much less accurate.
Finally, this lacks the "refine them anyway if any of them are near 0 or
1" rule - the original only did that if the transcendentals were used,
but it would be nice to do that for any of the cases. It might make
sense to have a variant that takes a boolean indicating whether to
ensure higher accuracy around 0 and 1, but that would require an API
change request...
...jim
On 1/4/11 2:02 PM, Denis Lila wrote:
> Hi Jim.
>
>> The test as it is has a test case (I just chose random numbers to
>> check
>> and got lucky - d'oh!) that generates 1 solution from the new code
>> even
>> though the equation had 2 distinct solutions that weren't even near
>> each
>> other...
>
> I figured out why this happens. It's because of cancellation in the
> computation of D (two large numbers are subtracted and the result is
> supposed to be 0 or close to 0, but it's about 1e-7, which wasn't
> enough to pass the iszero test). I've been working on this and I
> came up with a couple of different ways. They are in the attached
> file (it's a modified version of the file your CubicSolve.java).
>
> The first thing I did was to modify solveCubicOld. I tried to get
> a bit fancy and although I think I fixed the problems it had, the
> end result is ugly, complicated and it has small problems, like
> returning 3 very close roots when there should only be one.
>
> The other solution is to just check if the roots of the derivative
> are also roots of the cubic polynomial if only 1 root was computed
> by the closed form algorithm. This doesn't have the numerical
> accuracy of the first way (which used bisectRoots when things went wrong)
> but it's much faster, doesn't have the multiple roots problem, and it's
> much simpler. I called your trySolve function on a few hundred
> polynomials with random roots in [-10, 10] and it never finds fewer
> roots than there actually are. Sometimes it finds 3 roots when there are
> only 2, but I don't think this is a huge problem.
>
> I've attached what I have so far.
>
> Regards,
> Denis.
>
> ----- Original Message -----
>> Hi Denis,
>>
>> I'm attaching a test program I wrote that compares the old and new
>> algorithms.
>>
>> Obviously the old one missed a bunch of solutions because it
>> classified
>> all solutions as 1 or 3, but the new one also sometimes misses a
>> solution. You might want to turn this into an automated test for the
>> bug (and maybe use it as a stress test with a random number
>> generator).
>>
>> I think one problem might be that you use "is close to zero" to check
>> if
>> you should use special processing. I think any tests which say "do it
>> this way and get fewer roots" should be conservative and if we are on
>> the borderline and we can do the code that generates more solutions
>> then
>> we should generate more and them maybe refine the roots and eliminate
>> duplicates. That way we can be (more) sure not to leave any roots
>> unsolved.
>>
>
>>
>> ...jim
More information about the 2d-dev
mailing list