You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix: respect both multiple_of and minimum/maximum constraints
Previously, `generate_constrained_number()` would potentially generate
invalid numbers when `multiple_of` is not None and exactly one of either
`minimum` or `maximum` is not None, since it would just return
`multiple_of` without respecting the upper or lower bound.
This significantly changes the implementation of the code to correctly
handle this code. The `generate_constrained_number()` method has been
completely removed, being replaced with a
`generate_constrained_multiple_of()` function. A major difference
between the old function and the new function is that the new one does
not accept a `method` argument for generating random numbers. This is
because in the new function, we always use `create_random_integer()`,
since the problem reduces to generating a random integer multiplier.
The high-level algorithm behind `generate_constrained_multiple_of()` is
that we need to constrain the random integer generator to generate
numbers such that when they are multiplied with `multiple_of`, they
still fit within the original bounds constraints. This simplify involves
dividing the original bounds by `multiple_of`, with some special
handling for negative `multiple_of` numbers as well as carefully chosen
rounding behavior.
We also need to make some changes to other functions.
`get_increment()` needs to take an additional argument for the actual
value that the increment is for. This is because floating-point numbers
can't use a static increment or else it might get rounded away if the
numbers are too large. Python fortunately provides a `math.ulp()`
function for computing this for a given float value, so we make use of
that function. We still use the original `float_info.epsilon` constant
as a lower bound on the increment, though, since in the case that the
value is too close to zero, we still need to make sure that the
increment doesn't disappear when used against other numbers.
Finally, we rename and modify `passes_pydantic_multiple_validator()` to
`is_almost_multiple_of()`, modifying its implementation to defer the
casting of values to `float()` to minimize rounding errors. This
specifically affects Decimal numbers, where casting to float too early
causes too much loss of precision.
A significant number of changes were made to the tests as well, since
the original tests missed the bug being fixed here. Each of the integer,
floating-point, and decimal tests has been updated to assert that the
result is actually within the minimum and maximum constraints. In
addition, we remove some unnecessary sorting of the randomly generated
test input values, since this was unnecessarily constraining
`multiple_of` to be greater than or less than the minimum and maximum
values. This was causing a lot of the scenarios involving negative
values to be skipped.
Lastly, the floating-point and decimal tests need additional constraints
to avoid unrealistic extreme values from hitting precision issues. This
was done by adding a number of constraints on the number of significant
digits in the input numbers and on the relative magnitudes of the input
numbers.
0 commit comments