Rounding in Tcl

MB : The subject of this page is to analyse the methods to round a double real value into an integer. The Tcl language provide 4 commands :

  • int arg : The integer part of arg is determined, and then the low order bits of that integer value up to the machine word size are returned as an integer value.
  • ceil arg: Returns the smallest integral floating-point value (i.e. with a zero fractional part) not less than arg.
  • floor arg : Returns the largest integral floating-point value (i.e. with a zero fractional part) not greater than arg.
  • round arg : If arg is an integer value, returns arg, otherwise converts arg to integer by rounding and returns the converted value.

It is not obvious to see the effect of these operators on a given data, and that may generate bugs if the wrong operator is chosen. The following is a small Tcl script which shows the effect of the 4 operators on 8 real values from 0 to 2.

  for {set i 0} {$i<9} {incr i} {
  set x [expr {double($i)/4.0}]
  set ceil [expr {ceil($x)}]
  set floor [expr {floor($x)}]
  set round [expr {round($x)}]
  set int [expr {int($x)}]
  puts "ceil($x)=\t $ceil, \t floor($x)=\t $floor, \t round($x)=\t $round, \t int($x)=\t $int"
  }

And this is the result.

ceil(0.0)=0.0,floor(0.0)=0.0,round(0.0)=0,int(0.0)=0
ceil(0.25)=1.0,floor(0.25)=0.0,round(0.25)=0,int(0.25)=0
ceil(0.5)=1.0,floor(0.5)=0.0,round(0.5)=1,int(0.5)=0
ceil(0.75)=1.0,floor(0.75)=0.0,round(0.75)=1,int(0.75)=0
ceil(1.0)=1.0,floor(1.0)=1.0,round(1.0)=1,int(1.0)=1
ceil(1.25)=2.0,floor(1.25)=1.0,round(1.25)=1,int(1.25)=1
ceil(1.5)=2.0,floor(1.5)=1.0,round(1.5)=2,int(1.5)=1
ceil(1.75)=2.0,floor(1.75)=1.0,round(1.75)=2,int(1.75)=1
ceil(2.0)=2.0,floor(2.0)=2.0,round(2.0)=2,int(2.0)=2

The results of ceil() and floor() differ if an arbitrary precision value without direct floating point representation is supplied. Example:

set x 18014398509481983
% expr {ceil($x)}
18014398509481984.0
% expr {floor($x)}
18014398509481982.0

Both results in this case differ from round() because the latter does not convert to floating point and does not change value:

set x 18014398509481983
% expr {round($x)==ceil($x)}
0
% expr {round($x)==floor($x)}
0

See also