Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Problem doing fraction comparison in TCL

22 views
Skip to first unread message

Sharad

unread,
Jan 18, 2007, 1:06:22 AM1/18/07
to
Hi all,

I just tried to do a simple comparison of the two values, one was
user-defined value, and the other one was a result of a math expression
of other two user-defined values. It seems the result of the comparison
check was correct only when the values to be compare weren't fractions.
I have narrowed it down to the following simple script.

====

proc test { max min exp_diff } {
set diff [expr $max - $min]
puts "max=$max,min=$min,diff=$diff => want exp_diff=$exp_diff"
if { $diff <= $exp_diff } {
puts TRUE
} else {
puts FALSE
}
}

puts "trying when all values are integer"
test 2 1 1
puts "trying when all values are float"
test 1.1 1.0 0.1

====

When I ran the above script, I got the following output. The 2nd case
when all the values were floating point, the condition check came out
FALSE when it should have been TRUE. Is this a bug with ActiveState TCL
code or was I doing something totally wrong? I tried different TCL
versions from ATS4.0, 4.2, and 4.3, and they all seemed to behave the
same.
A modified function however gives the correct results:
proc test { max min exp_diff } {
set diff [expr $max - $min]
puts "max=$max,min=$min,diff=$diff => want exp_diff=$exp_diff"
if [ expr $diff <= $exp_diff ] {
puts TRUE
} else {
puts FALSE
}
}


Any ideas?

Regards
Sharad

sleb...@gmail.com

unread,
Jan 18, 2007, 1:28:26 AM1/18/07
to
Sharad wrote:
> Hi all,
>
> I just tried to do a simple comparison of the two values, one was
> user-defined value, and the other one was a result of a math expression
> of other two user-defined values. It seems the result of the comparison
> check was correct only when the values to be compare weren't fractions.
> I have narrowed it down to the following simple script.
>
> ====
>
> proc test { max min exp_diff } {
> set diff [expr $max - $min]
> puts "max=$max,min=$min,diff=$diff => want exp_diff=$exp_diff"
> if { $diff <= $exp_diff } {
> puts TRUE
> } else {
> puts FALSE
> }
> }
>
> puts "trying when all values are integer"
> test 2 1 1
> puts "trying when all values are float"
> test 1.1 1.0 0.1
>

Hmm it does appear that (1.1 - 1.0) is indeed larger than 0.1. The
following simplified code have been tested on 3 different CPUs:
Pentium4, Athlon64 and ARM7 (Xscale). All generate the same result:

% expr {(1.1 - 1.0) <= (0.1)}
0

However it is the following seems to work on all 3 platforms:

% expr {(1.1 - 1.0) <= (0.1)}
1

It should be noted that 0.1 is not representable as a binary floating
point number of a finite size (you'd need an infinite number of bits).
So it is possible that (1.1 - 1.0) is represented by a larger binary
number than (0.1 + 0).

Stephan Kuhagen

unread,
Jan 18, 2007, 1:41:34 AM1/18/07
to
sleb...@yahoo.com wrote:

> % expr {(1.1 - 1.0) <= (0.1)}
> 0
>
> However it is the following seems to work on all 3 platforms:
>
> % expr {(1.1 - 1.0) <= (0.1)}
> 1

Am I blind or are the two lines equal...?

Regards
Stephan

Leopold Gerlinger

unread,
Jan 18, 2007, 2:00:03 AM1/18/07
to Sharad
Dear Shard,

this is a problem in every language dealing with fractional values. See
below what happens when we set the highest precision for displaying
fractional values:

(bin) 60 % set tcl_precision 17
17
(bin) 61 % test 1.1 1.0 0.1
max=1.1,min=1.0,diff=0.10000000000000009 => want exp_diff=0.1
FALSE
(bin) 62 %

The problem comes up when a string is converted into a value (for
fractional values a internal type 'double' is used). Usually a so-called
epsilon value (a very small value, e.g. 1e-6) is used for fractional
comparison, e.g.:

if { abs($diff - $exp_diff) <= $epsilon } ....

Again, this is NOT a problem of Tcl but a problem of converting numbers
from strings (which is of base 10) to double's (which uses base 2) -
this conversion looses precision.

I hope I was able to help ;-)

Regards - Leo

Bruce Hartweg

unread,
Jan 18, 2007, 2:01:21 AM1/18/07
to

rounding errors, floating point numbers are not exactly representable,
the difference between the number is very minute, but not exact. always
leave an epsilon for checking 2 floats.

in the first case expr was directly comparing numbers, and diff is just a
little bit bigger than exp_diff. in the second case, the interp substitutes
values for the numbers as strings and then expr re-parses them and the come
out the same.

Bruce

Arjen Markus

unread,
Jan 18, 2007, 2:50:59 AM1/18/07
to

Bruce Hartweg schreef:

>
> rounding errors, floating point numbers are not exactly representable,
> the difference between the number is very minute, but not exact. always
> leave an epsilon for checking 2 floats.
>
> in the first case expr was directly comparing numbers, and diff is just a
> little bit bigger than exp_diff. in the second case, the interp substitutes
> values for the numbers as strings and then expr re-parses them and the come
> out the same.
>

You can read more about this on the Wiki: http://wiki.tcl.tk/11969

A solution is to use the fuzzy comparisons defined in the Tcllib
library
(the math::fuzzy package). This uses a, well, fuzzy comparison
technique
to get around these floating-point issues. And as others have pointed
out,
this is a very general problem.

Regards,

Arjen

Sharad

unread,
Jan 18, 2007, 3:11:28 AM1/18/07
to
Thanks to all for replying. Good explanations - I have more clarity on
the topic now.

Thanks again.
Sharad

sleb...@gmail.com

unread,
Jan 18, 2007, 3:27:24 AM1/18/07
to

Ah sorry, second line (the one that works) should have been:

expr {(1.1 - 1.0) <= (0.1 + 1 - 1)}

So in effect the test can be re-coded as:

if {$diff <= ($exp_diff + 1 - 1)} ...

Stéphane A.

unread,
Jan 18, 2007, 11:40:45 AM1/18/07
to

Just posting my 2 cents: the [math::bigfloat] package adresses
floating-point precision issues with arbitrary-precision.
(With the overcost of adding a layer upon your computations)

Regards,

Stéphane

0 new messages