~ubuntu-branches/ubuntu/intrepid/horae/intrepid

« back to all changes in this revision

Viewing changes to 0CPAN/Math-Round-0.05/Round.pm

  • Committer: Bazaar Package Importer
  • Author(s): Carlo Segre
  • Date: 2008-02-23 23:13:02 UTC
  • mfrom: (2.1.2 hardy)
  • Revision ID: james.westby@ubuntu.com-20080223231302-mnyyxs3icvrus4ke
Tags: 066-3
Apply patch to athena_parts/misc.pl for compatibility with 
perl-tk 804.28.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
package Math::Round;
2
 
 
3
 
use strict;
4
 
use POSIX;
5
 
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
6
 
 
7
 
require Exporter;
8
 
 
9
 
@ISA = qw(Exporter AutoLoader);
10
 
@EXPORT = qw(round nearest);
11
 
@EXPORT_OK = qw(round nearest round_even round_odd round_rand
12
 
   nearest_ceil nearest_floor nearest_rand
13
 
   nlowmult nhimult );
14
 
$VERSION = '0.05';
15
 
 
16
 
%EXPORT_TAGS = ( all => [ @EXPORT_OK ] );
17
 
 
18
 
#--- Determine what value to use for "one-half".  Because of the
19
 
#--- perversities of floating-point hardware, we must use a value
20
 
#--- slightly larger than 1/2.  We accomplish this by determining
21
 
#--- the bit value of 0.5 and increasing it by a small amount in a
22
 
#--- lower-order byte.  Since the lowest-order bits are still zero,
23
 
#--- the number is mathematically exact.
24
 
 
25
 
my $halfhex = unpack('H*', pack('d', 0.5));
26
 
if (substr($halfhex,0,2) ne '00' && substr($halfhex, -2) eq '00') {
27
 
   #--- It's big-endian.
28
 
   substr($halfhex, -4) = '1000';
29
 
} else {
30
 
   #--- It's little-endian.
31
 
   substr($halfhex, 0,4) = '0010';
32
 
}
33
 
 
34
 
my $half = unpack('d',pack('H*', $halfhex));
35
 
 
36
 
sub round {
37
 
 my $x;
38
 
 my @res = ();
39
 
 foreach $x (@_) {
40
 
   if ($x >= 0) {
41
 
      push @res, POSIX::floor($x + $half);
42
 
   } else {
43
 
      push @res, POSIX::ceil($x - $half);
44
 
   }
45
 
 }
46
 
 return (wantarray) ? @res : $res[0];
47
 
}
48
 
 
49
 
sub round_even {
50
 
 my $x;
51
 
 my @res = ();
52
 
 foreach $x (@_) {
53
 
   my ($sign, $in, $fr) = _sepnum($x);
54
 
   if ($fr == 0.5) {
55
 
      push @res, $sign * (($in % 2 == 0) ? $in : $in + 1);
56
 
   } else {
57
 
      push @res, $sign * POSIX::floor(abs($x) + $half);
58
 
   }
59
 
 }
60
 
 return (wantarray) ? @res : $res[0];
61
 
}
62
 
 
63
 
sub round_odd {
64
 
 my $x;
65
 
 my @res = ();
66
 
 foreach $x (@_) {
67
 
   my ($sign, $in, $fr) = _sepnum($x);
68
 
   if ($fr == 0.5) {
69
 
      push @res, $sign * (($in % 2 == 1) ? $in : $in + 1);
70
 
   } else {
71
 
      push @res, $sign * POSIX::floor(abs($x) + $half);
72
 
   }
73
 
 }
74
 
 return (wantarray) ? @res : $res[0];
75
 
}
76
 
 
77
 
sub round_rand {
78
 
 my $x;
79
 
 my @res = ();
80
 
 foreach $x (@_) {
81
 
   my ($sign, $in, $fr) = _sepnum($x);
82
 
   if ($fr == 0.5) {
83
 
      push @res, $sign * ((rand(4096) < 2048) ? $in : $in + 1);
84
 
   } else {
85
 
      push @res, $sign * POSIX::floor(abs($x) + $half);
86
 
   }
87
 
 }
88
 
 return (wantarray) ? @res : $res[0];
89
 
}
90
 
 
91
 
#--- Separate a number into sign, integer, and fractional parts.
92
 
#--- Return as a list.
93
 
sub _sepnum {
94
 
 my $x = shift;
95
 
 my ($sign, $i);
96
 
 $sign = ($x >= 0) ? 1 : -1;
97
 
 $x = abs($x);
98
 
 $i = int($x);
99
 
 return ($sign, $i, $x - $i);
100
 
}
101
 
 
102
 
#------ "Nearest" routines (round to a multiple of any number)
103
 
 
104
 
sub nearest {
105
 
 my ($targ, @inputs) = @_;
106
 
 my @res = ();
107
 
 my $x;
108
 
 
109
 
 $targ = abs($targ) if $targ < 0;
110
 
 foreach $x (@inputs) {
111
 
   if ($x >= 0) {
112
 
      push @res, $targ * int(($x + $half * $targ) / $targ);
113
 
   } else {
114
 
      push @res, $targ * POSIX::ceil(($x - $half * $targ) / $targ);
115
 
   }
116
 
 }
117
 
 return (wantarray) ? @res : $res[0];
118
 
}
119
 
 
120
 
# In the next two functions, the code for positive and negative numbers
121
 
# turns out to be the same.  For negative numbers, the technique is not
122
 
# exactly obvious; instead of floor(x+0.5), we are in effect taking
123
 
# ceiling(x-0.5).
124
 
 
125
 
sub nearest_ceil {
126
 
 my ($targ, @inputs) = @_;
127
 
 my @res = ();
128
 
 my $x;
129
 
 
130
 
 $targ = abs($targ) if $targ < 0;
131
 
 foreach $x (@inputs) {
132
 
    push @res, $targ * POSIX::floor(($x + $half * $targ) / $targ);
133
 
 }
134
 
 return (wantarray) ? @res : $res[0];
135
 
}
136
 
 
137
 
sub nearest_floor {
138
 
 my ($targ, @inputs) = @_;
139
 
 my @res = ();
140
 
 my $x;
141
 
 
142
 
 $targ = abs($targ) if $targ < 0;
143
 
 foreach $x (@inputs) {
144
 
    push @res, $targ * POSIX::ceil(($x - $half * $targ) / $targ);
145
 
 }
146
 
 return (wantarray) ? @res : $res[0];
147
 
}
148
 
 
149
 
sub nearest_rand {
150
 
 my ($targ, @inputs) = @_;
151
 
 my @res = ();
152
 
 my $x;
153
 
 
154
 
 $targ = abs($targ) if $targ < 0;
155
 
 foreach $x (@inputs) {
156
 
   my ($sign, $in, $fr) = _sepnear($x, $targ);
157
 
   if ($fr == 0.5 * $targ) {
158
 
      push @res, $sign * $targ * ((rand(4096) < 2048) ? $in : $in + 1);
159
 
   } else {
160
 
      push @res, $sign * $targ * int((abs($x) + $half * $targ) / $targ);
161
 
   }
162
 
 }
163
 
 return (wantarray) ? @res : $res[0];
164
 
}
165
 
 
166
 
#--- Next lower multiple
167
 
sub nlowmult {
168
 
 my ($targ, @inputs) = @_;
169
 
 my @res = ();
170
 
 my $x;
171
 
 
172
 
 $targ = abs($targ) if $targ < 0;
173
 
 foreach $x (@inputs) {
174
 
    push @res, $targ * POSIX::floor($x / $targ);
175
 
 }
176
 
 return (wantarray) ? @res : $res[0];
177
 
}
178
 
 
179
 
#--- Next higher multiple
180
 
sub nhimult {
181
 
 my ($targ, @inputs) = @_;
182
 
 my @res = ();
183
 
 my $x;
184
 
 
185
 
 $targ = abs($targ) if $targ < 0;
186
 
 foreach $x (@inputs) {
187
 
    push @res, $targ * POSIX::ceil($x / $targ);
188
 
 }
189
 
 return (wantarray) ? @res : $res[0];
190
 
}
191
 
 
192
 
#--- Separate a number into sign, "integer", and "fractional" parts
193
 
#--- for the 'nearest' calculation.  Return as a list.
194
 
sub _sepnear {
195
 
 my ($x, $targ) = @_;
196
 
 my ($sign, $i);
197
 
 $sign = ($x >= 0) ? 1 : -1;
198
 
 $x = abs($x);
199
 
 $i = int($x / $targ);
200
 
 return ($sign, $i, $x - $i*$targ);
201
 
}
202
 
 
203
 
1;
204
 
 
205
 
__END__
206
 
 
207
 
=head1 NAME
208
 
 
209
 
Math::Round - Perl extension for rounding numbers
210
 
 
211
 
=head1 SYNOPSIS
212
 
 
213
 
  use Math::Round qw(...those desired... or :all);
214
 
 
215
 
  $rounded = round($scalar);
216
 
  @rounded = round(LIST...);
217
 
  $rounded = nearest($target, $scalar);
218
 
  @rounded = nearest($target, LIST...);
219
 
 
220
 
  # and other functions as described below
221
 
 
222
 
=head1 DESCRIPTION
223
 
 
224
 
B<Math::Round> supplies functions that will round numbers in different
225
 
ways.  The functions B<round> and B<nearest> are exported by
226
 
default; others are available as described below.  "use ... qw(:all)"
227
 
exports all functions.
228
 
 
229
 
=head1 FUNCTIONS
230
 
 
231
 
=over 2
232
 
 
233
 
=item B<round> LIST
234
 
 
235
 
Rounds the number(s) to the nearest integer.  In scalar context,
236
 
returns a single value; in list context, returns a list of values.
237
 
Numbers that are halfway between two integers are rounded
238
 
"to infinity"; i.e., positive values are rounded up (e.g., 2.5
239
 
becomes 3) and negative values down (e.g., -2.5 becomes -3).
240
 
 
241
 
=item B<round_even> LIST
242
 
 
243
 
Rounds the number(s) to the nearest integer.  In scalar context,
244
 
returns a single value; in list context, returns a list of values.
245
 
Numbers that are halfway between two integers are rounded to the
246
 
nearest even number; e.g., 2.5 becomes 2, 3.5 becomes 4, and -2.5
247
 
becomes -2.
248
 
 
249
 
=item B<round_odd> LIST
250
 
 
251
 
Rounds the number(s) to the nearest integer.  In scalar context,
252
 
returns a single value; in list context, returns a list of values.
253
 
Numbers that are halfway between two integers are rounded to the
254
 
nearest odd number; e.g., 3.5 becomes 3, 4.5 becomes 5, and -3.5
255
 
becomes -3.
256
 
 
257
 
=item B<round_rand> LIST
258
 
 
259
 
Rounds the number(s) to the nearest integer.  In scalar context,
260
 
returns a single value; in list context, returns a list of values.
261
 
Numbers that are halfway between two integers are rounded up or
262
 
down in a random fashion.  For example, in a large number of trials,
263
 
2.5 will become 2 half the time and 3 half the time.
264
 
 
265
 
=item B<nearest> TARGET, LIST
266
 
 
267
 
Rounds the number(s) to the nearest multiple of the target value.
268
 
TARGET must be positive.
269
 
In scalar context, returns a single value; in list context, returns
270
 
a list of values.  Numbers that are halfway between two multiples
271
 
of the target will be rounded to infinity.  For example:
272
 
 
273
 
  nearest(10, 44)    yields  40
274
 
  nearest(10, 46)            50
275
 
  nearest(10, 45)            50
276
 
  nearest(25, 328)          325
277
 
  nearest(.1, 4.567)          4.6
278
 
  nearest(10, -45)          -50
279
 
 
280
 
=item B<nearest_ceil> TARGET, LIST
281
 
 
282
 
Rounds the number(s) to the nearest multiple of the target value.
283
 
TARGET must be positive.
284
 
In scalar context, returns a single value; in list context, returns
285
 
a list of values.  Numbers that are halfway between two multiples
286
 
of the target will be rounded to the ceiling, i.e. the next
287
 
algebraically higher multiple.  For example:
288
 
 
289
 
  nearest_ceil(10, 44)    yields  40
290
 
  nearest_ceil(10, 45)            50
291
 
  nearest_ceil(10, -45)          -40
292
 
 
293
 
=item B<nearest_floor> TARGET, LIST
294
 
 
295
 
Rounds the number(s) to the nearest multiple of the target value.
296
 
TARGET must be positive.
297
 
In scalar context, returns a single value; in list context, returns
298
 
a list of values.  Numbers that are halfway between two multiples
299
 
of the target will be rounded to the floor, i.e. the next
300
 
algebraically lower multiple.  For example:
301
 
 
302
 
  nearest_floor(10, 44)    yields  40
303
 
  nearest_floor(10, 45)            40
304
 
  nearest_floor(10, -45)          -50
305
 
 
306
 
=item B<nearest_rand> TARGET, LIST
307
 
 
308
 
Rounds the number(s) to the nearest multiple of the target value.
309
 
TARGET must be positive.
310
 
In scalar context, returns a single value; in list context, returns
311
 
a list of values.  Numbers that are halfway between two multiples
312
 
of the target will be rounded up or down in a random fashion.
313
 
For example, in a large number of trials, C<nearest(10, 45)> will
314
 
yield 40 half the time and 50 half the time.
315
 
 
316
 
=item B<nlowmult> TARGET, LIST
317
 
 
318
 
Returns the next lower multiple of the number(s) in LIST.
319
 
TARGET must be positive.
320
 
In scalar context, returns a single value; in list context, returns
321
 
a list of values.  Numbers that are between two multiples of the
322
 
target will be adjusted to the nearest multiples of LIST that are
323
 
algebraically lower. For example:
324
 
 
325
 
  nlowmult(10, 44)    yields  40
326
 
  nlowmult(10, 46)            40
327
 
  nlowmult(25, 328)          325
328
 
  nlowmult(.1, 4.567)          4.5
329
 
  nlowmult(10, -41)          -50
330
 
 
331
 
=item B<nhimult> TARGET, LIST
332
 
 
333
 
Returns the next higher multiple of the number(s) in LIST.
334
 
TARGET must be positive.
335
 
In scalar context, returns a single value; in list context, returns
336
 
a list of values.  Numbers that are between two multiples of the
337
 
target will be adjusted to the nearest multiples of LIST that are
338
 
algebraically higher. For example:
339
 
 
340
 
  nhimult(10, 44)    yields  50
341
 
  nhimult(10, 46)            50
342
 
  nhimult(25, 328)          350
343
 
  nhimult(.1, 4.512)          4.6
344
 
  nhimult(10, -49)          -40
345
 
 
346
 
=back
347
 
 
348
 
=head1 STANDARD FLOATING-POINT DISCLAIMER
349
 
 
350
 
Floating-point numbers are, of course, a rational subset of the real
351
 
numbers, so calculations with them are not always exact.  In order to
352
 
avoid surprises because of this, these routines use a value for
353
 
one-half that is very slightly larger than 0.5.  Nevertheless,
354
 
if the numbers to be rounded are stored as floating-point, they will
355
 
be subject, as usual, to the mercies of your hardware, your C
356
 
compiler, etc.  Thus, numbers that are supposed to be halfway between
357
 
two others may be stored in a slightly different way and thus behave
358
 
surprisingly.
359
 
 
360
 
=head1 AUTHOR
361
 
 
362
 
Math::Round was written by Geoffrey Rommel E<lt>GROMMEL@cpan.orgE<gt>
363
 
in October 2000.
364
 
 
365
 
=cut