Commit 3d5cf9ac authored by Luc Maisonobe's avatar Luc Maisonobe

Fixed inconsistency in AbsoluteDate/AbsoluteDate conversions.

Fixes #508
parent fd01a3b0
......@@ -327,27 +327,7 @@ public class FieldAbsoluteDate<T extends RealFieldElement<T>>
* instant, as measured in a regular time scale
*/
public FieldAbsoluteDate(final FieldAbsoluteDate<T> since, final double elapsedDuration) {
this.field = since.field;
final T sum = since.offset.add(elapsedDuration);
if (Double.isInfinite(sum.getReal())) {
offset = sum;
epoch = (sum.getReal() < 0) ? Long.MIN_VALUE : Long.MAX_VALUE;
} else {
// compute sum exactly, using Møller-Knuth TwoSum algorithm without branching
// the following statements must NOT be simplified, they rely on floating point
// arithmetic properties (rounding and representable numbers)
// at the end, the EXACT result of addition since.offset + elapsedDuration
// is sum + residual, where sum is the closest representable number to the exact
// result and residual is the missing part that does not fit in the first number
final double oPrime = sum.getReal() - elapsedDuration;
final double dPrime = sum.getReal() - oPrime;
final double deltaO = since.offset.getReal() - oPrime;
final double deltaD = elapsedDuration - dPrime;
final double residual = deltaO + deltaD;
final long dl = (long) FastMath.floor(sum.getReal());
offset = sum.subtract(dl).add(residual);
epoch = since.epoch + dl;
}
this(since.epoch, elapsedDuration, since.offset);
}
......@@ -359,12 +339,7 @@ public class FieldAbsoluteDate<T extends RealFieldElement<T>>
* instant, as measured in a regular time scale
*/
public FieldAbsoluteDate(final AbsoluteDate since, final T elapsedDuration) {
this.field = elapsedDuration.getField();
final double dT = since.durationFrom(AbsoluteDate.J2000_EPOCH);
final T deltaT = elapsedDuration.add(dT);
final FieldAbsoluteDate<T> j2000 = getJ2000Epoch(elapsedDuration.getField()).shiftedBy(deltaT);
offset = j2000.offset;
epoch = j2000.epoch;
this(since.getEpoch(), since.getOffset(), elapsedDuration);
}
/** Build an instance from an apparent clock offset with respect to another
......@@ -389,6 +364,36 @@ public class FieldAbsoluteDate<T extends RealFieldElement<T>>
timeScale);
}
/** Build an instance from mixed double and field raw components.
* @param epoch reference epoch in seconds from 2000-01-01T12:00:00 TAI
* @param tA double part of offset since reference epoch
* @param tB field part of offset since reference epoch
* @since 9.3
*/
private FieldAbsoluteDate(final long epoch, final double tA, final T tB) {
this.field = tB.getField();
final T sum = tB.add(tA);
if (Double.isInfinite(sum.getReal())) {
this.offset = sum;
this.epoch = (sum.getReal() < 0) ? Long.MIN_VALUE : Long.MAX_VALUE;
} else {
// compute sum exactly, using Møller-Knuth TwoSum algorithm without branching
// the following statements must NOT be simplified, they rely on floating point
// arithmetic properties (rounding and representable numbers)
// at the end, the EXACT result of addition tA + tB
// is sum + residual, where sum is the closest representable number to the exact
// result and residual is the missing part that does not fit in the first number
final double oPrime = sum.getReal() - tA;
final double dPrime = sum.getReal() - oPrime;
final double deltaO = tB.getReal() - oPrime;
final double deltaD = tA - dPrime;
final double residual = deltaO + deltaD;
final long dl = (long) FastMath.floor(sum.getReal());
this.offset = sum.subtract(dl).add(residual);
this.epoch = epoch + dl;
}
}
/** Build an instance from a CCSDS Unsegmented Time Code (CUC).
* <p>
* CCSDS Unsegmented Time Code is defined in the blue book:
......
......@@ -21,6 +21,9 @@
</properties>
<body>
<release version="TBD" date="TBD" description="TBD">
<action dev="luc" type="fix" issue="508">
Fixed inconsistency leading to inaccuracies in conversions from AbsoluteDate to FieldAbsoluteDate.
</action>
<action dev="pascal" type="fix" issue="495">
The MarshallSolarActivityFutureEstimation class implements
the NRLMSISE00InputParameters interface.
......
......@@ -250,6 +250,11 @@ public class FieldAbsoluteDateTest {
doTestWrapAtMinuteEnd(Decimal64Field.getInstance());
}
@Test
public void testIssue508() {
doTestIssue508(Decimal64Field.getInstance());
}
private <T extends RealFieldElement<T>> void doTestStandardEpoch(final Field<T> field) {
TimeScale tai = TimeScalesFactory.getTAI();
......@@ -966,4 +971,11 @@ public class FieldAbsoluteDateTest {
Assert.assertEquals("2008-03-01T00:00:00.000", stillBeforeMidnight.toString(utc));
}
private <T extends RealFieldElement<T>> void doTestIssue508(final Field<T> field) {
AbsoluteDate date = new AbsoluteDate(2000, 2, 24, 17, 5, 30.047, TimeScalesFactory.getUTC());
FieldAbsoluteDate<T> tA = new FieldAbsoluteDate<>(field, date);
FieldAbsoluteDate<T> tB = new FieldAbsoluteDate<>(date, field.getZero());
Assert.assertEquals(0.0, tA.durationFrom(tB).getReal(), Precision.SAFE_MIN);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment