为什么Tcl的expr命令会将实际非空的数值判定为空?
问题描述
我从无权限查看的未知来源导入了一组坐标数据,执行如下代码:
set yMin [lindex [lindex $bbox 0] 1] puts "yMin: <$yMin>"
运行正常,输出如下(尖括号用于排查是否存在空格或隐藏制表符):
yMin: <-6.149999999999999e-02>
但问题在于,如果将yMin与任意数值(例如0.5)相乘,就会报错:
set Y2 [expr {0.5 * $yMin}]
can't use empty string as operand of "*"
while executing "expr {0.5 * $yMin}"
invoked from within "set Y2 [expr {0.5 * $yMin}]"
哪怕仅打印yMin的expr运算结果,也会报空操作数错误:
set Y1 [expr $yMin] puts "Y1: <$Y1>"
empty expression in expression "" (parsing expression "")
invoked from within "expr $yMin"
invoked from within "set Y1 [expr $yMin]"
但有一个特殊测试结果:为expr添加花括号后运行正常:
set Y1 [expr {$yMin}] puts "Y1: <$Y1>"
Y1: <-0.06149999999999999>
问题复现尝试
感谢Glenn Jackman提供的测试模板,但如下代码无法复现问题,这也是发帖求助的原因:
% set bbox {{-4.599999999999999e-02 -6.149999999999999e-02} {8.000000000000002e-02 6.149999999999999e-02}} {-4.599999999999999e-02 -6.149999999999999e-02} {8.000000000000002e-02 6.149999999999999e-02} % set yMin [lindex [lindex $bbox 0] 1] -6.149999999999999e-02 % expr {1 + $yMin} 0.9385
排查尝试
首先验证yMin是否为空,结果显示非空:
if {$yMin eq {}} { puts "Empty records" exit 1 } else { puts "yMin is not empty: <$yMin>" }
yMin is not empty: <-6.149999999999999e-02>
之后我尝试用trim、map、regsub移除所有空格和制表符,处理后打印的结果仍正常,但运算依旧报错:
set trim_yMin [string trim $yMin] puts "trim_yMin: <$trim_yMin>" set map_yMin [string map {" " ""} $yMin] puts "map_yMin: <$map_yMin>" regsub -all {\s} $yMin {} reg_yMin puts "reg_yMin: <$reg_yMin>" if {$trim_yMin eq {} || $map_yMin eq {} || $reg_yMin eq {}} { puts "Empty records" exit 1 } else { puts "trim_yMin is not empty: <$reg_yMin>" puts "map_yMin is not empty: <$reg_yMin>" puts "reg_yMin is not empty: <$reg_yMin>" }
trim_yMin: <-6.149999999999999e-02>
map_yMin: <-6.149999999999999e-02>
reg_yMin: <-6.149999999999999e-02>
trim_yMin is not empty: <-6.149999999999999e-02>
map_yMin is not empty: <-6.149999999999999e-02>
reg_yMin is not empty: <-6.149999999999999e-02>
处理后的值运算依旧报错:
set reg_Y2 [expr {0.5 * $reg_yMin}] puts "0.5 * reg_yMin: $reg_Y2"
can't use empty string as operand of "*"
while executing "expr {0.5 * $reg_yMin}"
invoked from within "set reg_Y2 [expr {0.5 * $reg_yMin}]"
补充排查与回复
回复Ergwun的测试建议,执行代码如下:
puts $bbox; set yMin [lindex [lindex $bbox 0] 1] puts "yMin: <$yMin>"
{-4.599999999999999e-02 -6.149999999999999e-02} {8.000000000000002e-02 6.149999999999999e-02}
yMin: <-6.149999999999999e-02>
set Y2 [expr {0.5 * $yMin}] puts $Y2
can't use empty string as operand of "*"
while executing "expr {0.5 * $yMin}"
invoked from within "set Y2 [expr {0.5 * $yMin}]"
回复benaja的建议,查看变量表示:
puts [tcl::unsupported::representation $yMin]
value is a string with a refcount of 4, object pointer at 0x44ed0910, internal representation 0x450d6120:(nil), string representation "-6.14999999999...."
回复Shawn的建议,执行测试:
puts [tcl::unsupported::representation $yMin] puts [binary encode hex $yMin] puts [tcl::unsupported::representation $yMin] puts [string is double -strict $yMin] puts [tcl::unsupported::representation $yMin]
value is a string with a refcount of 4, object pointer at 0x44ed6030, internal representation 0x450de4b0:(nil), string representation "-6.1499999999..."
2d362e313439393939393939393939393939652d3032
value is a bytearray with a refcount of 4, object pointer at 0x44ed6030, internal representation 0x44ee0dc0:(nil), string representation "-6.1499999999..."
1
value is a double with a refcount of 4, object pointer at 0x44ed6030, internal representation 0xbfaf7ced916872af:(nil), string representation "-6.1499999999..."
直接使用字面量运算正常:
set Y2 [expr {0.5 * -6.149999999999999e-02}] puts "Y2: $Y2" set new_Y2 [expr {0.5*-6.149999999999999e-02}] puts "new_Y2: $new_Y2"
Y2: -0.030749999999999996
new_Y2: -0.030749999999999996
回复Schelte Bron的建议,给变量前加空格后报错类型变化:
puts [tcl::unsupported::representation $yMin] set yMin " $yMin" puts [tcl::unsupported::representation $yMin] set Y2 [expr {0.5 * $yMin}] puts $Y2
value is a string with a refcount of 4, object pointer at 0x44ec28a0, internal representation 0x450be540:(nil), string representation "-6.1499999999..."
value is a string with a refcount of 2, object pointer at 0x446bbc70, internal representation 0x44695fd0:(nil), string representation " -6.149999999..."
can't use non-numeric string as operand of "*"
while executing "expr {0.5 * $yMin}"
invoked from within
"set Y2 [expr {0.5 * $yMin}]"
按照建议做二进制编解码往返后运算依旧报错:
puts "Is yMin double? : [string is double -strict $yMin]" set binary_yMin [binary encode hex $yMin] set double_binary_yMin [binary decode hex $binary_yMin] puts "yMin: $yMin" puts "Binary of double yMin: $binary_yMin" puts "Double binary double: $double_binary_yMin" set Y2 [expr {0.5 * $double_binary_yMin}] puts $Y2
Is yMin double? : 1
yMin: -6.149999999999999e-02
Binary of double yMin: 2d362e313439393939393939393939393939652d3032
Double binary double: -6.149999999999999e-02
can't use empty string as operand of "*"
while executing "expr {0.5 * $double_binary_yMin}"
invoked from within "set Y2 [expr {0.5 * $double_binary_yMin}]"
使用tcl::mathop::*运算同样报错:
puts [tcl::mathop::* 0.5 $yMin]
can't use empty string as operand of "*"
while executing
"tcl::mathop::* 0.5 $yMin"
invoked from within
"puts [tcl::mathop::* 0.5 $yMin]"
已尝试所有能想到的排查手段仍无法解决问题,恳请各位提供解决思路,非常感谢。
内容的提问来源于stack exchange,提问作者Zureas

