diff --git a/src/main/java/org/apache/datasketches/tdigest/TDigestDouble.java b/src/main/java/org/apache/datasketches/tdigest/TDigestDouble.java index 680482781..4cd4169e0 100644 --- a/src/main/java/org/apache/datasketches/tdigest/TDigestDouble.java +++ b/src/main/java/org/apache/datasketches/tdigest/TDigestDouble.java @@ -117,6 +117,9 @@ public void update(final double value) { */ public void merge(final TDigestDouble other) { if (other.isEmpty()) { return; } + // Extreme centroid means are not the true min/max when those centroids have weight > 1. + minValue_ = Math.min(minValue_, other.minValue_); + maxValue_ = Math.max(maxValue_, other.maxValue_); Math.addExact(getTotalWeight(), other.getTotalWeight()); final int num = numCentroids_ + numBuffered_ + other.numCentroids_ + other.numBuffered_; final double[] values = new double[num]; diff --git a/src/test/java/org/apache/datasketches/tdigest/TDigestDoubleTest.java b/src/test/java/org/apache/datasketches/tdigest/TDigestDoubleTest.java index 05dd73806..7d1d5937a 100644 --- a/src/test/java/org/apache/datasketches/tdigest/TDigestDoubleTest.java +++ b/src/test/java/org/apache/datasketches/tdigest/TDigestDoubleTest.java @@ -124,6 +124,33 @@ public void mergeSmall() { assertEquals(td1.getMaxValue(), 3); } + @Test + public void mergePreservesDeserializedMinMaxWithWeightedTails() { + final TDigestDouble source = new TDigestDouble(); + source.update(0); + source.update(50); + source.update(90); + final byte[] bytes = source.toByteArray(); + // serialized layout: preamble 16 bytes, min 8 bytes, max 8 bytes + MemorySegment.ofArray(bytes).set(ValueLayout.JAVA_DOUBLE_UNALIGNED, 16, -1.0); + MemorySegment.ofArray(bytes).set(ValueLayout.JAVA_DOUBLE_UNALIGNED, 24, 100.0); + final TDigestDouble other = TDigestDouble.heapify(MemorySegment.ofArray(bytes)); + assertEquals(other.getMinValue(), -1.0); + assertEquals(other.getMaxValue(), 100.0); + + final TDigestDouble empty = new TDigestDouble(); + empty.merge(other); + assertEquals(empty.getMinValue(), -1.0); + assertEquals(empty.getMaxValue(), 100.0); + + final TDigestDouble left = new TDigestDouble(); + left.update(10); + left.update(20); + left.merge(other); + assertEquals(left.getMinValue(), -1.0); + assertEquals(left.getMaxValue(), 100.0); + } + @Test public void mergeLarge() { final int n = 10000;