Skip to content

Commit a53c23f

Browse files
authored
Merge pull request #414 from sahvx655-wq/bigint-nonfinite-bound
Handle non-finite bounds in BigIntegerValidator range checks
2 parents e066bd8 + 6891409 commit a53c23f

2 files changed

Lines changed: 46 additions & 4 deletions

File tree

src/main/java/org/apache/commons/validator/routines/BigIntegerValidator.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,8 @@ public boolean maxValue(final BigInteger value, final long max) {
152152
* <p>
153153
* This overrides the {@link Number} overload inherited from the superclass, which narrows the value to a {@code long} before comparing and so loses
154154
* magnitude for a {@code BigInteger} outside the long range. The operands are compared as {@code BigDecimal} so a non-integer bound keeps its fractional
155-
* part instead of being truncated towards zero.
155+
* part instead of being truncated towards zero. A non-finite {@link Double} or {@link Float} operand keeps the {@code doubleValue()} comparison, since
156+
* {@code BigDecimal} cannot represent {@code NaN} or an infinity.
156157
* </p>
157158
*
158159
* @param value The value validation is being performed on.
@@ -161,7 +162,7 @@ public boolean maxValue(final BigInteger value, final long max) {
161162
*/
162163
@Override
163164
public boolean maxValue(final Number value, final Number max) {
164-
return toBigDecimal(value).compareTo(toBigDecimal(max)) <= 0;
165+
return isFinite(value) && isFinite(max) ? compareTo(value, max) <= 0 : value.doubleValue() <= max.doubleValue();
165166
}
166167

167168
/**
@@ -181,7 +182,8 @@ public boolean minValue(final BigInteger value, final long min) {
181182
* <p>
182183
* This overrides the {@link Number} overload inherited from the superclass, which narrows the value to a {@code long} before comparing and so loses
183184
* magnitude for a {@code BigInteger} outside the long range. The operands are compared as {@code BigDecimal} so a non-integer bound keeps its fractional
184-
* part instead of being truncated towards zero.
185+
* part instead of being truncated towards zero. A non-finite {@link Double} or {@link Float} operand keeps the {@code doubleValue()} comparison, since
186+
* {@code BigDecimal} cannot represent {@code NaN} or an infinity.
185187
* </p>
186188
*
187189
* @param value The value validation is being performed on.
@@ -190,7 +192,7 @@ public boolean minValue(final BigInteger value, final long min) {
190192
*/
191193
@Override
192194
public boolean minValue(final Number value, final Number min) {
193-
return toBigDecimal(value).compareTo(toBigDecimal(min)) >= 0;
195+
return isFinite(value) && isFinite(min) ? compareTo(value, min) >= 0 : value.doubleValue() >= min.doubleValue();
194196
}
195197

196198
/**

src/test/java/org/apache/commons/validator/routines/BigIntegerValidatorTest.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,4 +238,44 @@ void testNumberRangeOutsideLongRange() {
238238
assertEquals(50L, wrapsIntoRange.longValue());
239239
assertFalse(instance.isInRange(wrapsIntoRange, min, max));
240240
}
241+
242+
/**
243+
* A non-finite {@link Double} bound must not be routed through {@link BigDecimal}, which cannot represent {@code NaN} or an infinity. The {@link Number}
244+
* overloads previously converted every bound to a {@code BigDecimal} and so threw {@code NumberFormatException} for such a bound, whereas the sibling
245+
* {@link BigDecimalValidator} already handled it. The behaviour now matches: a {@code NaN} bound is never satisfied, and an infinity is an open bound.
246+
*/
247+
@Test
248+
void testNumberRangeNonFiniteBound() {
249+
final AbstractNumberValidator instance = BigIntegerValidator.getInstance();
250+
final Number value = BigInteger.valueOf(100);
251+
// NaN bound: nothing compares against NaN
252+
assertFalse(instance.maxValue(value, Double.NaN));
253+
assertFalse(instance.minValue(value, Double.NaN));
254+
assertFalse(instance.isInRange(value, 0, Double.NaN));
255+
assertFalse(instance.isInRange(value, Double.NaN, 200));
256+
// POSITIVE_INFINITY as a maximum / NEGATIVE_INFINITY as a minimum are open bounds any finite value meets
257+
assertTrue(instance.maxValue(value, Double.POSITIVE_INFINITY));
258+
assertTrue(instance.minValue(value, Double.NEGATIVE_INFINITY));
259+
assertTrue(instance.isInRange(value, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY));
260+
// POSITIVE_INFINITY as a minimum / NEGATIVE_INFINITY as a maximum cannot be met
261+
assertFalse(instance.minValue(value, Double.POSITIVE_INFINITY));
262+
assertFalse(instance.maxValue(value, Double.NEGATIVE_INFINITY));
263+
final Number posInf = Double.valueOf(Double.POSITIVE_INFINITY);
264+
final Number negInf = Double.valueOf(Double.NEGATIVE_INFINITY);
265+
final Number nan = Double.valueOf(Double.NaN);
266+
// A non-finite value against itself: an infinity equals itself, NaN never compares equal
267+
assertTrue(instance.maxValue(posInf, posInf));
268+
assertTrue(instance.minValue(posInf, posInf));
269+
assertTrue(instance.maxValue(negInf, negInf));
270+
assertTrue(instance.minValue(negInf, negInf));
271+
assertFalse(instance.maxValue(nan, nan));
272+
assertFalse(instance.minValue(nan, nan));
273+
// A non-finite value against a different non-finite bound: -inf < +inf, anything with NaN fails
274+
assertTrue(instance.maxValue(negInf, posInf));
275+
assertTrue(instance.minValue(posInf, negInf));
276+
assertFalse(instance.maxValue(posInf, negInf));
277+
assertFalse(instance.minValue(negInf, posInf));
278+
assertFalse(instance.maxValue(posInf, nan));
279+
assertFalse(instance.minValue(posInf, nan));
280+
}
241281
}

0 commit comments

Comments
 (0)