Search code examples
tclprocedurens2

What is the reason of error "Floating point exception (core dumped)"


I'm trying to save output $result of proc Inverse2 which get scheduled after every one second (it is called inside another procedure,that procedure is rescheduled for 1s that is why Inverse2 procedure) I want to get output which is {x y now} and assign variable to it for latest two instances

x1-> x location at current time (for example at 8.0)
y1-> y location at current time 
x2-> x location at (current time+1) (for example at 9.0)
y2-> y location at (current time+1)

and use for further calculations. Below is a code I have tried but error I got after two iterations is Floating point exception (core dumped). Where I'm doing wrong?

code:

set result {}

proc Inverse2 {m} {
    set op [open output.tr w]
    global result
    global ns
    set now [$ns now]
    lassign [lindex $m 0 2] x1 
    lassign [lindex $m 0 3] y1 
    lassign [lindex $m 0 6] d1
    lassign [lindex $m 1 2] x2  
    lassign [lindex $m 1 3] y2  
    lassign [lindex $m 1 6] d2  
    lassign [lindex $m 2 2] x3
    lassign [lindex $m 2 3] y3  
    lassign [lindex $m 2 6] d3   

    set mt {{? ?} {? ?}}
    lset mt 0 0 [expr 2*($x1-$x2)]
    lset mt 0 1 [expr 2*($y1-$y2)]
    lset mt 1 0 [expr 2*($x1-$x3)]
    lset mt 1 1 [expr 2*($y1-$y3)]
    set const {{?} {?}}
    lset const 0 [expr {(pow($x1,2)+pow($y1,2)-pow($d1,2))-(pow($x2,2)+pow($y2,2)-pow($d2,2))}]
    lset const 1 [expr {(pow($x1,2)+pow($y1,2)-pow($d1,2))-(pow($x3,2)+pow($y3,2)-pow($d3,2))}]
    #puts $result "$const"
    # puts $result "$mt"
    set x [expr {double([lindex [Inverse3 $mt] 0 0]  * [lindex $const 0]
                       + [lindex [Inverse3 $mt] 0 1] * [lindex $const 1])}]
    set y [expr {double([lindex [Inverse3 $mt] 1 0]  * [lindex $const 0]
                       + [lindex [Inverse3 $mt] 1 1] * [lindex $const 1])}]

    lappend result "$x $y $now"
    puts $result
    for {set i 0} {$i< [llength $result]} {incr i} {  #for latest two instances
        for {set j 1} {$i< [llength $result]} {incr j} {     
            set X1 [lindex $result $i 0]
            set Y1 [lindex $result $i 1]
            if {[llength $result] >1} {    #to ensure length of list is greater than 1
                set X2 [lindex $result $j 0]
                set Y2 [lindex $result $j 1]

                set v [expr hypot($X2-$X1,$Y2-$Y1)/ ($now-($now-1))]
                set theta [expr acos(($X2-$X1)/(hypot($X2-$X1,$Y2-$Y1)))]
                set Xp [expr ($X2+($v*$now*cos($theta)))]
                set Yp [expr ($Y2+($v*$now*sin($theta)))]
                puts "$Xp $Yp"
            }
            break
        }
    }
}

Solution

  • Floating point exceptions can come from a few different things. In general, the main culprit is doing something awful like dividing zero by zero. However, Tcl is usually pretty good at ensuring that such things don't crash your program entirely, and instead just generate errors you can catch. Whatever is going on is therefore either one of the trickier cases, or due to running in ns2 and that turning signalling floating point errors on (Tcl's standard implementation disables them precisely to avoid probably-unwarranted fatal crashes).

    If it is the latter, moving the processing out of the process into a standard tclsh is the easiest way forward. We can make stronger guarantees about the correctness of behaviour there as we have more control of tricky things like FPU flags.

    But if it is the former… the problem should lie in these lines:

                set v [expr hypot($X2-$X1,$Y2-$Y1)/ ($now-($now-1))]
                set theta [expr acos(($X2-$X1)/(hypot($X2-$X1,$Y2-$Y1)))]
                set Xp [expr ($X2+($v*$now*cos($theta)))]
                set Yp [expr ($Y2+($v*$now*sin($theta)))]
    

    Of the lines there, the one that looks most suspicious is the calculation of theta. There's several problems with what you're doing (e.g., it won't handle some quadrants correctly due to trigonometric periodicities) but the big nasty is that you've got a division in there that will be by zero if two successive positions are the same. Given that you're able to use hypot(), computing the angle is by far best computed with atan2(), as that deals with tricky edge cases much better (e.g., it has no problems with awful infinities). Try this:

                set theta [expr { atan2($Y2-$Y1, $X2-$X1) }]
    

    Also put your expressions in {braces} as I've done above. It permits Tcl to bytecode-compile the expression and makes your code quite a bit faster. It also lets you put spaces in the expression safely, which aids readability a lot even when you're not splitting over multiple lines, and ensures you get (much!) better error messages if you ever happen to use a variable holding a non-numeric value in your expression. In short, it's easy to do and makes your code much better.


    Other minor issues

    1. Do you expect ($now-($now-1)) to ever compute anything other than 1? Or at least a value very close to 1.0, given that you're dealing with floating point numbers for simulation time? I think your calculation of v can be safely simplified down to straight use of hypot().

    2. These two nested loops look odd:

      for {set i 0} {$i< [llength $result]} {incr i} {
          for {set j 1} {$i< [llength $result]} {incr j} {     
      

      I think you either mean to do this:

      for {set i 0} {$i< [llength $result]} {incr i} {
          for {set j 0} {$j< [llength $result]} {incr j} {     
              if {$i == $j} continue; # Skip the diagonal in the comparison matrix
      

      or this:

      for {set i 0} {$i< [llength $result]} {incr i} {
          for {set j [expr {$i + 1}]} {$j< [llength $result]} {incr j} {
              # Just the upper triangle of the comparison matrix
      

      depending on whether the rest of the code should compare values from both ways round (but never with itself), or just one way round. The latter does less work, but might be wrong if comparisons aren't symmetric (which depends on the details of what you're up to).