Erlang Central

Difference between revisions of "Floating Point Compare"

From ErlangCentral Wiki

(remove irritating angsting, remove bogus reference to 'imaginary' Erlang bignums and its consequent reasoning (hint: Erlang has bignums.))
m (eep, HTML element issue.)
(One intermediate revision by one user not shown)
Line 9: Line 9:
 
In these cases, you can use floating-point byte strings to represent and compare numbers:  
 
In these cases, you can use floating-point byte strings to represent and compare numbers:  
 
<code>
 
<code>
1> Aval = 8.001e-3 * 9.001e5.
+
fuzzy_match(A,B,L) ->
 +
    &lt;&lt;AT:L/binary, _/binary>> = &lt;&lt;A/float>>,
 +
    &lt;&lt;BT:L/binary, _/binary>> = &lt;&lt;B/float>>,
 +
    AT == BT.
 +
1> A = 8.001e-3 * 9.001e5.
 
7201.70
 
7201.70
2> Bval = 8.0011e-3 * 9.001e5.
+
2> B = 8.0011e-3 * 9.001e5.
 
7201.79
 
7201.79
3> Aval == Bval.
+
3> A == B.
 
false
 
false
4> ABin = << Aval/float >>.
+
4> fuzzy_match(A,B,3).
<<64,188,33,179,57,192,235,237>>
+
5> BBin = << Bval/float >>.
+
<<64,188,33,202,68,166,34,63>>
+
6> << ABinTest:3/binary, ARest:5/binary >> = ABin.
+
<<64,188,33,179,57,192,235,237>>
+
7> ABinTest.
+
<<64,188,33>>
+
8> << BBinTest:3/binary, BRest:5/binary >> = BBin.
+
<<64,188,33,202,68,166,34,63>>
+
9> BBinTest.
+
<<64,188,33>>
+
10> BinTest == BinTest2.
+
 
true
 
true
11> << ABinTest2:4/binary, ARest2:4/binary >> = ABin.
+
5> fuzzy_match(A,B,4).
<<64,188,33,179,57,192,235,237>>
+
12> << BBinTest2:4/binary, BRest2:4/binary >> = BBin.
+
<<64,188,33,202,68,166,34,63>>
+
13> ABinTest2 == BBinTest2.
+
 
false
 
false
 
</code>
 
</code>
Line 39: Line 27:
 
Another option is to convert the numbers into strings and then compare the portions of the numbers of interest:  
 
Another option is to convert the numbers into strings and then compare the portions of the numbers of interest:  
 
<code>
 
<code>
14> [Nsa]=io_lib:format("~.12f", [Aval]).
+
equal_to_digit(A,B,D) ->
["7201.700099999999"]
+
    [As0,Bs0] = io_lib:fwrite("~.*f~.*f", [D+1,A-trunc(A),D+1,B-trunc(B)]),
15> [NSB]=io_lib:format("~.12f", [Bval]). 
+
    As = string:substr(As0,1,D+2), Bs = string:substr(Bs0,1,D+2),
["7201.790110000001"]
+
    As == Bs.
16> string:substr(NSAa, 1, 6) == string:substr(NSB, 1, 6).
+
5> equal_to_digit(7201.700099999999, 7201.790110000001,1).
true
+
</code>
+
 
+
A final thought:
+
<code>
+
20> Equal_to_digit = fun(Digit, Numa, Numb) ->                   
+
20> [N_a] = io_lib:format("~.12f", [Numa]),                  
+
20> [N_b] = io_lib:format("~.12f", [Numb]), 
+
20> string:substr(N_a, 1, Digit) == string:substr(N_b,1,Digit) end.
+
#Fun<erl_eval.18.83214871>
+
21> Equal_to_digit(6, Aval, Bval).
+
 
true
 
true
 +
6> equal_to_digit(7201,700099999999, 7201.790110000001,2).
 +
false
 
</code>
 
</code>
  

Revision as of 15:49, 24 September 2006

Problem

You want to compare two floating-point numbers and know if they are equal. Unfortunately, floating-point arithmetic is not precise so very few results will match exactly. Consequently, we usually want to compare floating point values up to a certain number of decimal places.

Solution

Implement a "fuzzy match" on two real numbers where the difference is below some epsilon threshhold.

In these cases, you can use floating-point byte strings to represent and compare numbers:

fuzzy_match(A,B,L) ->
    <<AT:L/binary, _/binary>> = <<A/float>>,
    <<BT:L/binary, _/binary>> = <<B/float>>,
    AT == BT.
1> A = 8.001e-3 * 9.001e5.
7201.70
2> B = 8.0011e-3 * 9.001e5.
7201.79
3> A == B.
false
4> fuzzy_match(A,B,3).
true
5> fuzzy_match(A,B,4).
false

Another option is to convert the numbers into strings and then compare the portions of the numbers of interest:

equal_to_digit(A,B,D) ->
    [As0,Bs0] = io_lib:fwrite("~.*f~.*f", [D+1,A-trunc(A),D+1,B-trunc(B)]),
    As = string:substr(As0,1,D+2), Bs = string:substr(Bs0,1,D+2),
    As == Bs.
5> equal_to_digit(7201.700099999999, 7201.790110000001,1).
true
6> equal_to_digit(7201,700099999999, 7201.790110000001,2).
false

Note: Some error handling would obviously be necessary to handle cases where the digits are insufficient for the match.

See Also

Volume 2, Section 4.2.2 of The Art of Computer Programming