Simple loop error

D

dan

The following code does not print 1, because it goes from 0.81 to
0.820000000000001. This is quite a big deal isn't it?

for ($i = 0; $i <= 1; $i += 0.01) {
print "$i\n";
}

Using perl v5.10.0
 
D

Dr.Ruud

dan said:
The following code does not print 1, because it goes from 0.81 to
0.820000000000001. This is quite a big deal isn't it?

for ($i = 0; $i <= 1; $i += 0.01) {
print "$i\n";
}

Using perl v5.10.0

perldoc -q 999
 
D

Dr.Ruud

dan said:
Dr.Ruud:

So something like this is necessary?

for ($i = 0; $i <= 1; $i = sprintf("%.2f", $i + 0.01)) {
print "$i\n";
}

For integer behavior, it is necessary to use integers.

for my $i (24 .. 42) {
printf "%.2f", $i / 100;
}
 
J

Jürgen Exner

dan said:
The following code does not print 1, because it goes from 0.81 to
0.820000000000001. This is quite a big deal isn't it?

No, only for those, who missed the first class in Basics of Computer
Numerics:

"Thou shalt not test floating point numbers for equality"

I'm still amazed how often this topic comes up. See 'perldoc -q 999' for
a very brief introduction why this is a bad idea and otherwise check the
archives for previous discussions of this topic or a see a book about
Computer Numerics or even basic introduction about about the difference
between decimal and binary numbers.

jue
 
D

dan

The following code does not print 1, because it goes from 0.81 to
0.820000000000001. This is quite a big deal isn't it?

for ($i = 0; $i <= 1; $i += 0.01) {
print "$i\n";
}

Using perl v5.10.0

Over time I have posted a few questions to this newsgroup, and have been
impressed with the level of expertise and helpfulness of the anwers.
However with respect to this issue, a hint of hysteria has crept in to
some of the responses. Probably because to the uninitiated, it appears
that floating-point arithmetic is simply broken.
 
J

Jürgen Exner

dan said:
Over time I have posted a few questions to this newsgroup, and have been
impressed with the level of expertise and helpfulness of the anwers.
However with respect to this issue, a hint of hysteria has crept in to
some of the responses. Probably because to the uninitiated, it appears
that floating-point arithmetic is simply broken.

More like because it is the same old broken record played a gazillion
times before. And because if you are writing any code you REALLY should
know that computers are using binary arithmetic, not decimal.

jue
 
S

sln

Over time I have posted a few questions to this newsgroup, and have been
impressed with the level of expertise and helpfulness of the anwers.
However with respect to this issue, a hint of hysteria has crept in to
some of the responses. Probably because to the uninitiated, it appears
that floating-point arithmetic is simply broken.

Yes, a little hysterical.

IMO there is nothing wrong with comparing floating point numbers
and testing for equality. I would not do it any other way.

As long as you realize that you are actually comparing binary conversions
(base 2) of decimal numbars. You may get equality once in a while, however,
it may not be translatable into the equality of the original decimal.

-sln
------------
use strict;
use warnings;


my @tests = (
1.0 , 2.0 , 0.1 ,
1.0 , 10.0 , 1.0 ,
.0 , .09 , .01 ,
.7 , 1.0 , .01 ,
);

while (my ($start,$limit,$incr) = splice (@tests,0,3))
{
my $curval = $start;
my $i = $start;

for ($i = $start; $i <= $limit; $i += $incr) {
if ($curval != $i) {
print "Whoa!!, not equal ... ";
}
$curval += $incr;
print sprintf("%f", $i), "\t$i\n";
if ($i == $limit) {
print "Value equals limit ($i, $limit)\n";
}
}
print "$i\n\n";

}

__END__

1.000000 1
1.100000 1.1
1.200000 1.2
1.300000 1.3
1.400000 1.4
1.500000 1.5
1.600000 1.6
1.700000 1.7
1.800000 1.8
1.900000 1.9
2

1.000000 1
2.000000 2
3.000000 3
4.000000 4
5.000000 5
6.000000 6
7.000000 7
8.000000 8
9.000000 9
10.000000 10
Value equals limit (10, 10)
11

0.000000 0
0.010000 0.01
0.020000 0.02
0.030000 0.03
0.040000 0.04
0.050000 0.05
0.060000 0.06
0.070000 0.07
0.080000 0.08
0.090000 0.09
Value equals limit (0.09, 0.09)
0.1

0.700000 0.7
0.710000 0.71
0.720000 0.72
0.730000 0.73
0.740000 0.74
0.750000 0.75
0.760000 0.76
0.770000 0.77
0.780000 0.78
0.790000 0.79
0.800000 0.8
0.810000 0.81
0.820000 0.82
0.830000 0.83
0.840000 0.84
0.850000 0.85
0.860000 0.86
0.870000 0.87
0.880000 0.88
0.890000 0.89
0.900000 0.9
0.910000 0.91
0.920000 0.92
0.930000 0.93
0.940000 0.94
0.950000 0.95
0.960000 0.96
0.970000 0.97
0.980000 0.98
0.990000 0.99
1
 
P

Peter J. Holzer

Wrong. It goes from 0.8100000000000004973799150320701301097869873046875
to 0.8200000000000005062616992290713824331760406494140625

Change this to
printf("%.60f\n", $i);
to see what's going on.
So something like this is necessary?

for ($i = 0; $i <= 1; $i = sprintf("%.2f", $i + 0.01)) {
print "$i\n";
}

No. Use something like this:

for (my $ii = 0; $ii <= 100; $ii++)) {
my $i = $ii / 100;
print "$i\n";
}

or more idiomatically:

for my $ii (0 .. 100) {
my $i = $ii / 100;
print "$i\n";
}

More generally, if you want to want to go from $begin to $end in $steps
steps, use:

for my $i (0 .. $steps) {
my $value = $begin + ($end - $begin) * $i / $steps;
...
}

As a general rule, with floating point arithmetic, try to avoid repeated
addition and subtraction.

hp
 
P

Peter J. Holzer

It is ancient knowledge that one does not compare IEEE Standard 754
floating point numbers for equality.

I'd like to call this an ancient myth instead of ancient knowledge. It's
complete and utter bullshit. If you need to know whether two numbers are
equal, use ==. If you just need to know whether two numbers are similar,
compare the difference to some epsilon. That doesn't have anything to do
with whether you are using IEEE Standard 754 floating point numbers or
not. It doesn't even have anything to do with whether you are using
floating point - the same is true for fixed point or even integers. It's
just that FP numbers are typically used for values for which the exact
value is not only irrelevant but inherently unknowable, so a comparison
for equality doesn't make sense (or the other way around: If you need to
represent exact values, FP is probably the wrong type).

And it's irrelevant in this case because he *didn't* compare for
equality.

hp
 
B

Bradley K. Sherman

...

And it's irrelevant in this case because he *didn't* compare for
equality.

Yes, he did, and then complained about it. See original
message.

--bks
 
M

Mart van de Wege

dan said:
Over time I have posted a few questions to this newsgroup, and have been
impressed with the level of expertise and helpfulness of the anwers.
However with respect to this issue, a hint of hysteria has crept in to
some of the responses. Probably because to the uninitiated, it appears
that floating-point arithmetic is simply broken.

Here's the problem: when it comes to floating point math and it's
implementation in an inherently fixed-point platform such as a digital
computer, a programmer is not supposed to be uninitiated.

Mart
 
B

Bradley K. Sherman

No; "<=" is not an equality operator.


Oddly enough, you quoted the relevant text from the original message,
which clearly reads "<=".

The original message begins in this manner:and the reason it does not is because of the equality operator.

--bks
 
J

jl_post

The following code does not print 1, because it goes from 0.81 to
0.820000000000001. This is quite a big deal isn't it?

for ($i = 0; $i <= 1; $i += 0.01) {
  print "$i\n";
}


This reminds me of a post a while back where someone discovered
that:

int(2.55 * 100) == 254 # not 255 !

I responded with an explanation as to why that was happening. While I
could explain your issue in similar words, to avoid repeating myself
I'll just post a link to my reply:

http://groups.google.com/group/comp.lang.perl.misc/msg/030893a4492dfbd9?dmode=source

(In case that link ever grows stale, the reply was posted to
comp.lang.perl.misc, had a subject of "Re: perl floating point
problem", and was posted on Thu, 08 Nov 2007 14:26:10 -0800.)

I hope this helps, Dan.

-- Jean-Luc
 
B

Bradley K. Sherman

That much is correct.


Which part of 'No; "<=" is not an equality operator.' don't you
understand?

The = part. That's why we say "less than or equal". It's
equivalent to ! ( i > j) which is also two operations, but
in that case, neither is equality. HTH.

--bks
 
B

Bradley K. Sherman

Shmuel (Seymour J.) Metz said:
...
In the first case there is only one operator and it is *not* an equality
operator any more than it is an assignment operator.
...

So, in short, on your planet, in Perl, '<=' does not test
for equality, while here on Earth, it does.

--bks
 
J

Jochen Lehmeier

So, in short, on your planet, in Perl, '<=' does not test
for equality, while here on Earth, it does.

Does not.

At least not in a way that matters for floating point.

Let's get down to machine level: say we have a constant float $a, and a
value $b which is the result of a lengthy calculation. $a is exact,
because we wrote it down as a constant in the source code, and took good
care that it is a number that can actually be represented in binary
floating point without any error. $a=1.0 will do fine, but it does not
matter. Being floating point numbers, $b will most definitely not be
exact, but will shift around the theoretical target value by some margin
of error.

Let us assume a benign algorithm, constructed by a wise mathemagician,
which guarantees that $b will always end up in a well known interval
around $a. Let us assume that this interval is simply $a itself surrounded
by the 4 next larger or smaller numbers that can be represented as IEEE
floating point numbers (this assumption is conceptually well defined since
IEEE FP numbers are rather strict in this respect, i.e. every number that
can actually be represented in IEEE FP has exactly one, and not several,
legal representations). Let's also assume that each of those 9 results is
as likely as the others. Then we can write little diagrams where "*" means
"one of the close neighbours" and "!" means "exactly $a as represented in
IEEE FP". Underneath, we write "1" or "0" depending on the actual result
of the operator.

We do that for each of the operators "==", "<=" and "<":


For $a==$b, expected result TRUE:

* * * * ! * * * *
0 0 0 0 1 0 0 0 0

=> in 8 out of 9 cases, $a == $b will turn out FALSE. (This operator does
not matter to this thought experiment, but is included for fun)


For $a<$b, expected result FALSE:

* * * * ! * * * *
0 0 0 0 0 1 1 1 1

=> in 4 out of 9 cases, "$a < $b" will turn out TRUE.


For $a<=$b, expected result TRUE:

* * * * ! * * * *
0 0 0 0 1 1 1 1 1

=> in 4 out of 9 cases, $a<=$b will turn out FALSE. ..... hey, that's the
same probability!


The important point is the fact that the error percentage for "<" and "<="
is exactly the same. This holds for all cases where you use "<" and "<="
in the context of floating points: you can safely exchange "<" vs. "<=" in
most FP algorithms and it will not matter at all (exceptions, especially
constructed ones, nonwithstanding).

(NB: while not related to the question at hand, this also shows why == and
< / <= are very different beasts for floating point numbers. For larger
amounts of possible values for $b (larger error margin), the error
percentage of == quickly approaches 100%, while the percentage for < and
<= approximates 50%. While the usefulness of == quickly degrades to zero,
the usefulness of < and <= always stay around 50%, even if $b has an
arbitrarily large (!) error interval (centered around $a with equal
distribution, of course). And in the opposite extreme, where $b can take 3
possible values, == is wrong in 2 out of 3 cases, while < and <= are wrong
in 1 out of 3 cases.)

I love math. ;-)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,215
Messages
2,571,113
Members
47,715
Latest member
ReeceTaren

Latest Threads

Top