Work at SourceForge, help us to make it a better place! We have an immediate need for a Support Technician in our San Francisco or Denver office.

Close

#619 New function in "expr": sgn()

open
5
2012-05-28
2012-05-26
No

There are functions double, int, floor and ceil for rounding floating point values in various mode, but e.g. floor($num) and ceil($num) are equivalent with round($num -+ 0.5). But the signum function can be implemented only with longer constructs.

Discussion

  • I would take back my example: "floor($num) and ceil($num) are equivalent with round($num -+ 0.5)" it is not right.
    But not this is the point now.

     
    • summary: New funcion in "expr": sgn() --> New function in "expr": sgn()
     
  • Here's a complete workaround (modulo ensuring that $x is numeric and handling NaN):

    proc tcl::mathfunc::sgn {x} {
    expr {$x < 0 ? -1 : $x > 0 ? 1 : 0}
    }

    The only question is whether Tcl should provide such a thing by default.

     
  • Twylite
    Twylite
    2012-05-28

    is sgn() meant to be "sign()" as in DKF's example, or is it meant to be "round to a number of significant digits" as discussed below?

    In Excel for example, =FLOOR(1.134, 0.01) rounds 1.134 down to the nearest multiple of 0.01, giving 1.13. Similarly =FLOOR(1.5, 0.4) gives 1.2. The =ROUND() function rounds to a number of digits, e.g. =ROUND(1.135, 2) gives 1.14.

    The Excel functions combine a mathematical operation and an output formatting operation. The result of =FLOOR(1.134, 0.01) may not be accurately represented by a float or a double, so the floating point value is truncated for display purposes.

    Rounding to a significant number of digits (as in =ROUND) as opposed to an exact significance (as in =FLOOR) is quite easy in Tcl:

    proc round {d sgn} { format %0.${sgn}f $d }

    Computing and displaying floor or ceiling to a specific precision is more complicated:

    proc floor {d sgn} {
    set prec [expr { int(ceil(-log($sgn)/log(10))) }]
    format %0.${prec}f [expr { floor($d / $sgn) * $sgn }]
    }

    A compromise may be to extend round, floor and ceil to take an optional second parameter (significance), and implement equivalent to:

    proc round {d {sgn 1}} { expr { round($d / $sgn) * $sgn } }
    proc floor {d {sgn 1}} { expr { floor($d / $sgn) * $sgn } }

    This gives a mathematically accurate answer (within the constraints of floating point arithmetic), but does not attempt to format/truncate the output. This would be appropriate behaviour for [expr] in my opinion (but including formatting behaviour would not be appropriate).