Strange float / tuple problem

classic Classic list List threaded Threaded
9 messages Options
Reply | Threaded
Open this post in threaded view
|

Strange float / tuple problem

Donald Steven
Hi all,

I'm having a problem I don't understand.

I'm creating some X and Y coordinates in a program.  When I print them
via:    io:format("~nX: ~f   Y: ~f ", [X,Y]),

I get:    X: 0.000000   Y: -5.000000

which is correct.

======

When I make a tuple of them, add them to a list and print them via:    
io:format("~nL: ~p~n", [L]),

I get:    {4.440892098500626e-16,-5.0},

which is very strange.  There are many other {X,Y} tuples in the list
and all of them are correct, including earlier values of X which were
also 0.0 and print correctly as 0.0.

=====

Your thoughts would be very welcome.

Thanks.

Don
_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: Strange float / tuple problem

zxq9-2
On 2016年6月3日 金曜日 23:11:16 Donald Steven wrote:

> Hi all,
>
> I'm having a problem I don't understand.
>
> I'm creating some X and Y coordinates in a program.  When I print them
> via:    io:format("~nX: ~f   Y: ~f ", [X,Y]),
>
> I get:    X: 0.000000   Y: -5.000000
>
> which is correct.
>
> ======
>
> When I make a tuple of them, add them to a list and print them via:    
> io:format("~nL: ~p~n", [L]),
>
> I get:    {4.440892098500626e-16,-5.0},
>
> which is very strange.  There are many other {X,Y} tuples in the list
> and all of them are correct, including earlier values of X which were
> also 0.0 and print correctly as 0.0.
>
> =====
>
> Your thoughts would be very welcome.

Life with floats can be a bit ヽ( ̄д ̄;)ノ

4.440892098500626e-16 == 0.000000000000000444089209850

Pretty darn close to zero.

This 0 is almost certainly the result of some computation and is a
float -- so it is not really a 0, it is some fractional value that
probably cannot be represented in binary directly.

So you have something *very close* to, but not quite, 0.0 left over.

-Craig
_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: Strange float / tuple problem

zxq9-2
On 2016年6月4日 土曜日 12:58:14 zxq9 wrote:

> On 2016年6月3日 金曜日 23:11:16 Donald Steven wrote:
> >
> > I get:    X: 0.000000   Y: -5.000000
> >
> > which is correct.
> >
> > ======
> >
> > When I make a tuple of them, add them to a list and print them via:    
> > io:format("~nL: ~p~n", [L]),
> >
> > I get:    {4.440892098500626e-16,-5.0},
> >
> >
> > Your thoughts would be very welcome.
>
> 4.440892098500626e-16 == 0.000000000000000444089209850

Sorry, I used the wrong symbol. It really should be written as:

4.440892098500626e-16 ≒ 0.000000000000000444089209850

These two are not actually equal -- and actually doing strict
comparisons on float values after computation is a recipe for
intense frustration, so avoid it by deriving an approximation
that is "good enough" first, and then comparing that.
_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: Strange float / tuple problem

Matthias Lang
In reply to this post by Donald Steven
> When I print them via io:format("~nX: ~f   Y: ~f ", [X,Y]),
>
> I get:    X: 0.000000   Y: -5.000000
>
> which is correct.
>
> ======
>
> When I make a tuple of them, add them to a list and print them via:
> io:format("~nL: ~p~n", [L]),
>
> I get:    {4.440892098500626e-16,-5.0},
>
> which is very strange.

Does this clear things up?

  1> X = 4.44E-16.
  4.44e-16
  2> io:fwrite("With twiddle-f: ~f With twiddle-p: ~p\n", [X, X]).
  With twiddle-f: 0.000000 With twiddle-p: 4.44e-16

With the ~f format string, the default precision is 6, which is
why you see 0.000000 in that case.

Here's a site which talks about the general problem without getting
unnecessarily abstract: http://floating-point-gui.de/

Matt
_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: Strange float / tuple problem

Donald Steven
Thanks Matthias and Craig.  I see the issue and I've got a work around.  
BTW, is there a way to truncate a float to a given # of places, not just
0 -- not just a formatted output, but a true truncation?

Don

On 06/04/2016 04:31 AM, Matthias Lang wrote:

>> When I print them via io:format("~nX: ~f   Y: ~f ", [X,Y]),
>>
>> I get:    X: 0.000000   Y: -5.000000
>>
>> which is correct.
>>
>> ======
>>
>> When I make a tuple of them, add them to a list and print them via:
>> io:format("~nL: ~p~n", [L]),
>>
>> I get:    {4.440892098500626e-16,-5.0},
>>
>> which is very strange.
> Does this clear things up?
>
>    1> X = 4.44E-16.
>    4.44e-16
>    2> io:fwrite("With twiddle-f: ~f With twiddle-p: ~p\n", [X, X]).
>    With twiddle-f: 0.000000 With twiddle-p: 4.44e-16
>
> With the ~f format string, the default precision is 6, which is
> why you see 0.000000 in that case.
>
> Here's a site which talks about the general problem without getting
> unnecessarily abstract: http://floating-point-gui.de/
>
> Matt

_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: Strange float / tuple problem

zxq9-2
On 2016年6月4日 土曜日 10:07:11 Donald Steven wrote:
> Thanks Matthias and Craig.  I see the issue and I've got a work around.  
> BTW, is there a way to truncate a float to a given # of places, not just
> 0 -- not just a formatted output, but a true truncation?

Keep in mind that in this case you don't want mere truncation -- what
if your teeny, tiny value winds up being teeny, tiny on the negative
side of zero?

You need a test to check for whether a value is "close enough" to a
given value to be considered a valid approximation of it for your case,
and have a way to declare how close "close enough" means. That's not
truncation.

You want to compare to 5.0, for example.
If 5.0000000000001 is close enough then 4.9999999999999 is also.

Unfortunately floats are just hard to deal with (in every language),
but at least floats have strictly defined rules that limit how hard
they are.

The first thing to consider is whether you should really be doing
fixed-point math, or if floats are good enough for your use. Assuming
floats really are a good fit, something like this could help:

approximate(Precision) ->
    fun(Z, Z) -> true;
       (Z, Q) -> (Z - Precision =< Q) and (Q =< Z + Precision)
    end.

1> Approx = numbers:approximate(0.00001).
#Fun<numbers.0.78713399>
2> Approx(1, 2).
false
3> Approx(5, 5).
true
4> Approx(-5.2, -5.2).
true
5> Approx(10, 10.0000000001).
true
6> Approx(10, 10.1).
false
7> Approx(0.0, 4.440892098500626e-16).
true
8> Approx(0.0, -4.440892098500626e-16).
true

This is probably an inefficient implementation -- I've not taken the
time to really examine it carefully, but it should be a pretty clear
what the intent is. You provide a value that represents how close a
value must be to be considered approximately equal and you get back
a function that will check if the input values are that close.

(There are probably some float corner cases that can pop up here, and
its probably more efficient to define precision as a Power base X and
slide things around to compare ranges, etc. but the implementation
isn't the point and I have no idea how any of that would actually hit
the underlying machine without writing it in assembler, so clarity
wins in the majority case.)

Surely there is some library out there that already provides something
like this.

-Craig
_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: Strange float / tuple problem

zxq9-2
On 2016年6月5日 日曜日 13:11:30 zxq9 wrote:
> On 2016年6月4日 土曜日 10:07:11 Donald Steven wrote:
> > Thanks Matthias and Craig.  I see the issue and I've got a work around.  
> > BTW, is there a way to truncate a float to a given # of places, not just
> > 0 -- not just a formatted output, but a true truncation?
>
> approximate(Precision) ->
>     fun(Z, Z) -> true;
>        (Z, Q) -> (Z - Precision =< Q) and (Q =< Z + Precision)
>     end.

I forgot to mention something... in your particular case you may really
be wanting to convert to a specific value instead of just compare, in that
case a small change is more direct:

force_fit(Precision) ->
    Approx = approximate(Precision),
    fun(Z, Q) ->
        case Approx(Z, Q) of
            true  -> Z;
            false -> Q
        end
    end.

1> Rough = numbers:force_fit(0.00001).
#Fun<numbers.1.117927507>
2> Rough(0.0, 4.440892098500626e-16).
0.0
3> Rough(0.0, 0.1).
0.1

Obviously this would be playing with fire on intermediate values (*much*
more dangerous than the usual caveats that apply to iterative processing
with floats). But it may be closer to the behavior you are looking for --
conceptually, anyway. I have no idea what you're really doing.

-Craig
_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: Strange float / tuple problem

Donald Steven
This is most helpful.   Thanks Craig.

Don

On 06/05/2016 12:25 AM, zxq9 wrote:

> On 2016年6月5日 日曜日 13:11:30 zxq9 wrote:
>> On 2016年6月4日 土曜日 10:07:11 Donald Steven wrote:
>>> Thanks Matthias and Craig.  I see the issue and I've got a work around.
>>> BTW, is there a way to truncate a float to a given # of places, not just
>>> 0 -- not just a formatted output, but a true truncation?
>> approximate(Precision) ->
>>      fun(Z, Z) -> true;
>>         (Z, Q) -> (Z - Precision =< Q) and (Q =< Z + Precision)
>>      end.
> I forgot to mention something... in your particular case you may really
> be wanting to convert to a specific value instead of just compare, in that
> case a small change is more direct:
>
> force_fit(Precision) ->
>      Approx = approximate(Precision),
>      fun(Z, Q) ->
>          case Approx(Z, Q) of
>              true  -> Z;
>              false -> Q
>          end
>      end.
>
> 1> Rough = numbers:force_fit(0.00001).
> #Fun<numbers.1.117927507>
> 2> Rough(0.0, 4.440892098500626e-16).
> 0.0
> 3> Rough(0.0, 0.1).
> 0.1
>
> Obviously this would be playing with fire on intermediate values (*much*
> more dangerous than the usual caveats that apply to iterative processing
> with floats). But it may be closer to the behavior you are looking for --
> conceptually, anyway. I have no idea what you're really doing.
>
> -Craig
> _______________________________________________
> erlang-questions mailing list
> [hidden email]
> http://erlang.org/mailman/listinfo/erlang-questions

_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: Strange float / tuple problem

Richard A. O'Keefe-2
In reply to this post by Donald Steven


On 5/06/16 2:07 AM, Donald Steven wrote:
> Thanks Matthias and Craig.  I see the issue and I've got a work
> around. BTW, is there a way to truncate a float to a given # of
> places, not just 0 -- not just a formatted output, but a true truncation?
I think you may need something more fundamental than a workaround.
For what it's worth, DBL_EPSILON for IEEE doubles is
2.2204460492503130808473E-16.
Now DBL_EPSILON is the smallest value of e such that 1.0+e /= 1.0,
so the 4.4...e-16 value you reported is 2 units in the last place for a
number close to 1 or 1 unit in the last place for a number close to 2,
or to put it another way, one or two rounding errors.

We are definitely dealing with some sort of calculation here, not a
literal 0.0, and it would help a lot to show us that calculation.

Since IEEE arithmetic gives you the closest representable value for
the result of any one operation, I think you'll appreciate that truncation
is, as a general rule, going to INCREASE errors.  The best you can do
to truncate a floating point number to a given number of decimal places
is truncate(X*10**N)/10**N.
Needless to say, the result is NOT exact, because almost all decimal
fractions
are not exactly representable in binary arithmetic.

Why do you think that truncation rather than rounding is right here?

Calculations where you expect exact answers should be done in integer
arithmetic, if possible.
_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions