From 4a7923add5be874cc352faf9afd0f627794de32e Mon Sep 17 00:00:00 2001 From: Tuukka Lehtonen Date: Thu, 30 Apr 2020 00:55:09 +0300 Subject: [PATCH] Fixed CSVFormatter mind floating point inaccuracy when resampling These changes prevent the resampling export mode from dropping samples that have timestamps closer than 1e-13 sec to the last exported sample time stamp. gitlab #529 --- .../simantics/history/csv/CSVFormatter.java | 49 +++++++++++++++++-- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/bundles/org.simantics.history/src/org/simantics/history/csv/CSVFormatter.java b/bundles/org.simantics.history/src/org/simantics/history/csv/CSVFormatter.java index 6cc7a2a6f..b04e0dd37 100644 --- a/bundles/org.simantics.history/src/org/simantics/history/csv/CSVFormatter.java +++ b/bundles/org.simantics.history/src/org/simantics/history/csv/CSVFormatter.java @@ -38,6 +38,23 @@ import org.simantics.history.util.ValueBand; */ public class CSVFormatter { + /** + * This is the tolerance used to decide whether or not the last data point of + * the exported items is included in the exported material or not. If + * 0 <= (t - t(lastDataPoint) < {@value #RESAMPLING_END_TIMESTAMP_INCLUSION_TOLERANCE} + * is true, then the last exported data point will be + * lastDataPoint, with timestamp t(lastDataPoint) even + * if t > t(lastDataPoint). + * + *

+ * This works around problems where floating point inaccuracy causes a data + * point to be left out from the the export when it would be fair for the user + * to expect the data to be exported would contain a point with time stamp + * 9.999999999999996 when sampling with time-step 1.0 + * starting from time 0.0. + */ + private static final double RESAMPLING_END_TIMESTAMP_INCLUSION_TOLERANCE = 1e-13; + List items = new ArrayList(); double from = -Double.MAX_VALUE; double end = Double.MAX_VALUE; @@ -329,6 +346,13 @@ public class CSVFormatter { BigDecimal bigTime = new BigDecimal(String.valueOf(time)); BigDecimal bigTimeStep = new BigDecimal(String.valueOf(timeStep)); + // Loop kill-switch for the case where timeStep > 0 + boolean breakAfterNextWrite = false; + +// System.out.println("time: " + time); +// System.out.println("timeStep: " + timeStep); +// System.out.println("_end: " + Double.toString(_end)); + for (Item i : items) i.iter.gotoTime(time); do { if ( monitor!=null && monitor.isCanceled() ) return; @@ -394,10 +418,26 @@ public class CSVFormatter { sb.append( lineFeed ); - // Read next values, and the following times - if ( timeStep>0.0 ) { - bigTime = bigTime.add(bigTimeStep); - time = bigTime.doubleValue(); + if (breakAfterNextWrite) + break; + + // Read next values, and the following times + if ( timeStep>0.0 ) { + bigTime = bigTime.add(bigTimeStep); + time = bigTime.doubleValue(); + + // gitlab #529: prevent last data point from getting dropped + // due to small imprecisions in re-sampling mode. + double diff = time - _end; + if (diff > 0 && diff <= RESAMPLING_END_TIMESTAMP_INCLUSION_TOLERANCE) { + time = _end; + breakAfterNextWrite = true; + // Take floating point inaccuracy into account when re-sampling + // to prevent the last data point from being left out if there + // is small-enough imprecision in the last data point time stamp + // to be considered negligible compared to expected stepped time. + } + } else { // Get smallest end time that is larger than current time Double nextTime = null; @@ -420,6 +460,7 @@ public class CSVFormatter { if(contains(i, time)) hasMore = true; } + //System.out.println("hasMore @ " + time + " (" + bigTime + ") = " + hasMore); if(!hasMore) break; } while (time<=_end); -- 2.43.2