Why is 1.0 not equal to 1.0 in tcl when using ==

In the following code, I came across strange phenomena. On On runs on the line if {$ma == $mb} both mb and ma were equal to 1.0 , but if was not taken. When I changed == to eq or [cequal $ma $mb] , it was done.

In addition, when I tried to change one of the variables with 1.0 ( if {$ma == 1.0} and if {$1.0 == $mb} ), then if was not taken the same way as if with the expression if {1.0 ==1.0} .

Another thing that happened to me after changing == - eq is that 0.0 and -0.0 not equal when using eq , but when using == are equal.

What is the source of these differences?

I know that when comparing floating point numbers, it is better to use a small epsilon to check if the two numbers are really close to each other, but in this case the comparison is done to avoid division by zero.

code:

 proc geometry_intersect_two_sections {xa1 ya1 xa2 ya2 xb1 yb1 xb2 yb2} { if {($xa1 == $xa2) && ($xb1 == $xb2)} { return {} } if {!($xa1 == $xa2)} { set ma [expr (($ya1-$ya2)*1.0)/($xa1-$xa2)] set na [expr $ya1 - ($ma * 1.0 * $xa1)] } if {!($xb1 == $xb2)} { set mb [expr (($yb1-$yb2)*1.0)/($xb1-$xb2)] set nb [expr $yb1 - ($mb * 1.0 * $xb1)] } if {$xa1 == $xa2} { set retx [expr $xa1 * 1.0] set rety [expr $retx * 1.0 * $mb + $nb] if {($rety <= [max $yb1 $yb2]) && ($rety >= [min $yb1 $yb2]) && ($rety <= [max $ya1 $ya2]) && ($rety >= [min $ya1 $ya2]) && \ ($retx <= [max $xb1 $xb2]) && ($retx >= [min $xb1 $xb2]) && ($retx <= [max $xa1 $xa2]) && ($retx >= [min $xa1 $xa2])} {ety] } else { return {} } } if {$xb1 == $xb2} { set retx [expr $xb1 * 1.0] set rety [expr $retx * 1.0 * $ma + $na] if {($rety <= [max $yb1 $yb2]) && ($rety >= [min $yb1 $yb2]) && ($rety <= [max $ya1 $ya2]) && ($rety >= [min $ya1 $ya2]) && \ ($retx <= [max $xb1 $xb2]) && ($retx >= [min $xb1 $xb2]) && ($retx <= [max $xa1 $xa2]) && ($retx >= [min $xa1 $xa2])} { return [list $retx $rety] } else { return {} } } if {$mb == $ma} { return {} } set retx [expr 1.0 * ($na - $nb)/($mb - $ma)] set rety [expr 1.0 * ($ma * $retx) + $na] if {($rety <= [max $yb1 $yb2]) && ($rety >= [min $yb1 $yb2]) && ($rety <= [max $ya1 $ya2]) && ($rety >= [min $ya1 $ya2]) && \ ($retx <= [max $xb1 $xb2]) && ($retx >= [min $xb1 $xb2]) && ($retx <= [max $xa1 $xa2]) && ($retx >= [min $xa1 $xa2])} { return [list $retx $rety] } else { return {} } 
+4
source share
3 answers

The IEEE floating point values โ€‹โ€‹(which Tcl uses internally because they are supported by your processor hardware) know very well that they do not represent the values โ€‹โ€‹exactly. In any case, as a first approximation; they represent values โ€‹โ€‹just like they have a fixed number of bits (64 for double , which Tcl uses), but the value they represent may be slightly different from what you think (because many values โ€‹โ€‹cannot be represented exactly in a fixed number of binary digits, like 1/3 almost ten-digit, but not exactly 0.3333333333, this is the same, but in a different number base).

Tcl takes some limited steps to resolve this issue for display; starting at 8.5, it displays floating point numbers with the minimum number of digits needed to get the exact value, and at 8.4 and before it just uses fewer digits when printing the number (up to 15 decimal digits instead of 17 what would be needed for the exact views), where it is controlled by the magic variable tcl_precision . However, do not set this variable; it does not do what you need, as it is all about rendering the value for the string, not the value itself. Instead, you need to use a different (and very well-known) equality strategy: equal-in-epsilon.

 # Magic value! This one is OK for values in the range of small integers proc equal_float {ab {epsilon 1e-15}} { return [expr {abs($a - $b) < $epsilon}] } 

Then you will use it as follows:

 # Instead of: if {$x == 42.3} { ... } if {[equal_float $x 42.3]} { ... } 

Note that this is another consequence of this: you should never use floating point numbers for iteration purposes, as this allows errors to accumulate and exceed epsilon. Instead of going from 0 to 25 in increments of 0.1, go from 0 to 250 in whole steps, and then print the float value by multiplying by 0.1.

+6
source

To directly answer your question, == will fail if both numbers are not equal.

In your question, you mentioned that eq gave a different result than == . This is due to the fact that eq leads to the transformation values in the line before the comparison. When floating point values โ€‹โ€‹are converted to strings, they should be rounded to a certain value. In the case of two floating point numbers that fall within a certain range, they will be rounded to the same value, which will give exactly the same string.

+4
source

== forces a numerical comparison, and eq forcibly compares the string comparison (and your cequal comes from Tclx if the memory serves me, and essentially has the same semantics as eq ).

The problem of comparing floating point values โ€‹โ€‹for strict equality is erroneous in design because these types do not contain exact values โ€‹โ€‹(as opposed to integers). One sensible approach to comparing two floats is to determine a specific (very small) constant "epsilon" and see if their absolute difference is less than "epsilon". If it is less, you are claiming that the two floats are the same and not the same. A quick Google search includes this essay and this page , among other useful links.

Another approach is to try to stick to integers (using appropriate scaling and zooming out when necessary so as not to be under / over the stream).

+2
source

Source: https://habr.com/ru/post/1437413/


All Articles