Reputation: 4914
Taking a floating point number, I would like to round down, depending upon a user defined 'bin' size. The bin size therefore will change depending upon user preference. For example, the bin sizes may be 0.5, 0.1, 1 or even 0.01.
My aim is to determine which bin a floating point number will fall into. As an example:
a 0.1 bin size:
2348.285 will fall into a 2348.2 bin
238.592 will fall into a 238.5 bin
a 0.5 bin size:
2348.285 will fall into a 2348.0 bin
238.592 will fall into a 238.5 bin
a 0.01 bin size:
2348.285 will fall into a 2348.28 bin
238.592 will fall into a 238.59 bin
a 1 bin size:
2348.285 will fall into a 2348 bin
238.592 will fall into a 238 bin
I have looked into rounding in Perl for example, the floor(), sprintf() and also substr() methods, however, none of them do quite want I want to do, or rather, I can't make them do what I want. What neat code will dynamically change the floor value that the floating point number will round down to given the user-defined bin size?
Upvotes: 1
Views: 232
Reputation: 118595
Subject to some quirks, the round-down-to-arbitrary-bin-size formula is
use POSIX 'floor';
$bin = $bin_size * floor( $value / $bin_size )
A workaround to the 24.01 / 0.01 => 2400.99999999999996758 =>
rounds to 2400
(for example) is to perturb your input, like
$epsilon = 1.0e-12;
$bin = $bin_size * floor( ($value+$epsilon) / $bin_size)
or
$bin = $bin_size * floor( $value / $bin_size + $epsilon )
Note that you may have better luck multiplying by 1/$bin_size
than by dividing by $bin_size
. For example, you've seen that floating-point arithmetic with the value 0.1
can be trouble, so computing $value * 10
might not give unexpected results as often as $value / 0.1
.
(but what's the workaround to the workaround when your actual input is 24.01 - $epsilon
and you intend for that to be rounded down to 24.00?)
Upvotes: 1
Reputation: 1375
You might try Math::Round:
use Math::Round 'nlowmult';
print nlowmult( .01, 2348.285 ) . "\n";
print nlowmult( .01, 238.592 ) . "\n";
Upvotes: 5