当`expr`嵌套在Tcl中时,为什么算术结果会有所不同?

时间:2012-03-28 08:31:28

标签: floating-point expression tcl

由于浮点数的内部表示而知道不准确(请参阅Wikipedia,搜索到:“使用相等性测试”......),在执行操作时仍有意外:

% expr int(0.57 * 10000)
5699
% expr int([expr 0.57 * 10000])
5700

是否禁止嵌套expr?为什么它会改变通过的浮点值?或者它是否会改变浮点运算的顺序,影响结果的那些?

更新:关于comparing floating point numbers with speed(和辅助链接),some basics and not so basics here以及the IEEE 754-2008 standard's description主题的良好阅读。

1 个答案:

答案 0 :(得分:7)

这是一个有趣的问题,因为它涉及一些微妙的事情。

% expr int(0.57 * 10000)
5699

这段代码(没有替换,所以它的作用“不出所料”)表明浮点数本身就是令人惊讶的事情。特别是,0.57没有精确的表示形式作为IEEE双精度浮点数(它是base-2);事实上,它的表示略低于0.57,所以当向下舍入时(这是int(...)所做的; 10000本身就是精确的)你会下降到5699.你会看到与其他行为相同的行为语言也是如此。

% expr int([expr 0.57 * 10000])
5700

现在这个特别有趣。你首先看到内部计算正在完成,结果浮点数转换为字符串(因为没有其他方法可以做到)。现在,你必须使用Tcl 8.4 (或之前),其中默认数字渲染规则(实际上)是通过打印数字的前15位有效数字得到的;在这种情况下,它会给你5700.00000000000(好吧,在右边有一些零的截断),然后从头开始重新解释 作为第一个双精度(正好​​是5700.0),然后转换为5700

数字转换规则在Tcl 8.5中已更改。如今,当Tcl将浮点数转换为字符串时,它产生最短的字符串,该字符串将转换回完全相同的浮点数(即,通过字符串的边的行程将在得到的双字符中给出相同的位模式)。这意味着你现在从来没有看到上述两件事之间的差异。 (如果您确实要强制使用固定数量的小数位,请使用format %.15g [expr 0.57 * 10000]。)

如果你正确地表达你的表达,你也不会观察到8.0到8.4的观察行为,因为社区已经建议人们做了十多年:

$ tclsh8.4
% expr {int([expr 0.57 * 10000])}
5699

这是因为这不会强制将内部expr调用的结果解释为字符串。 (它也更快更安全。)