Re: Help with decimals
by HyperZonk (Friar) on Jul 29, 2001 at 23:33 UTC
|
The printf statement doesn't change the value it's printing,
just the format. Thus, formatting 1500 with 2 decimal places
should do exactly what you described it doing: printing
1500.00.
I do not really understand what you mean by avoiding "doing
math" ... why do you not want to use math (as far as I know,
you pretty much have to do math to meaningfully and reliably
generate the output you desire). Perhaps you mean that you
do not want to alter the value of $foo to generate the output,
and also do not want to generate a new variable just for
output formatting. If so, that is simple enough:
$foo = 1500;
printf ("%.2f", $foo/100);
This generates the desired output without affecting the
value of $foo and without using another variable. Yes, you
have still multiplied by 0.01, but that value is only used
for the printf output and then vanishes.
-HZ | [reply] [d/l] |
Re: Help with decimals
by rchiav (Deacon) on Jul 29, 2001 at 23:49 UTC
|
HyperZonk's way of doing it is much better, but if you didn't want to do "math" (and I'm really not sure why), you could use chop. It's much uglier, cumbersome and all around a worse method.. but in spirit of TIMTOWTDI..
#!/usr/bin/perl -w
use strict;
my $foo;
$foo = 1500;
print myformat($foo, 2) . "\n";
sub myformat {
my ($number, $places) = @_;
my $decimal;
$decimal = "";
for (1 .. $places) {
if ($number) {
$decimal .= chop $number;
}
else {
$decimal .= 0;
}
}
return "$number\." . reverse $decimal;
}
Update: Changed this to be a sub so it doesn't munge the original value as suggested by HyperZonk.
I also added the reverse because I no-so-brillantly forgot that the decimals would be reversed.
Rich | [reply] [d/l] |
|
|
Your plucking the digits off one end, sticking on the dot, and sticking them back on sounds like something a regex should do!
$number .= '0' while length($number) < $places;
# add trailing zeros if needed
$number =~ /(.{$places})/\.$1/;
Or a substr
# add trailing zeros, as above.
substr ($number, -$places, 0, '.');
| [reply] [d/l] [select] |
|
|
Acutally a regex isn't all that good of an idea. It's very slow. But substr is better than chop.. benchmarks included..
Benchmark: timing 100000 iterations of chop, math, regex, substr...
chop: 6 wallclock secs ( 4.92 usr + 0.03 sys = 4.95 CPU) @ 202
+02.02/s (n=100000)
math: 2 wallclock secs ( 1.07 usr + 0.02 sys = 1.09 CPU) @ 917
+43.12/s (n=100000)
regex: 25 wallclock secs (17.40 usr + 0.32 sys = 17.72 CPU) @ 56
+43.34/s (n=100000)
substr: 2 wallclock secs ( 1.99 usr + 0.03 sys = 2.02 CPU) @ 495
+04.95/s (n=100000)
Doing the math is still the best option..
| [reply] [d/l] |
(ichimunki) Re: Help with decimals
by ichimunki (Priest) on Jul 30, 2001 at 03:51 UTC
|
You say that some of your numbers will already have decimals in them. If you have a number like 1500 that you want to display as 15.00, then what do you want to do with numbers like 1501.5 or 1501.55? Do you want them to print as 15.02 and 15.02 (correctly rounded)? Or do you want them to print as 1501.50 and 1501.55?
If the former, then the printf( "%.2f", $foo/100 ) solution works fine (although I don't know if it will round correctly for display or not)... If the latter, then you really need to ask why you are storing your numbers in such a disparate fashion and consider standardizing. Otherwise you have to convert units (i.e. do math) at some point, utterly dependent on the units of the original number. | [reply] |
Re: Help with decimals
by La12 (Sexton) on Jul 30, 2001 at 05:59 UTC
|
Sorry to bring so much confusion to this problem.
Let me explain my comment as to why I wanted to avoid math.
$file = 100+ lines of 12 field comma delimited text and numbers (text,text,num,num,num,...) there are no decimals in these numbers.
$bar = a number from 0 thru 9 that will be given to me by the third field in one 12 field comma delimited line. This number will determine the number of decimal spaces that the other fields in that same line will have.
$foo = numerical fields number 4 thru 8.
I thought I could say something like:
if $bar = x then put a "." x+1 characters in from the right of $foo.
as opposed to saying:
if $bar = 2 then $foo * .01 .. printf...
if $bar = 3 then $foo * .001 printf ...
if $bar = 4 then $foo * .0001 printf...
So this is why I thought that it would be easier not to do math.
I'm really sorry about not explaining myself properly.
Hope this helps. | [reply] |
|
|
First of all, let me say that there must be
a better way for you to store your data. However, you might
be stuck with the format, for all I know, so on with an
attempted solution ...
Okay, let's say you've split one line into fields using
the appropriate module.
You store those fields in @currentLine. Then you can do something
like:
foreach (@currentLine[3 .. 11]) {
printf ("%.*f", $currentLine[2], $_/(10 ** $currentLine[2])
}
The * sucks up the next variable in the list and uses it
as the field width; in this case, it slurps the first
appearance of $currentLine[2] in the list.
Update: As tilly has noted, the module I mentioned
above does not handle newlines embedded within records.
Another module,
recommended by the same esteemed personage who is on
vacation does that, if it is required.
-HZ | [reply] [d/l] [select] |
|
|
You're right about the data. The file is given to me by a client. I have no control over the format.
| [reply] |
Re: Help with decimals
by La12 (Sexton) on Jul 30, 2001 at 06:07 UTC
|
one more example...
If the number is 1368 and $bar is 3
then the result should be 1.368
if the number xxxxx and $bar is 2
then the result should be xxx.xx
| [reply] |
|
|
Then you just want to do a..
#!/usr/bin/perl -w
use strict;
my ($foo, $bar);
$foo = 1500;
$bar = 2;
print $foo / (10 ** $bar) ."\n";
Hope this helps..
Rich | [reply] [d/l] |
Re: Help with decimals
by La12 (Sexton) on Jul 30, 2001 at 07:23 UTC
|
Rich,
When I run your script I get "15" not "15.00"
if $foo = 1500 and...
if $bar = 4 then the result should be .1500
if $bar = 3 then the result should be 1.500
if $bar = 2 then the result should be 15.00
if $bar = 1 then the result should be 150.0
if $bar = 0 then the result should be 1500
another example
if $foo = 12653 and...
if $bar = 4 then the result should be 1.2653
if $bar = 3 then the result should be 12.653
if $bar = 2 then the result should be 126.53
if $bar = 1 then the result should be 1265.3
if $bar = 0 then the result should be 12653
Again, this is why I thought of it as "lets put add a dot to $foo at $bar+1 from the right (I don't know how to code that)" as opposed to lets multiply $foo by .01 or .001 etc.
| [reply] |
|
|
First make sure $bar is numeric, then...
printf ("%.${bar}f", $foo / (10 ** $bar));
To deconstruct:
Take 10 to the power $bar to get the correct divisor. Then divide by it to move the decimal place over non-destructively. Then, to give the correct number of places, use printf and %. f -- note that there must be {} around bar because otherwise Perl will try to interpolate $barf, which, if you're using strict and have no variable called $barf will indeed barf. To get the string value rather than printing it out, use sprintf rather than printf.
So, for instance, if you have $foo being 570 and $bar being 1, you get (reducing the expression, so to speak):
printf ("%.${bar}f", $foo / (10 ** $bar))
printf ("%.1f", 570 / (10 ** 1))
printf ("%.1f", 570 / 10)
printf ("%.1f", 57)
"57.0"
... which is what you wanted I think. | [reply] [d/l] |
|
|
Do you want the decimals even if they aren't siginficant? 2 zeros aren't significant. if you change the number to 1555, it will print 15.55. It just drops non significant digits.
| [reply] |
|
|
yes I do. I understand 15 is the same as 15.00, but I need these zeros to remain in $foo's end result. The original digits in $foo must remain (i.e. $foo=1500 then result must have 1500 in it). I'm sorry, I did not try to use a different number in your script.
| [reply] |
Re: Help with decimals
by Monky Python (Scribe) on Jul 30, 2001 at 17:18 UTC
|
This code solves the problem without "doing math".
#!/usr/bin/perl -w
use strict;
my $foo = 17432;
my $bar = 3;
$foo =~ s/([0-9]+)([0-9]{$bar})/$1.$2/;
print $foo;
MP | [reply] [d/l] |
Re: Help with decimals
by La12 (Sexton) on Jul 30, 2001 at 22:01 UTC
|
Thanks everyone for your input.
I've decided to go with premchai21's suggestion which "does math", as opposed to Monky Python suggestion which is exactly what I was looking for (I can hear everyone now... 'Is he nuts?!', 'Has he gone mad?', 'First he asks for 'no math' and then he decides to figure out the exact value of pi!', 'Burn him at the stake!').
Ah, but you see my friends, you have all taught me the error of my ways. Math did not make finding a solution more complicated. My problem was that I was not "seeing" the proper math and thus thought it would be a messy solution. In fact, because I cannot trust the intergrity of the data handed to me, math makes my inital problem easier to solve. My fear is that $bar may be greater than the number of digits in $foo and thus I would need to add zeros in front of the number ($bar=5 $foo=1500 correct result = .01500). This could be done with reg exp, but my new thinking (IMHO) is that math would be a cleaner solution (I hope i'm not opening a can of worms here).
Thanks again everyone for the learning experience.
| [reply] |