Hi guys
I need to compare two floats something like 0.99999998... vs 1.000000
and we came across accuracy problems when testing dihedral angles on a molecule...
so we read http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm
and wanted to implement something like:
if (*(int*)&f1 < *(int*)&f2)...
when ef1 and f2 are floats... (f1 about 0.9999899.. and f2=1.0
is think we should start with...
<<Padding:16,Myint:64>> = term_to_binary(1.9999976,[{minor_version,1}]).
and let Padding swallow the external format and tag while Myint gets the ieee754 float
But MyInt = 4611686017346523993 instead of 1073741822
these conversion should follow ieee754 and being lexicografic ordered
but ...
term_to_binary(1.99999976,[{minor_version,1}]).
<<131,70,63,255,255,255,191,147,83,89>>
term_to_binary(1.99999988,[{minor_version,1}]).
<<131,70,63,255,255,255,223,201,169,173>>
doesnt seem to be the same thatn you spect to see after reading that page...
..What im doing wrong....?
ieee754 layout....
|
+1.99999976 |
0x3FFFFFFE |
1073741822 |
|
+1.99999988 |
0x3FFFFFFF |
1073741823 |
|
+2.00000000 |
0x40000000 |
1073741824 |
|
+2.00000024 |
0x40000001 |
1073741825 |
|
+2.00000048 |
0x40000002 |
1073741826 |
Thanks!..
/Angel
_______________________________________________
erlang-questions mailing list
erlang-q...@erlang.org
http://erlang.org/mailman/listinfo/erlang-questions
1> <<1.000000000000000002/float>>.
<<63,240,0,0,0,0,0,0>>
2> <<1.0/float>>.
<<63,240,0,0,0,0,0,0>>
3> 1.000000000000000002.
1.0
Anyway I think I know what you are about. You want work with numbers
in some interval as they are same. Something like
1> F = fun(X) when is_float(X) -> <<V:32, _/binary>> = <<X/float>>,
<<F/float>> = <<V:32, 0:32>>, <<T/float>> = <<V:32, 16#ffffffff:32>>,
{F, T} end.
#Fun<erl_eval.6.82930912>
2> F(1.0).
{1.0,1.0000009536743162}
3> F(0.99999999).
{0.9999995231628418,0.9999999999999999}
4> F(1.99999976).
{1.9999990463256836,1.9999999999999998}
So you would be able make approximate comparison:
5> Comp = fun(X, Y, N) when is_float(X), is_float(Y), is_integer(N),
N>16, N<64 -> <<XI:N,_/bits>> = <<X/float>>, <<YI:N,_/bits>> =
<<Y/float>>, if XI < YI -> lt; XI =:= YI -> eq; XI > YI -> gt end end.
#Fun<erl_eval.18.82930912>
6> Comp(1.99999976, 1.99999988, 32).
eq
7> Comp(1.99999976, 1.99999988, 48).
lt
lt
--
Hynek Vychodil
Chief Scientists
GoodData
náměstí 28. října 1104/17, 602 00, Brno - Černá Pole
Office: +420 530 50 7704
E-mail: hy...@gooddata.com
Web: www.gooddata.com
That's what we need, i must confess that i have not earlier expeience with
binaries so needed a bit of thinking just to attack this problem but now im
starting to see it ...
Thanks Angel
Sorry folks, im just a bit puzzled for the strangeness of fp operations
I am talking about things like this:
8> 1.0000000000000001 > 1.0.
false
9> 1.0000000000000002 > 1.0.
true
And i wander that there is some method for avoiding such beasts..
Well ill be happy just if i can compare if Something > 1.0 and it just returns
false unless Something is really greater than 1.0 (whatever the accuracy is)
so i can relay on doing things like
if Angle > 1.0 the shutdown_orbital_laser_before_its_too_late().
because we found that:
math:acos(0.99999999999999991).
1.4901161193847656e-8
math:acos(0.99999999999999992).
1.4901161193847656e-8
math:acos(0.99999999999999993).
1.4901161193847656e-8
math:acos(0.99999999999999999).
0.0
math:acos(1.0000000000000001).
0.0
math:acos(1.0000000000000002).
** exception error: bad argument in an arithmetic expression
in function math:acos/1
called as math:acos(1.0000000000000002)
this last case is waht we wanted to avoid, specifically
/Angel
As others have pointed out, this is not strange at all. These are the
same values, once precision has been lost by converting the decimal
string to floating point (which has limited precision, see e.g.[1]).
>
> And i wander that there is some method for avoiding such beasts..
>
> Well ill be happy just if i can compare if Something> 1.0 and it just returns
> false unless Something is really greater than 1.0 (whatever the accuracy is)
> so i can relay on doing things like
>
> if Angle> 1.0 the shutdown_orbital_laser_before_its_too_late().
>
To PROPERLY make these comparisons, you need more precision (e.g.
BigDecimal, or fixed point/rational which would be simple with Erlang's
bignum integers).
There are many other issues with floating point, but to deal with the
above < and > comparisons, you simply need sufficient precision.
--Toby
[1] http://en.wikipedia.org/wiki/IEEE_754-1985
Result = if Myfloat < 1.0 -> Myfloat; true -> 1.0 end,
but little bit more efficient.
--
Hynek Vychodil
Chief Scientists
GoodData
náměstí 28. října 1104/17, 602 00, Brno - Černá Pole
Office: +420 530 50 7704
E-mail: hy...@gooddata.com
Web: www.gooddata.com
> The problem can be stated as:
>
> Result = case Myfloat > 1.0 of
> true -> 1.0;
> false MyFloat
> end
> ...
> ...
> math:acos(Result).
>
> after that we came across some values that fail to pass the test > 1.0 but are seen
> as greater to the math:acos function rendering a bad arithmetic expression error
If true, this indicates a serious bug. You should report such cases.
I am somewhat skeptical that such a gross bug would lie undetected so
far in erlang, so please supply an actual reproducible example.
Cheers,
P.
Assuming is not MyFloat < -1, that is.
Cheers
It starts smelling a bit, like i messed something up...