Float problem
Filip Konvička
filip.konvicka at logis.cz
Mon Jun 25 14:00:16 CEST 2007
Hi Torsten,
> Dear all,
>
> I came across some behaviour of floats in a C-style loop which I don't
> understand. In the following test function, I want to collect N floats
> between 0.0 and 1.0, including these boundaries.
>
> fun {SampleInts N}
> Incr = 1.0 / {IntToFloat N-1}
> in
> for X in 0.0;X=<1.0;X+Incr
> collect:C
> do {C X}
> end
> end
>
> {SampleInts 10}
> % -> [0.0, 0.11111 ... 0.88889]
>
> However, the last value (i.e., 1.0) is left out in the result. It appears that
> this last value is actually already slightly greater then 1.0 (due to the
> typical float problems I assume). So, I came up with the following hack,
> where I test against a number which is slightly larger than 1.0.
>
Sure, you would get similar results in C.
> fun {SampleInts2 N}
> Incr = 1.0 / {IntToFloat N-1}
> Max = 1.0+1.0E~13
> in
> for X in 0.0;X=<Max;X+Incr
> collect:C
> do {C X}
> end
> end
>
> {SampleInts2 10}
> % -> [0.0, 0.11111 ... 0.88889 1.0]
>
> Now, the surprising aspect for me is that with such a hack, the last element
> is indeed displayed as 1.0 and not anything slightly larger.
>
> I should perhaps mention that I am porting some Common Lisp program to Oz,
> where the equivalent of the first function works totally fine in Lisp. So, is
> there some magic at the Lisp side going on (Lisp supports ratios, not only
> floats), and the Oz behaviour is the usual thing to happen when you use
> floats? Any idea, how to write a version of SampleInts which works for an
> arbitrary high N?
>
You could try some custom rounding of the sum in each iteration if your
increments are just slightly imprecise, like rouding to 6 valid digits
when you're using <0, 1> increments. Of course, this can not be used in
general, you have to think of how many numbers you have and if rounding
does not corrupt your sequence...
A nice alternative is using XRI, where each float constant can be
represented by an interval containing the (precise) number. So you get
interval arithmetics and you can test whether "1." is in or out of the
resulting interval.
Yet another alternative is expressing each element of your sequence like
Ei = ((Mx - Mn) * I / N) + Mn
BTW, did you notice that you're shadowing the built-in Max variable? See
ozc --warnshadow
Best,
Filip
More information about the mozart-users
mailing list