For reasons described in excruciating detail in the document already cited, there will be errors, but according to a quick test on my system, they are nowhere near as large as you claim:
$ perl -e '$x=0.001; $sum += $x for 1..40; printf "%.20f", $sum/40'
0.00100000000000000067
Looks more like a "one off" error to me (i.e. summing over one more than you divide by):
$ perl -e '$x=0.001; $sum += $x for 0..40; printf "%.20f", $sum/40'
0.00102500000000000074
| [reply] [d/l] [select] |
:D Looks like two separate off-by-one error (OBOE) errors to me :)
First you start with non-zero and add 40 times (one too many), then you start with non-zero and add 41 times (one too many twice).
If you start with non-zero you need to add only 39 times, or start with zero and add 40 times :)
In short
perl -MData::Dump -e " @f = map { 0.001 } 1 .. 40; dd\@f; $o = 0; for(
+@f){ dd $o+=$_; } dd int @f; dd $o/int(@f); "
perl -MData::Dump -e " $o = 0; for(1 .. 40){ dd $o+= 0.001; } dd $o/4
+0; "
It didn't dawn on me to check wickedjesters (or your) math until ww raised the quesiton | [reply] [d/l] |
$ perl -le '$sum += 1 for 1..40; print $sum'
40
So where is the problem? I think you overlooked that $sum is initially undef/zero. | [reply] [d/l] [select] |
wickedjester:
You don't show your code, but I'm pretty sure you're not doing what you think you're doing. Specifically, I believe you're adding 41 copies of 0.001, as that's the only way I can reproduce your results:
$ cat t.pl
#!/usr/bin/perl
my @a = (0.001) x 41;
my $sum=0;
$sum += $_ for @a;
print "Avg: ", $sum/40, "\n";
$ perl t.pl
Avg: 0.001025
...roboticus
When your only tool is a hammer, all problems look like your thumb. | [reply] [d/l] |
- sprintf
- perlfaq4 Why am I getting long decimals (eg, 19.9499999999999) instead of the numbers I should be getting (eg, 19.95)?
| [reply] |
As I see it, you're performing addition, division, and averaging. You can dispense with the addition, division, and
since you have an array, just do an average of the elements.
#!/usr/bin/perl -l
use strict;
use warnings;
use Array::Average;
print average(
0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001,
0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001,
0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001,
0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001,
);
Returns: 0.001 | [reply] [d/l] |
if (@data) {
my $sum=0;
$sum+=$_ foreach @data;
return $sum/scalar(@data);
} else {
return undef;
}
Anyhow, as has already been pointed out, the OP's problem has likely nothing whatsoever to do with those general floating point issues, but is presumably simply the result of having computed the sum incorrectly. | [reply] [d/l] |