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

Discussion in 'C Programming' started by lloyd, Mar 18, 2011.

  1. lloyd

    lloyd Guest

    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;
    }
    ============================

    >./test

    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?
    lloyd, Mar 18, 2011
    #1
    1. Advertising

  2. lloyd <> writes:

    > 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;
    > }
    > ============================
    >
    >>./test

    > 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

    --
    Ben.
    Ben Bacarisse, Mar 18, 2011
    #2
    1. Advertising

  3. lloyd

    Willem Guest

    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
    Willem, Mar 18, 2011
    #3
  4. lloyd

    Tim Prince Guest

    On 3/18/2011 11:09 AM, lloyd wrote:
    > 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;
    > }
    > ============================
    >
    >> ./test

    > 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.


    --
    Tim Prince
    Tim Prince, Mar 18, 2011
    #4
  5. lloyd

    Tim Prince Guest

    On 3/18/2011 11:09 AM, lloyd wrote:
    > 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;
    > }
    > ============================
    >
    >> ./test

    > 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.


    --
    Tim Prince
    Tim Prince, Mar 18, 2011
    #5
  6. lloyd

    lloyd Guest

    Willem wrote:

    > 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 ?
    lloyd, Mar 18, 2011
    #6
  7. lloyd

    Tim Prince Guest

    On 3/18/2011 12:53 PM, lloyd wrote:
    > Willem wrote:
    >
    >> 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 ?
    >

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

    --
    Tim Prince
    Tim Prince, Mar 18, 2011
    #7
  8. lloyd <> writes:
    > 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.

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    Nokia
    "We must do something. This is something. Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
    Keith Thompson, Mar 18, 2011
    #8
  9. Tim Prince <> writes:

    > On 3/18/2011 12:53 PM, lloyd wrote:
    >> Willem wrote:
    >>
    >>> 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 ?
    >>

    > 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.

    --
    Ben.
    Ben Bacarisse, Mar 18, 2011
    #9
  10. Tim Prince <> writes:
    > On 3/18/2011 12:53 PM, lloyd wrote:
    >> Willem wrote:
    >>
    >>> 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 ?
    >>

    > 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).

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    Nokia
    "We must do something. This is something. Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
    Keith Thompson, Mar 18, 2011
    #10
  11. On Mar 18, 11:28 pm, Keith Thompson <> wrote:
    > Tim Prince <> writes:
    >
    > > 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).
    >

    But it's getting like ASCII. In theory, just one encoding scheme
    amongst many, in practice, universal.
    Malcolm McLean, Mar 19, 2011
    #11
  12. In article <>,
    Malcolm McLean <> wrote:
    >On Mar 18, 11:28 pm, Keith Thompson <> wrote:
    >> Tim Prince <> writes:
    >>
    >> > 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).
    >>

    >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)
    Kenny McCormack, Mar 19, 2011
    #12
  13. lloyd

    James Kuyper Guest

    On 03/19/2011 05:37 AM, Malcolm McLean wrote:
    > On Mar 18, 11:28�pm, Keith Thompson<> wrote:
    >> Tim Prince<> writes:
    >>
    >>> 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).
    >>

    > 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.

    --
    James Kuyper
    James Kuyper, Mar 19, 2011
    #13
  14. lloyd

    iC and iC++ Guest

    On Mar 18, 2:09 pm, lloyd <> wrote:
    > 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;}
    >
    > ============================
    >
    > >./test

    >
    > 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.
    iC and iC++, Mar 19, 2011
    #14
  15. lloyd

    Geoff Guest

    On Fri, 18 Mar 2011 11:09:28 -0700 (PDT), lloyd
    <> wrote:

    >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;
    >}
    >============================
    >
    >>./test

    >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;
    }
    Geoff, Mar 20, 2011
    #15
  16. In article <im28ge$4us$-september.org>,
    James Kuyper <> wrote:

    > On 03/19/2011 05:37 AM, Malcolm McLean wrote:
    > > On Mar 18, 11:28?pm, Keith Thompson<> wrote:
    > >> Tim Prince<> writes:
    > >>
    > >>> 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).
    > >>

    > > 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.


    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.

    --
    Michael Press
    Michael Press, Mar 21, 2011
    #16
  17. lloyd

    James Kuyper Guest

    On 03/21/2011 12:04 AM, Michael Press wrote:
    > In article<im28ge$4us$-september.org>,
    > James Kuyper<> wrote:
    >
    >> On 03/19/2011 05:37 AM, Malcolm McLean wrote:

    ....
    >>> 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.

    >
    > 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?
    --
    James Kuyper
    James Kuyper, Mar 21, 2011
    #17
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. bd
    Replies:
    0
    Views:
    608
  2. Replies:
    10
    Views:
    1,222
    Big K
    Feb 2, 2005
  3. Carsten Fuchs
    Replies:
    45
    Views:
    1,509
    James Kanze
    Oct 8, 2009
  4. rantingrick
    Replies:
    44
    Views:
    1,177
    Peter Pearson
    Jul 13, 2010
  5. Isaac Won
    Replies:
    9
    Views:
    354
    Ulrich Eckhardt
    Mar 4, 2013
Loading...

Share This Page