1 common.inc | valid_number_step($value, $step, $offset = 0.0) |
Verifies that a number is a multiple of a given step.
The implementation assumes it is dealing with IEEE 754 double precision floating point numbers that are used by PHP on most systems.
Parameters
mixed $value: The numeric value that needs to be checked, integer or float.
mixed $step: The step scale factor, integer or float. Must be positive.
mixed $offset: (optional) An offset, to which the difference must be a multiple of the given step, integer or float.
Return value
bool: TRUE if no step mismatch has occurred, or FALSE otherwise.
Related topics
File
- core/
includes/ common.inc, line 1513 - Common functions that many Backdrop modules will need to reference.
Code
function valid_number_step($value, $step, $offset = 0.0) {
// Avoid division by zero.
if ($step == 0) {
return FALSE;
}
// Consider offset, get absolute values.
$value = abs($value - $offset);
$step = abs($step);
// Value and step are the same, no need for math.
if ($value == $step) {
return TRUE;
}
// Both numbers are integers - use modulo right away.
if ((int) $value == $value && (int) $step == $step) {
return $value % $step == 0;
}
// Convert from possible exponent notation to decimal strings to get the scale
// values. Note: sprintf also rounds values but seems to give more reliable
// results than number_format.
// "10" is the maximum value allowed by the number widget, so higher scale
// values do not make much sense.
// But as several (also unrelated) tests require a higher value, we have to
// provide at least a scale of 13 here.
$max_scale = 13;
$step_string = rtrim(sprintf("%.{$max_scale}F", $step), '0');
$value_string = rtrim(sprintf("%.{$max_scale}F", $value), '0');
// If the actual value has more significant decimals than expected, it has a
// higher precision than desired and is not divisible by the step.
// But if the scale value is high, and the length of string (minus comma) is
// beyond the php setting for precision, it's more likely that the conversion
// to string caused a problem with rounding. It's a compromise to allow it,
// because there's no way for php to handle it reliably.
$step_scale = strpos(strrev($step_string), '.');
$value_scale = strpos(strrev($value_string), '.');
if ($value_scale > $step_scale) {
if ($value_scale > 10 && $step_scale > 0 && strlen($value_string) - 1 > ini_get('precision')) {
$value = round($value, $step_scale);
}
else {
return FALSE;
}
}
// Because of the floating point precision problem, fmod() has a high error
// rate. We convert the floats to integers instead by multiplying powers of 10
// and use the modulo operator.
$step_int = intval($step * pow(10, $step_scale));
$value_int = intval($value * pow(10, $step_scale));
return $value_int % $step_int == 0;
}