Skip to content

Commit

Permalink
Fix float multiple_of validation for negative numbers (#1373)
Browse files Browse the repository at this point in the history
  • Loading branch information
K-dash authored Feb 5, 2025
1 parent e89bd8c commit ed15f6c
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 23 deletions.
7 changes: 4 additions & 3 deletions src/validators/float.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,10 @@ impl Validator for ConstrainedFloatValidator {
return Err(ValError::new(ErrorTypeDefaults::FiniteNumber, input));
}
if let Some(multiple_of) = self.multiple_of {
let rem = float % multiple_of;
let threshold = float.abs() / 1e9;
if rem.abs() > threshold && (rem - multiple_of).abs() > threshold {
let tolerance = 1e-9;
let rounded_div = (float / multiple_of).round();
let diff = (float - (rounded_div * multiple_of)).abs();
if diff > tolerance {
return Err(ValError::new(
ErrorType::MultipleOf {
multiple_of: multiple_of.into(),
Expand Down
40 changes: 30 additions & 10 deletions tests/validators/test_decimal.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,24 +171,44 @@ def test_decimal_kwargs(py_and_json: PyAndJson, kwargs: dict[str, Any], input_va
@pytest.mark.parametrize(
'multiple_of,input_value,error',
[
(0.5, 0.5, None),
(0.5, 1, None),
# Test cases for multiples of 0.5
*[(0.5, round(i * 0.5, 1), None) for i in range(-4, 5)],
(0.5, 0.49, Err('Input should be a multiple of 0.5')),
(0.5, 0.6, Err('Input should be a multiple of 0.5')),
(0.5, 0.51, Err('Input should be a multiple of 0.5')),
(0.5, -0.75, Err('Input should be a multiple of 0.5')),
(0.5, 0.501, Err('Input should be a multiple of 0.5')),
(0.5, 1_000_000.5, None),
(0.5, 1_000_000.49, Err('Input should be a multiple of 0.5')),
(0.5, int(5e10), None),
# Test cases for multiples of 0.1
*[(0.1, round(i * 0.1, 1), None) for i in range(-10, 11)],
(0.1, 0, None),
(0.1, 0.0, None),
(0.1, 0.2, None),
(0.1, 0.3, None),
(0.1, 0.4, None),
(0.1, 0.5, None),
(0.1, 0.5001, Err('Input should be a multiple of 0.1')),
(0.1, 0.05, Err('Input should be a multiple of 0.1')),
(0.1, -0.15, Err('Input should be a multiple of 0.1')),
(0.1, 1_000_000.1, None),
(0.1, 1_000_000.05, Err('Input should be a multiple of 0.1')),
(0.1, 1, None),
(0.1, 1.0, None),
(0.1, int(5e10), None),
(2.0, -2.0, None),
# Test cases for multiples of 2.0
*[(2.0, i * 2.0, None) for i in range(-5, 6)],
(2.0, -2.1, Err('Input should be a multiple of 2')),
(2.0, -3.0, Err('Input should be a multiple of 2')),
(2.0, 1_000_002.0, None),
(2.0, 1_000_001.0, Err('Input should be a multiple of 2')),
(2.0, int(5e10), None),
# Test cases for multiples of 0.01
*[(0.01, round(i * 0.01, 2), None) for i in range(-10, 11)],
(0.01, 0.005, Err('Input should be a multiple of 0.01')),
(0.01, -0.015, Err('Input should be a multiple of 0.01')),
(0.01, 1_000_000.01, None),
(0.01, 1_000_000.005, Err('Input should be a multiple of 0.01')),
(0.01, int(5e10), None),
# Test cases for values very close to zero
(0.1, 0.00001, Err('Input should be a multiple of 0.1')),
(0.1, -0.00001, Err('Input should be a multiple of 0.1')),
(0.01, 0.00001, Err('Input should be a multiple of 0.01')),
(0.01, -0.00001, Err('Input should be a multiple of 0.01')),
],
ids=repr,
)
Expand Down
40 changes: 30 additions & 10 deletions tests/validators/test_float.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,24 +105,44 @@ def test_float_kwargs(py_and_json: PyAndJson, kwargs: dict[str, Any], input_valu
@pytest.mark.parametrize(
'multiple_of,input_value,error',
[
(0.5, 0.5, None),
(0.5, 1, None),
# Test cases for multiples of 0.5
*[(0.5, round(i * 0.5, 1), None) for i in range(-4, 5)],
(0.5, 0.49, Err('Input should be a multiple of 0.5')),
(0.5, 0.6, Err('Input should be a multiple of 0.5')),
(0.5, 0.51, Err('Input should be a multiple of 0.5')),
(0.5, -0.75, Err('Input should be a multiple of 0.5')),
(0.5, 0.501, Err('Input should be a multiple of 0.5')),
(0.5, 1_000_000.5, None),
(0.5, 1_000_000.49, Err('Input should be a multiple of 0.5')),
(0.5, int(5e10), None),
# Test cases for multiples of 0.1
*[(0.1, round(i * 0.1, 1), None) for i in range(-10, 11)],
(0.1, 0, None),
(0.1, 0.0, None),
(0.1, 0.2, None),
(0.1, 0.3, None),
(0.1, 0.4, None),
(0.1, 0.5, None),
(0.1, 0.5001, Err('Input should be a multiple of 0.1')),
(0.1, 0.05, Err('Input should be a multiple of 0.1')),
(0.1, -0.15, Err('Input should be a multiple of 0.1')),
(0.1, 1_000_000.1, None),
(0.1, 1_000_000.05, Err('Input should be a multiple of 0.1')),
(0.1, 1, None),
(0.1, 1.0, None),
(0.1, int(5e10), None),
(2.0, -2.0, None),
# Test cases for multiples of 2.0
*[(2.0, i * 2.0, None) for i in range(-5, 6)],
(2.0, -2.1, Err('Input should be a multiple of 2')),
(2.0, -3.0, Err('Input should be a multiple of 2')),
(2.0, 1_000_002.0, None),
(2.0, 1_000_001.0, Err('Input should be a multiple of 2')),
(2.0, int(5e10), None),
# Test cases for multiples of 0.01
*[(0.01, round(i * 0.01, 2), None) for i in range(-10, 11)],
(0.01, 0.005, Err('Input should be a multiple of 0.01')),
(0.01, -0.015, Err('Input should be a multiple of 0.01')),
(0.01, 1_000_000.01, None),
(0.01, 1_000_000.005, Err('Input should be a multiple of 0.01')),
(0.01, int(5e10), None),
# Test cases for values very close to zero
(0.1, 0.00001, Err('Input should be a multiple of 0.1')),
(0.1, -0.00001, Err('Input should be a multiple of 0.1')),
(0.01, 0.00001, Err('Input should be a multiple of 0.01')),
(0.01, -0.00001, Err('Input should be a multiple of 0.01')),
],
ids=repr,
)
Expand Down

0 comments on commit ed15f6c

Please sign in to comment.