Floating point 为什么使用tcl时1.0不等于1.0==

Floating point 为什么使用tcl时1.0不等于1.0==,floating-point,tcl,Floating Point,Tcl,在下面的代码中,我遇到了一个奇怪的现象。在if{$ma==$mb}行上的运行中,mb和ma都是相等的1.0,但没有采用if。当我将==更改为eq或[cequal$ma$mb]时,if被执行 另外,当我尝试用1.0更改其中一个变量时(if{$ma==1.0}和if{$1.0==mb}),也没有使用if,但是使用了带有表达式if{1.0==1.0}的if 在将==更改为eq后,我遇到的另一件事是0.0和-0.0在使用eq时不相等,但在使用=时相等 这些差异的根源是什么 我知道在浮点数比较中,最好使用

在下面的代码中,我遇到了一个奇怪的现象。在
if{$ma==$mb}
行上的运行中,
mb
ma
都是相等的
1.0
,但没有采用if。当我将
==
更改为
eq
[cequal$ma$mb]
时,if被执行

另外,当我尝试用
1.0
更改其中一个变量时(
if{$ma==1.0}
if{$1.0==mb}
),也没有使用if,但是使用了带有表达式
if{1.0==1.0}
的if

在将
==
更改为
eq
后,我遇到的另一件事是
0.0
-0.0
在使用
eq
时不相等,但在使用
=
时相等

这些差异的根源是什么

我知道在浮点数比较中,最好使用一个小ε来检查两个数是否真的很接近,但在这种情况下,进行比较是为了避免被零除

代码:

proc geometry_intersect_两段{xa1 ya1 xa2 ya2 xb1 yb1 xb2 yb2}{
如果{($xa1==$xa2)&($xb1==$xb2)}{
返回{}
}
如果{!($xa1==$xa2)}{
设置ma[expr($ya1-$ya2)*1.0)/($xa1-$xa2)]
设置na[expr$ya1-($ma*1.0*$xa1)]
}
如果{!($xb1==$xb2)}{
设置mb[expr($yb1-$yb2)*1.0)/($xb1-$xb2)]
set nb[expr$yb1-($mb*1.0*$xb1)]
}
如果{$xa1==$xa2}{
设置retx[expr$xa1*1.0]
设置rety[expr$retx*1.0*$mb+$nb]
如果{($rety=[min$yb1$yb2])&($rety=[min$ya1$ya2])&&\
($retx=[min$xb1$xb2])&&($retx=[min$xa1$xa2])}{ety]
}否则{
返回{}
}
}
如果{$xb1==$xb2}{
设置retx[expr$xb1*1.0]
设置rety[expr$retx*1.0*$ma+$na]
如果{($rety=[min$yb1$yb2])&($rety=[min$ya1$ya2])&&\
($retx=[min$xb1$xb2])&&($retx=[min$xa1$xa2])){
返回[列表$retx$rety]
}否则{
返回{}
}
}
如果{$mb==$ma}{
返回{}
}
set retx[expr 1.0*($na-$nb)/($mb-$ma)]
设置rety[expr 1.0*($ma*$retx)+$na]
如果{($rety=[min$yb1$yb2])&($rety=[min$ya1$ya2])&&\
($retx=[min$xb1$xb2])&&($retx=[min$xa1$xa2])){
返回[列表$retx$rety]
}否则{
返回{}
}

要直接回答您的问题,
=
将在两个数字不相等时失败


在您的问题中,您提到
eq
给出的结果与
=
不同。这是因为
eq
导致在比较之前将值转换为字符串。当浮点值转换为字符串时,它们必须向上舍入到某个值。在两个浮点值的情况下在一定范围内的成员,它们将四舍五入到相同的值,产生完全相同的字符串。

=
强制进行数字比较,而
eq
强制进行字符串比较(如果内存可用,那么您的
cequal
来自Tclx,基本上与
eq
具有相同的语义)

比较严格相等的浮点值的问题在设计上是有缺陷的,因为这些类型不包含精确值(与整数相反)。比较两个浮点值的一种明智方法是定义一个特定(非常小)的常量“epsilon”,并查看其差值的绝对值是否小于该“epsilon”。如果更少,则声明两个浮点数相同,否则不相同。会出现一个快速的谷歌搜索,以及其他有用的链接


另一种方法是尝试坚持使用整数(在适当的情况下使用适当的升尺度和降尺度,以避免欠流/过流)。

IEEE浮点值(Tcl内部使用这些值,因为它们受CPU硬件的支持)众所周知,它们的特性是不精确地表示值。无论如何,在一级近似值中,它们确实精确地表示值,因为它们具有固定的位数(64表示Tcl使用的双精度位),但它们表示的值可能与您认为的值略有不同(因为许多值不能用固定数量的二进制数字精确表示,就像1/3在十进制中几乎是但不完全是0.333;这是完全相同的问题,但在另一个基数中)

Tcl采取了一些有限的步骤来解决这个问题,以便于显示;因为8.5它以再次获得准确值所需的最小位数来呈现浮点数,而在8.4和之前,它只是在打印数字时使用较小的位数(最多15位小数,而不是精确表示所需的17位小数)这可以通过magic
tcl\u precision
变量进行控制。但不要设置该变量;它不会做您需要做的事情,因为它只是将值呈现为字符串,而不是值本身。相反,您需要使用不同的(也是众所周知的)相等策略:epsilon内相等

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

请注意,这样做的另一个后果是,您永远不应出于迭代目的使用浮点数,因为这会使错误累积并超过ε。不要以0.1的步长从0到25,而是以整数步长从0到250,然后通过乘以0.1得出浮点数。

您绝对确定吗这两个变量的文字值都是1.0?有没有可能其中一个变量的值像1.0000000001,由于四舍五入,它被打印为1.0?@BryanOakley,我不知道,我什么时候做了
[set ma]
[put$ma]
我得到了
1.0
,并且
eq
在它们之间返回了真值
# Magic value! This one is OK for values in the range of small integers
proc equal_float {a b {epsilon 1e-15}} {
    return [expr {abs($a - $b) < $epsilon}]
}
# Instead of: if {$x == 42.3} { ... }
if {[equal_float $x 42.3]} { ... }