Re: Rounding error?
by BrowserUk (Patriarch) on Oct 19, 2004 at 01:52 UTC
|
It all comes down to the inexactitude in the way computers represent floating point values. By printing the values out with as many digits of precision as you can, you'll see that one value rounds down, and the other rounds up.
printf "%.17f\n", 40.88050;
40.88049999999999800
printf "%.17f\n", 41.78050;
41.78050000000000400
The (probable) reason that you see a different result from C, is that you are (probably) using single precision (floats) in your C code, whilst Perl uses double precision. (Probably:)
See Re: Re: Re: Bug? 1+1 != 2 for more information.
Examine what is said, not who speaks.
"Efficiency is intelligent laziness." -David Dunham
"Think for yourself!" - Abigail
"Memory, processor, disk in that order on the hardware side. Algorithm, algorithm, algorithm on the code side." - tachyon
| [reply] [d/l] |
|
|
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char * * argv) {
{
double a = 40.88050;
printf("%.3f\n",a);
printf("%.17f\n",a);
a = 41.78050;
printf("%.3f\n",a);
printf("%.17f\n",a);
}
{
float a = 40.88050;
printf("%.3f\n",a);
printf("%.17f\n",a);
a = 41.78050;
printf("%.3f\n",a);
printf("%.17f\n",a);
}
return 0;
}
Prints:
40.880
40.88049999999999784
41.781
41.78050000000000352
40.881
40.88050079345703125
41.780
41.78049850463867188
| [reply] [d/l] [select] |
Re: Rounding error?
by data64 (Chaplain) on Oct 19, 2004 at 01:48 UTC
|
See Does Perl Have a round function ?. It briefly mentions the error in rounding and blames it on the IEEE representation used internally.
Just a tongue-tied, twisted, earth-bound misfit. -- Pink Floyd
| [reply] |
Re: Rounding error?
by pg (Canon) on Oct 19, 2004 at 01:48 UTC
|
This is expected. sprintf does an unbiased rounding, so .5 not always round up.
"C is doing the exact opposite"
Your Perl simply calls the underlying c sprintf. Instead of saying C is doing the opposite, you should just say "my c follows slightly different rule than the c used to compile my Perl."
| [reply] |
Re: Rounding error?
by bmann (Priest) on Oct 19, 2004 at 02:29 UTC
|
my $y = 1.5;
$y = sprintf "%.0f", $y;
my $x = 2.5;
$x = sprintf "%.0f", $x;
print "$y\n$x\n";
Still surprised? As pg says above, perl's rounding is unbiased - rather than rounding every .5 up, it always rounds to even. 1.5 and 2.5 both become 2.
Update: This is not documented. What is documented in perlfaq4 is this: it probably pays not to trust whichever system rounding is being used by Perl, but to instead implement the rounding function you need yourself.
I downloaded 5.6.1 from AS, and as ikegami says below it rounds 1.5 to 2 and 2.5 up to 3. Oh, and this all has nothing to do with the original question ;) | [reply] [d/l] |
|
|
This rule has its formal name, called "round to even".
Update
Sorry, I didn't notice that bmann already mentioned this term in his post ;-)
Update 2
To support what bmann said below, I tried this c code, regardless whether I define i as float or double, the round to even rule does apply. The c compiler I used is borland c 5.5, my PC is windows XP.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char * * argv) {
for (double i=0; i < 10; i += .5) {
printf("%.1f: %.0f\n", i, i);
}
return 0;
}
| [reply] [d/l] |
|
|
I'm surprised by your comment, not by the output, seeing as that prints 2 and 3 for me.
ActivePerl 5.6.1
ActivePerl 5.8.0
If it did give 2 for both, I'd be asking: "Can someone explain the sense of a rounding where the following prints 19, not 20:"
$sum += sprintf("%.0f", $_/10) foreach (0..19);
print($sum, $/);
| [reply] [d/l] |
|
|
That's strange. Here's the version of perl I ran it on originally
C:\>perl -v
This is perl, v5.8.4 built for MSWin32-x86-multi-thread
(with 3 registered patches, see perl -V for more detail)
Copyright 1987-2004, Larry Wall
Binary build 810 provided by ActiveState Corp.
I just ran it on 5.8.4 on debian - then 5.00503, 5.8.2 and 5.8.5 on FreeBSD. All versions round both 1.5 and 2.5 to 2
Does anyone else get something different?
for ($i=0;$i<10;$i+=.5){
printf "%.1f: %.0f\n", $i, $i;
}
__END__
Output:
0.0: 0
0.5: 0
1.0: 1
1.5: 2
2.0: 2
2.5: 2
3.0: 3
3.5: 4
4.0: 4
4.5: 4
5.0: 5
5.5: 6
6.0: 6
6.5: 6
7.0: 7
7.5: 8
8.0: 8
8.5: 8
9.0: 9
9.5: 10
| [reply] [d/l] [select] |
Re: Rounding error? (it's normal)
by grinder (Bishop) on Oct 19, 2004 at 09:13 UTC
|
| [reply] |
Re: Rounding error?
by TedPride (Priest) on Oct 19, 2004 at 12:56 UTC
|
The easiest thing to do is round the number yourself and then sprintf:
printf("%.3f\n", int($_*1000+.5)/1000) for <DATA>;
__DATA__
40.88061
41.78050
42.24033
44.4
| [reply] [d/l] |