On Tue, 20 Apr 2021 at 13:51, Gilles Sadowski <gillese...@gmail.com> wrote:

> Hi Alex.
>
> Le mar. 20 avr. 2021 à 11:17, Alex Herbert <alex.d.herb...@gmail.com> a
> écrit :
> >
> > [...]
>
> I'm a bit lost in all these bits... ;-)
>

I also had to remind myself of the work since it was a long time ago.


>
> > Any opinions on how changing LinearComination may affect Geometry? Either
> > we clean up the implementation to use the fast dot2s algorithm with
> correct
> > support for splitting 53-bit mantissas, or switch to the extended
> precision
> > version. But I do not think we should leave it as the current
> > implementation which has disadvantages against either of the
> alternatives.
>
> What is your suggestion?
>

Some background...

The dot2s is a specialisation of the DotK algorithm where the K represents
the k-fold increase in precision over a standard scalar product and denotes
qualitatively the level of robustness for a sum with massive cancellation
(e.g. 1e-100 + 1e200 - 2e-100 - 1e200 = -1e-100). Any floating-point sum is
limited by the representation of a 64-bit double as a binary floating point
number with only 53-bits of precision and an exponent.  When adding numbers
the result is computed and any extra bits that cannot fit into the mantissa
are lost. Whether this occurs is dependent on the input numbers, but in
general as the difference in magnitude becomes greater then the result will
contain progressively less of the true result due to the 53-bit mantissa.
If two numbers are different by more than 2^53 then adding them makes no
difference to the bigger number. In the example above a standard precision
result is 0. Only with extended precision can you hold the result of 1e-100
+ 1e200, etc.

In a sum of magnitude this only really matters when cancellation (opposite
signs) may occur. Take the recently discussed code to compute the 3D vector
length:

len = sqrt(x^2 + y^2 + z^2)

This uses a sum where the sign of the terms is the same. There will be no
cancellation and thus the answer will be close to the true result (within a
few ulps).

But if the sum of multiple values has different signs then the answer may
be in between the magnitudes of the terms. So the intermediate sum should
store knowledge of more bits of the intermediate result than the 64-bits of
a double.

The LinearCombination is actually providing two operations, multiplication
and addition:

sum_i {a_i * b_i}

The multiplication has a potential 106-bit mantissa for the result with a
single exponent. Any addition may require a longer mantissa with a single
exponent which is how BigDecimal would store the result. An extended
precision double number would represent the result as an array of two
doubles of very different magnitudes. Adding or multiplying the extended
precision result can increase the length of the result further. The use of
arrays of double to represent a number in extended precision is well
described in Shewchuk (1997): Arbitrary Precision Floating-Point Arithmetic
[1], on which a lot of the work in Numbers-142 is based.

There could be a case for a class to implement this generically:

public class ExtendedDouble {
  public static ExtendedDouble of(double a);
  public ExtendedDouble add(double a);
  public ExtendedDouble add(ExtendedDouble a);
  public ExtendedDouble multiply(double a);
  public ExtendedDouble multiply(ExtendedDouble a);
  public double toDouble();
}

That could be in another module in numbers. Shewchuk does not describe
division but it may be in references he provides. His paper is based upon
developing exact arithmetic for geometry applications so addition and
multiplication are all that are used.

As for LinearCombination then I would suggest that the use case is for any
dot product where cancellation may be expected. If handling cancellation is
more important than speed then using the most accurate method would seem to
be a better choice.

I do recall seeing LinearCombination used in the Geometry code inside a
loop with the result stored as a double on each iteration. This would
result in loss of the extended precision intermediate sum and so could
cause error due to cancellation. So there is a case for going through
Geometry to find use cases for LinearCombination and potentially find
places where changes should be made to compute all the terms in advance and
call LinearCombination once with the two input arrays.


> My impression is that [Geometry] emphasizes accuracy over ultimate
> speed (as opposed to libraries used for real-time rendering, I guess).
>
> However, could it be possible to leave this as a user's decision?
> Quoting from Matt's tutorial:
> ---CUT---
> Typically, users of Commons Geometry will construct a single instance
> of this type for use by multiple objects throughout an entire
> operation, or even application. Since we don't want our class to
> assume such a heavy responsibility, we will simply accept a
> DoublePrecisionContext in the constructor.
> ---CUT---
> Would it be conceivable that the choice of the implementation
> activated by a call to the "LinearCombination" facility is also
> encapsulated in the "DoublePrecisionContext"?
>

Numbers-142 did discuss the change of LinearCombination to an interface.
Then we already have several implementations for the interface where a user
can choose the balance between speed and robustness to cancellation.

It would mean reworking Geometry at all places that use LinearCombination
to use an instance rather than the current static method call. There would
be a default instance or a user supplied one.

I would not opt for using a singleton to define the behaviour of the static
method. This would work for a standalone application using Geometry but
would have issues for any application where different code requires a
different precision level.


[1]
http://www-2.cs.cmu.edu/afs/cs/project/quake/public/papers/robust-arithmetic.ps

Reply via email to