If you are new to programming, then the following discussion may contain some surprising information. But even if you are used to writing programs, computers can do unexpected things with numbers. The purpose of this lesson is to shed some light on some of the mysteries and quirks you can encounter. These mysteries exist independently of the programming language, though one programming language may be better at isolating you from them than another. The problem is that computers do not deal with numbers the way humans normally do.
% expr {1/6} 0 % expr {1/6.0} 0.16666666666666666
The difference is the decimal point in the second expression.
Tcl uses a simple but efficient strategy to decide what kind of numbers to use for the computations:
Floating-point computations are quite complex, and the current(IEEE) standard prescribes what should happen in minute detail. One such detail is that results outside the proper ranges are reported. Tcl catches these and displays a warning:
% # Compute 1.0e+300/1.0-300 % puts [expr {1.0e300/1.0e-300}] floating-point value too large to represent
Now some of the mysteries you can find yourself involved in. Run the following scripts:
# # Division # puts "1/2 is [expr {1/2}]" puts "-1/2 is [expr {-1/2}]" puts "1/2 is [expr {1./2}]" puts "1/3 is [expr {1./3}]" puts "1/3 is [expr {double(1)/3}]"
The first two computations have the surprising result: 0 and -1. That is because the result is an integer number and the mathematically exact results 1/2 and -1/2 are not. If you interested in the details of how Tcl works, the outcome q is determined as follows:
a = q * b + r 0 <= |r| < |b| r has the same sign as q
Here are some examples with floating-point numbers:
puts "1/2 is [expr {1./2}]" puts "1/3 is [expr {1./3}]" set a [expr {1.0/3.0}] puts "3*(1/3) is [expr {3.0*$a}]" set b [expr {10.0/3.0}] puts "3*(10/3) is [expr {3.0*$b}]" set c [expr {10.0/3.0}] set d [expr {2.0/3.0}] puts "(10.0/3.0) / (2.0/3.0) is [expr {$c/$d}]" set e [expr {1.0/10.0}] puts "1.2 / 0.1 is [expr {1.2/$e}]"
While many of the above computations give the result you would expect, note however the last decimals, the last two do not give exactly 5 and 12! This is because computers can only deal with numbers with a limited precision: floating-point numbers are not our mathematical real numbers.
Somewhat unexpectedly, 1/10 also gives problems. 1.2/0.1 results in 11.999999999999998, not 12. That is an example of a very nasty aspect of most computers and programming languages today: they do not work with ordinary decimal fractions, but with binary fractions. So, 0.5 can be represented exactly, but 0.1 can not.
# # The wrong way # set number [expr {int(1.2/0.1)}] ;# Force an integer - ;# accidentally number = 11 for { set i 0 } { $i <= $number } { incr i } { set x [expr {$i*0.1}] ... create label $x } # # A right way - note the limit # set x 0.0 set delta 0.1 while { $x < 1.2+0.5*$delta } { ... create label $x set x [expr {$x + $delta}] }
# # Two different estimates of "pi" # set pi1 [expr {4.0*atan(1.0)}] set pi2 [expr {6.0*asin(0.5)}] puts [expr {$pi1-$pi2}] -4.4408920985006262e-016