Rambatino
Rambatino

Reputation: 4914

Rounding down to varying decimal place counts in Perl

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

Answers (2)

mob
mob

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

Chris J
Chris J

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

Related Questions