is it a bad idea to index a for loop with a float?

L

lloyd

I find this odd:

===========================
#include <stdio.h>

int main(int argc, char *argv[])
{
float phi, phi_start, phi_end, phi_step;

phi_start = 0.0;
phi_end = 3.0;
phi_step = 0.2;

for (phi = phi_start; phi<= phi_end; phi=phi+phi_step)
printf("phi = %.15f\n",phi);

return 0;
}
============================
phi = 0.000000000000000
phi = 0.200000002980232
phi = 0.400000005960464
phi = 0.600000023841858
phi = 0.800000011920929
phi = 1.000000000000000
phi = 1.200000047683716
phi = 1.400000095367432
phi = 1.600000143051147
phi = 1.800000190734863
phi = 2.000000238418579
phi = 2.200000286102295
phi = 2.400000333786011
phi = 2.600000381469727
phi = 2.800000429153442
What the heck is up? It's fine if I set phi_step to 0.25 (I mean you
get the expected values of phi.) The bad digits are further away from
the decimal point if I use doubles instead, but they still are enough
to break a test of "if (phi==floor(phi))" which I want to use so I can
flag cases where phi is an integer. Currently I'm using "if (phi -
floor(phi) < epsilon)" to catch those cases, and I've had to change my
loop condition to "phi<=phi_end+epsilon". What's the usual practice?
 
B

Ben Bacarisse

lloyd said:
I find this odd:

===========================
#include <stdio.h>

int main(int argc, char *argv[])
{
float phi, phi_start, phi_end, phi_step;

phi_start = 0.0;
phi_end = 3.0;
phi_step = 0.2;

for (phi = phi_start; phi<= phi_end; phi=phi+phi_step)
printf("phi = %.15f\n",phi);

return 0;
}
============================
phi = 0.000000000000000
phi = 0.200000002980232
phi = 0.400000005960464
phi = 0.600000023841858
phi = 0.800000011920929
phi = 1.000000000000000
phi = 1.200000047683716
phi = 1.400000095367432
phi = 1.600000143051147
phi = 1.800000190734863
phi = 2.000000238418579
phi = 2.200000286102295
phi = 2.400000333786011
phi = 2.600000381469727
phi = 2.800000429153442
What the heck is up? It's fine if I set phi_step to 0.25 (I mean you
get the expected values of phi.) The bad digits are further away from
the decimal point if I use doubles instead, but they still are enough
to break a test of "if (phi==floor(phi))" which I want to use so I can
flag cases where phi is an integer. Currently I'm using "if (phi -
floor(phi) < epsilon)" to catch those cases, and I've had to change my
loop condition to "phi<=phi_end+epsilon". What's the usual practice?

Float variables usually only have a digits of precision, but one thing
is certain: if they use base 2 (thay almost always do these days),
neither they nor doubles can represent the step you are using (0.2)
without error so you will have to cope with that.

The usual practise for for loops is to use integers and to calculate phi
from the integer loop count. If the loops is not really a counted loop,
use a while loop. You'll have to decide based on the problem domain
when the loops stops.

You will have a lot of trouble detecting an integer except under very
favourable conditions.

You might like to have a read of http://cr.yp.to/2005-590/goldberg.pdf
 
W

Willem

lloyd wrote:
) What the heck is up? It's fine if I set phi_step to 0.25 (I mean you
) get the expected values of phi.) The bad digits are further away from
) the decimal point if I use doubles instead, but they still are enough
) to break a test of "if (phi==floor(phi))" which I want to use so I can
) flag cases where phi is an integer. Currently I'm using "if (phi -
) floor(phi) < epsilon)" to catch those cases, and I've had to change my
) loop condition to "phi<=phi_end+epsilon". What's the usual practice?

The usual practise is to try to not rely on equality with floats,
and to use epsilon when you do want to check for equality.

What you did, basically.


SaSW, Willem
--
Disclaimer: I am in no way responsible for any of the statements
made in the above text. For all I know I might be
drugged or something..
No I'm not paranoid. You all think I'm paranoid, don't you !
#EOT
 
T

Tim Prince

I find this odd:

===========================
#include<stdio.h>

int main(int argc, char *argv[])
{
float phi, phi_start, phi_end, phi_step;

phi_start = 0.0;
phi_end = 3.0;
phi_step = 0.2;

for (phi = phi_start; phi<= phi_end; phi=phi+phi_step)
printf("phi = %.15f\n",phi);

return 0;
}
============================
phi = 0.000000000000000
phi = 0.200000002980232


Besides exposing the issues of mixing float and double, this is likely
to stop optimizations which depend on a counted loop. You shouldn't
consider this as odd, but as a natural consequence.
 
T

Tim Prince

I find this odd:

===========================
#include<stdio.h>

int main(int argc, char *argv[])
{
float phi, phi_start, phi_end, phi_step;

phi_start = 0.0;
phi_end = 3.0;
phi_step = 0.2;

for (phi = phi_start; phi<= phi_end; phi=phi+phi_step)
printf("phi = %.15f\n",phi);

return 0;
}
============================
phi = 0.000000000000000
phi = 0.200000002980232


Besides exposing the issues of mixing float and double, this is likely
to stop optimizations which depend on a counted loop. You shouldn't
consider this as odd, but as a natural consequence.
 
L

lloyd

Willem said:
The usual practise is to try to not rely on equality with floats,
and to use epsilon when you do want to check for equality.

Thanks Willem, and Ben for the link to the impressively thorough
document, and everyone else. If I continue to check integerness with
epsilon, can I rely on the inaccuracy being on the positive side, so
that I just need to check whether phi - floor(phi) < epsilon, and not
abs(phi-floor(phi))<epsilon ?
 
T

Tim Prince

Thanks Willem, and Ben for the link to the impressively thorough
document, and everyone else. If I continue to check integerness with
epsilon, can I rely on the inaccuracy being on the positive side, so
that I just need to check whether phi - floor(phi)< epsilon, and not
abs(phi-floor(phi))<epsilon ?
The rounding from double to float should be IEEE nearest, so whether it
is up or down depends on the value.
 
K

Keith Thompson

lloyd said:
I find this odd:

===========================
#include <stdio.h>

int main(int argc, char *argv[])
{
float phi, phi_start, phi_end, phi_step;

phi_start = 0.0;
phi_end = 3.0;
phi_step = 0.2;

for (phi = phi_start; phi<= phi_end; phi=phi+phi_step)
printf("phi = %.15f\n",phi);

return 0;
}
============================
[...]

http://www.c-faq.com/fp/index.html, section 14.
 
B

Ben Bacarisse

Tim Prince said:
The rounding from double to float should be IEEE nearest, so whether
it is up or down depends on the value.

Also, the OP calculated phi in a loop. In general the errors will
accumulate. That's why I suggested using an int for the loop control:
calculating phi from that can ensure you get the best rounding.
However, I suspect the simple loop was an example and a more complex one
is envisaged.
 
K

Keith Thompson

Tim Prince said:
The rounding from double to float should be IEEE nearest, so whether it
is up or down depends on the value.

Whether the implementation uses IEEE or not is implementation-specific
(possibly implementation-defined).
 
M

Malcolm McLean

Whether the implementation uses IEEE or not is implementation-specific
(possibly implementation-defined).
But it's getting like ASCII. In theory, just one encoding scheme
amongst many, in practice, universal.
 
K

Kenny McCormack

But it's getting like ASCII. In theory, just one encoding scheme
amongst many, in practice, universal.

And VT100 (or thereabouts), aka "ANSI", in the world of terminals (and
"terminal emulators").

And MS Windows in the world of desktop operating systems (Yeah, that was
flamebait...)

--
Religion is regarded by the common people as true,
by the wise as foolish,
and by the rulers as useful.

(Seneca the Younger, 65 AD)
 
J

James Kuyper

But it's getting like ASCII. In theory, just one encoding scheme
amongst many, in practice, universal.

ASCII universality would be a useful feature only if you refer very
specifically to a single encoding; presumably US-ASCII? However, while
variants and descendants of ASCII are legion, US-ASCII itself is very
far from being universal. National variants of ASCII, many different
encodings for Asian characters, and various Unicode encodings are all
quite common. Your own message's headers indicate that it was encoded in
ISO 8859-1, though I don't know whether it makes any use of the
encodings that are in ISO 8859-1, and not in ASCII.
 
I

iC and iC++

I find this odd:

===========================
#include <stdio.h>

int main(int argc, char *argv[])
{
        float phi, phi_start, phi_end, phi_step;

        phi_start = 0.0;
        phi_end = 3.0;
        phi_step = 0.2;

        for (phi = phi_start; phi<= phi_end; phi=phi+phi_step)
                printf("phi = %.15f\n",phi);

        return 0;}

============================

phi = 0.000000000000000
phi = 0.200000002980232
phi = 0.400000005960464
phi = 0.600000023841858
phi = 0.800000011920929
phi = 1.000000000000000
phi = 1.200000047683716
phi = 1.400000095367432
phi = 1.600000143051147
phi = 1.800000190734863
phi = 2.000000238418579
phi = 2.200000286102295
phi = 2.400000333786011
phi = 2.600000381469727
phi = 2.800000429153442



What the heck is up? It's fine if I set phi_step to 0.25 (I mean you
get the expected values of phi.) The bad digits are further away from
the decimal point if I use doubles instead, but they still are enough
to break a test of "if (phi==floor(phi))" which I want to use so I can
flag cases where phi is an integer. Currently I'm using "if (phi -
floor(phi) < epsilon)" to catch those cases, and I've had to change my
loop condition to "phi<=phi_end+epsilon". What's the usual practice?

The epsilon method is the standard way to go. In all of computation,
you have to approximate around a small number (epsilon) and accept
with values in the neighborhood of epsilon.
 
G

Geoff

I find this odd:

===========================
#include <stdio.h>

int main(int argc, char *argv[])
{
float phi, phi_start, phi_end, phi_step;

phi_start = 0.0;
phi_end = 3.0;
phi_step = 0.2;

for (phi = phi_start; phi<= phi_end; phi=phi+phi_step)
printf("phi = %.15f\n",phi);

return 0;
}
============================
phi = 0.000000000000000
phi = 0.200000002980232
phi = 0.400000005960464
phi = 0.600000023841858
phi = 0.800000011920929
phi = 1.000000000000000
phi = 1.200000047683716
phi = 1.400000095367432
phi = 1.600000143051147
phi = 1.800000190734863
phi = 2.000000238418579
phi = 2.200000286102295
phi = 2.400000333786011
phi = 2.600000381469727
phi = 2.800000429153442
What the heck is up? It's fine if I set phi_step to 0.25 (I mean you
get the expected values of phi.) The bad digits are further away from
's not "bad" per se to the decimal point if I use doubles instead, but they still are enough
to break a test of "if (phi==floor(phi))" which I want to use so I can
flag cases where phi is an integer. Currently I'm using "if (phi -
floor(phi) < epsilon)" to catch those cases, and I've had to change my
loop condition to "phi<=phi_end+epsilon". What's the usual practice?

As others have pointed out your choice of phi_step can influence the
accuracy of your results due to implementation of floating point
representation. You are also computing phi such that the error is
additive when the representation can't be exact. You've created a
scenario where the error is proportional to epsilon*phi.

Better to use an integer in the loop control and multiply phi_step by
it to compute phi. This will keep your value of phi closer to the
actual value. This way, your values are off by step*i*epsilon but they
trend back when the representation is exact. This will work as you
expect for larger data sets.

#include <stdio.h>
#include <float.h>
#include <math.h>

int main(int argc, char *argv[])
{
float phi, phi_start, phi_end, phi_step;
int i, count;

phi_start = 0.0;
phi_end = 3.0;
phi_step = 0.2;

count = floor( (phi_end-phi_start) / phi_step + FLT_EPSILON );

phi = phi_start;
for(i=0; i <= count; i++) {
phi = i*phi_step;
printf("phi = %.15f\n", phi);
}
return 0;
}

phi = 0.000000000000000
phi = 0.200000002980232
phi = 0.400000005960464
phi = 0.600000023841858
phi = 0.800000011920929
phi = 1.000000000000000
phi = 1.200000047683716
phi = 1.399999976158142
phi = 1.600000023841858
phi = 1.800000071525574
phi = 2.000000000000000
phi = 2.200000047683716
phi = 2.400000095367432
phi = 2.600000143051148
phi = 2.799999952316284

For all of the effort to surround the for loop, you could just as well
use a while loop:

#include <stdio.h>
#include <float.h>
#include <math.h>

int main(int argc, char *argv[])
{
float phi, phi_start, phi_end, phi_step;
int i = 0;
int count;

phi_start = 0.0;
phi_end = 3.0;
phi_step = 0.2;

count = floor( (phi_end-phi_start) / phi_step + FLT_EPSILON );

phi = phi_start;
while(i <= count) {
phi = i*phi_step;
printf("phi = %.15f\n", phi);
i++;
}
return 0;
}
 
M

Michael Press

James Kuyper said:
ASCII universality would be a useful feature only if you refer very
specifically to a single encoding; presumably US-ASCII? However, while
variants and descendants of ASCII are legion, US-ASCII itself is very
far from being universal. National variants of ASCII, many different
encodings for Asian characters, and various Unicode encodings are all
quite common. Your own message's headers indicate that it was encoded in
ISO 8859-1, though I don't know whether it makes any use of the
encodings that are in ISO 8859-1, and not in ASCII.

American Standard Code for Information Interchange
is graven in stone.

USA Standard Code for Information Interchange,
USAS X3.4-1967, United States of America Standards Institute, July 7, 1967

Extensions are not ASCII. Expressions such as "US-ASCII"
and "extended ASCII" are not official nomenclature.
 
J

James Kuyper

American Standard Code for Information Interchange
is graven in stone.
USA Standard Code for Information Interchange,
USAS X3.4-1967, United States of America Standards Institute, July 7, 1967

What about ASA X3.4-1963? USASI X3.4-1968? ANSI X3.4-1977? or ANSI
X3.4-1986? Wikipedia does not describe the differences between those
versions: Did they really just re-issue the same exact standard 5 times
with 5 different names without any modification to it's provisions? If
not, that doesn't seem very "graven in stone" to me.
Extensions are not ASCII. ...

I referred to variants and descendants of ASCII, not extensions. I made
no suggestion that the variants and descendants were themselves ASCII. I
was giving Malcolm the benefit of the doubt, by considering the
possibility that he was using "ASCII" in a more inclusive sense that
includes those variants and descendants, because his comment would make
more sense that way.

Without out such inclusiveness, it would be even less accurate to refer
to ISO 8859-1, which Malcolm used in that message, or UTF-8, which you
used in this one, as ASCII. That tends to argue against his claims for
the near-universality of ASCII.
... Expressions such as "US-ASCII"
and "extended ASCII" are not official nomenclature.

As I pointed out above, I did not use the phrase "extended ASCII". As
far as "US-ASCII" is concerned, according to wikipedia
<http://en.wikipedia.org/wiki/ASCII> (apply all the usual reservations
about the reliability of that source):

"US-ASCII is the Internet Assigned Numbers Authority (IANA) preferred
charset name for ASCII."
....
"A June 1992 RFC[35] and the Internet Assigned Numbers Authority
registry of character sets[10] recognize the following case-insensitive
aliases for ASCII as suitable for use on the Internet:"
....
"US-ASCII (preferred MIME name)"
....
"Of these, the IANA encourages use of the name "US-ASCII" for Internet
uses of ASCII."
....
'10. ^ a b c Internet Assigned Numbers Authority (May 14, 2007).
"Character Sets". Accessed 2008-04-14.'

"35. ^ RFC 1345 (June 1992)."

Those two footnotes contain links to
<http://www.iana.org/assignments/character-sets> and
<http://tools.ietf.org/html/rfc1345>. The web sites for those DNS names
claim to be the official web sites for the corresponding organizations.
Those documents appear to support Wikipedia's comments.

So US-ASCII does seem to be the "official" (whatever that means)
designation for ASCII in at least some contexts. Does that article
require correction?
 

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
473,769
Messages
2,569,582
Members
45,057
Latest member
KetoBeezACVGummies

Latest Threads

Top