Re: Dividing and format
by kilinrax (Deacon) on Nov 27, 2003 at 14:01 UTC
|
printf / sprintf is the obvious way, and given they are builtin functions, I see no reason not to use them.
Want to give us a slightly fuller description of the problem and reasons for the restriction before this gets branded as homework? ;)
| [reply] |
|
I don't need to display the result, I first want to store the result, do some calculations with it and than display another result.
| [reply] |
|
Normally, intermediate calculations are done without rounding, to gain maximum accuracy - you round at the end for display purposes. If for some reason you actually need to round the number for intermediate calculations, then you use sprintf.
| [reply] |
|
Re: Dividing and format
by Abigail-II (Bishop) on Nov 27, 2003 at 14:14 UTC
|
What's the problem with using printf? Anyway, the following
code seems to work (although it doesn't do 'Bankers rounding'):
#!/usr/bin/perl
use strict;
use warnings;
while (<DATA>) {
chomp;
my $o = $_;
next if /\D/ || /^$/;
substr $_ => -0, 0, 0 x (4 - length);
$_ ++ if 50 <= substr $_ => -2, 2, "";
substr $_ => -1, 0, ".";
print "$o / 1000 = $_\n";
}
__DATA__
12000
12678
5
49
50
499
500
501
12000 / 1000 = 12.0
12678 / 1000 = 12.7
5 / 1000 = 0.0
49 / 1000 = 0.0
50 / 1000 = 0.1
499 / 1000 = 0.5
500 / 1000 = 0.5
501 / 1000 = 0.5
Abigail | [reply] [d/l] |
Re: Dividing and format
by l3nz (Friar) on Nov 27, 2003 at 15:17 UTC
|
I frankly don't understand why you should have an internal representation of the number with the decimal zero (either you want to print it or you want to compute with it, but maybe I'm wrong). Anyway the right way to go is using sprintf() to generate a correctly truncated string and then rely on Perl's ability to cast numbers in the most appropriate ways.
Here is an example:
use strict;
while ( <DATA> ) {
my $val = sprintf( "%.01f", $_/1000 );
# do maths with the truncated result....
my $val2 = $val * 2;
print "$val - $val2\n";
}
__DATA__
100
1000
2314
171
123456789
The result is
0.1 - 0.2
1.0 - 2
2.3 - 4.6
0.2 - 0.4
123456.8 - 246913.6
Note how the .0 is lost if you do any maths on 1.0 unless you format it again before printing - the first column is made of strings, the second of floats.
| [reply] [d/l] |
Re: Dividing and format
by zengargoyle (Deacon) on Nov 27, 2003 at 14:24 UTC
|
$ cat test.in
12000
12678
5
49
50
499
500
501
^D
$ perl -lpe '$_=int(($_+50)/100)/10;$_.=".0"unless/\./' <test.in
12.0
12.7
0.0
0.0
0.1
0.5
0.5
0.5
| [reply] [d/l] |
Re: Dividing and format
by tadman (Prior) on Nov 27, 2003 at 16:38 UTC
|
You can do it with straight math, too:
sub round_to
{
my ($value, $places) = @_;
my $exp = 10 ** $places;
return int(($value * $exp * 10 + 5) / 10) / $exp;
}
print round_to(12678/1000, 1),$/;
| [reply] [d/l] |
|
Perhaps bizarrely, tadman's code doesn't work with all numbers. I think it's internal rounding errors on Perl's part rather than a fault with the code (though I could be wrong :). I've broken down the code to analyse what's going on, viz:
$number = 12650 / 10000;
$dp = 2;
print "Actual: $number\n";
my $exp = 10 ** $dp;
$val1 = $number * $exp;
$val2 = $val1 + 0.5;
$val3 = int($val2);
$val4 = $val3 / $exp;
print "val1 = value x exp = $val1\n";
print "val2 = val1 + 0.5 = $val2\n";
print "val3 = int(val2) = $val3\n";
print "val4 = val3 / exp = $val4\n";
The output:
Actual: 1.265
val1 = value x exp = 126.5
val2 = val1 + 0.5 = 127
val3 = int(val2) = 126
val4 = val3 / exp = 1.26
Verified on Win32 with ActivePerl 5.8.1 b807, and in Linux with Perl 5.8.0.
Replace the first line with "$number = 1.265;", however, and you get the correct result. Weird. | [reply] [d/l] [select] |
|
It's probably storing "127" as 126.9999999999999 which rounds down with the int function. Aren't floating point numbers fun?
The documentation for int suggests that the POSIX::floor function is a better selection for this kind of work.
| [reply] |
|
use strict;
my $number = 12650 / 10000;
#my $number = 1.265;
my $dp = 2;
print "Actual: $number\n";
my $exp = 10 ** ($dp+1);
my $val1 = int($number * $exp);
my $val2 = $val1 + 5;
my $val4 = $val2 / $exp;
print "val1 = value x exp = $val1\n";
print "val2 = val1 + 5 = $val2\n";
#print "val3 = int(val2) = $val3\n";
print "val4 = val2 / exp = $val4\n";
Output
Actual: 1.265
val1 = value x exp = 1265
val2 = val1 + 5 = 1270
val4 = va2 / exp = 1.27
Ok I messed it up if you use 1200 you get 12.05 so I think you need to do it in steps throwing away un needed decimals.
Oh well need to catch a train will play with it tonight because it is not Internet Explorer and I need something that is not Internet Explorer.
"No matter where you go, there you are." BB
| [reply] [d/l] [select] |
|
|
|
|
|
|
| [reply] |
|
Eventually you're going to have to use something printf-like function such as sprintf anyway. I was just proposing something that kept the numbers as numbers instead of stringifying them. It is numerically 12.0, even if print decides to show it as 12 without the decimal.
That being said, any time you're dealing with floating point numbers it's pretty much advised to use some kind of formatting method. Some numbers might be assigned a value of 12.0 but end up displaying as 11.9999999999999999981 instead, or even 12.00000000000000012.
| [reply] |
Re: Dividing and format
by davido (Cardinal) on Nov 27, 2003 at 17:42 UTC
|
As others have mentioned, the obvious answer is to use sprintf as follows:
my $num = 12000/1000;
my $string = sprintf "%.01f", $num;
print $string, "\n";
But for no good reason I started thinking, hmm, what if we lived in some wierd world where sprintf didn't exist, but printf did?
One could write his own sprintf function by hand. But that's not nearly as fun as abusing Perl 5.8.0 (or later):
use strict;
use warnings;
require 5.8.0;
my $num = 12;
print format_str( "%.01f", $num ), "\n";
sub format_str {
my $string;
open STROUT, ">", \$string or die "You fool: $!\n";
printf STROUT shift, @_;
close STROUT or die "trying: $!\n";
return $string;
}
This method doesn't meet the OP's spec, because it uses printf. But I couldn't resist an opportunity to write to an in-memory file (held in a scalar, $string) and then turn around and use it as a plain old scalar later.
I dare you to turn that one in to the teacher. ;)
Update: Added 'require 5.8.0;' to my snippet to protect people who might otherwise ignore my suggestion that this is only a Perl 5.8.0 or later solution.
Dave
"If I had my life to live over again, I'd be a plumber." -- Albert Einstein
| [reply] [d/l] [select] |
Re: Dividing and format with regular expression the perl way
by Roger (Parson) on Nov 27, 2003 at 20:15 UTC
|
Yes, you can certainly do this without using printf or sprintf. Number can be treated as string in perl. I will provide a simple regular expression solution since nobody has thought about it yet. ;-)
#!/usr/local/bin/perl -w
use strict;
my $xx = 12000/1000; # wanted result is 12.0
my $yy = 12678/1000; # wanted result is 12.7 - rounded up
print "\$xx = ", rounding_with_regex($xx), "\n";
print "\$yy = ", rounding_with_regex($yy), "\n";
sub rounding_with_regex
{
my $num = shift;
$num += 0.05; # want to round up (as number)
$num =~ s/\.(\d).*/.$1/; # perform the rounding (as string)
return $num;
}
And the output is exactly as required -
$xx = 12.0
$yy = 12.7
| [reply] [d/l] [select] |
|
| [reply] [d/l] |
|
Well, just drop trailing dot-star in the regex and see the result. ;-)
I want to get rid of anything after the first decimal point, the tailing (.*) let me do that. Of course I can also rewrite the regular expression as -
$num =~ s/\.(\d)\d+/.$1/; # perform the rounding (as string)
But that requires 3 characters, and I am just too lazy. Besides there is an assumption that whatever passed in is a floating point.
| [reply] [d/l] |
|
In this case, it removes anything after the first decimal.
Perhaps you are thinking of needless .* at the end of a m//?
| [reply] |