This is an automated email from the ASF dual-hosted git repository.
asf-gitbox-commits pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-numbers.git
The following commit(s) were added to refs/heads/master by this push:
new 7d6f68ea NUMBERS-213: Restore signed zeros within the [from, to) range
7d6f68ea is described below
commit 7d6f68ea13253804c0a809f13b83a9ca0b84932c
Author: Alex Herbert <[email protected]>
AuthorDate: Fri Aug 21 09:33:28 2026 +0100
NUMBERS-213: Restore signed zeros within the [from, to) range
---
.../apache/commons/numbers/arrays/Selection.java | 15 ++++++----
.../commons/numbers/arrays/SelectionTest.java | 33 ++++++++++++++++++++++
src/changes/changes.xml | 5 ++++
3 files changed, 48 insertions(+), 5 deletions(-)
diff --git
a/commons-numbers-arrays/src/main/java/org/apache/commons/numbers/arrays/Selection.java
b/commons-numbers-arrays/src/main/java/org/apache/commons/numbers/arrays/Selection.java
index 45636ab0..d54fb888 100644
---
a/commons-numbers-arrays/src/main/java/org/apache/commons/numbers/arrays/Selection.java
+++
b/commons-numbers-arrays/src/main/java/org/apache/commons/numbers/arrays/Selection.java
@@ -242,8 +242,10 @@ public final class Selection {
// Restore signed zeros
if (cn != 0) {
- // Use partition index below zero to fast-forward to zero as much
as possible
- for (int j = a[k] < 0 ? k : -1;;) {
+ // Use partition index below zero to fast-forward to zero as much
as possible.
+ // The scan must start within the range to avoid modifying data
+ // outside [fromIndex, toIndex).
+ for (int j = a[k] < 0 ? k : fromIndex - 1;;) {
if (a[++j] == 0) {
a[j] = -0.0;
if (--cn == 0) {
@@ -311,8 +313,10 @@ public final class Selection {
// Restore signed zeros
if (cn != 0) {
- // Use partition indices below zero to fast-forward to zero as
much as possible
- int j = -1;
+ // Use partition indices below zero to fast-forward to zero as
much as possible.
+ // The scan must start within the range to avoid modifying data
+ // outside [fromIndex, toIndex).
+ int j = fromIndex - 1;
if (n < 0) {
// Binary search on -n sorted indices: hi = (-n) - 1
int lo = 0;
@@ -320,7 +324,8 @@ public final class Selection {
while (lo <= hi) {
final int mid = (lo + hi) >>> 1;
if (a[k[mid]] < 0) {
- j = mid;
+ // Track the index into the data, not the index into k
+ j = k[mid];
lo = mid + 1;
} else {
hi = mid - 1;
diff --git
a/commons-numbers-arrays/src/test/java/org/apache/commons/numbers/arrays/SelectionTest.java
b/commons-numbers-arrays/src/test/java/org/apache/commons/numbers/arrays/SelectionTest.java
index b4801b6f..f1524816 100644
---
a/commons-numbers-arrays/src/test/java/org/apache/commons/numbers/arrays/SelectionTest.java
+++
b/commons-numbers-arrays/src/test/java/org/apache/commons/numbers/arrays/SelectionTest.java
@@ -562,6 +562,39 @@ class SelectionTest {
}, false);
}
+ /**
+ * Test that selection on a sub-range does not modify data outside the
range
+ * {@code [fromIndex, toIndex)} when restoring signed zeros, and restores
the
+ * negative zeros inside the range.
+ */
+ @Test
+ void testDoubleSelectRangeSignedZerosOutsideRangeUntouched() {
+ // A zero below the range must not be rewritten by the restoration scan
+ // when the range contains no negative values to fast-forward from.
+ final double[] a = {0.0, 0.0, -0.0};
+ Selection.select(a, 1, 3, 2);
+ Assertions.assertArrayEquals(new double[] {0.0, -0.0, 0.0}, a, "single
k");
+ final double[] b = {0.0, 0.0, -0.0};
+ Selection.select(b, 1, 3, new int[] {1, 2});
+ Assertions.assertArrayEquals(new double[] {0.0, -0.0, 0.0}, b,
"multiple k");
+ }
+
+ /**
+ * Test that selection on a sub-range does not modify data outside the
range
+ * {@code [fromIndex, toIndex)} when restoring signed zeros using the
+ * fast-forward from sorted partition indices with values below zero.
+ */
+ @Test
+ void testDoubleSelectRangeSignedZerosFastForwardWithinRange() {
+ // Negative values inside the range: the fast-forward must use the
+ // partition index into the data, not the index into k, otherwise the
+ // restoration scan may start below fromIndex.
+ final double[] a = {0.0, 0.0, 0.0, 0.0, 7.0, -1.0, -0.0, 8.0, -3.0,
-2.0};
+ Selection.select(a, 4, 10, new int[] {4, 5, 6, 7, 8, 9});
+ Assertions.assertArrayEquals(
+ new double[] {0.0, 0.0, 0.0, 0.0, -3.0, -2.0, -1.0, -0.0, 7.0,
8.0}, a);
+ }
+
static void assertPartition(double[] values, int[] indices,
DoublePartitionFunction function,
boolean sortedRange) {
final double[] data = values.clone();
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 2cf87881..b4da3c11 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -56,6 +56,11 @@ If the output is not quite correct, check for invisible
trailing spaces!
<release version="1.4" date="TBD" description="
New features, updates and bug fixes.
">
+ <action dev="aherbert" type="fix" due-to="Security scan, Alex Herbert"
issue="NUMBERS-213">
+ "Selection": Fix silent modification of the sign of zeros outside the
+ [fromIndex, toIndex) range due to incorrectly restoring signs from the
start
+ of the array.
+ </action>
<action dev="aherbert" type="fix" due-to="Security scan, Alex Herbert"
issue="NUMBERS-212">
"MultidimensionalCounter": Any product of strictly positive dimension
sizes
equal or above 2^31 is rejected.