Comp3 data conversion

Function to convert comp3 packed decimal data (aka BCD) (see [L1 ]) to a string.

Note: This function assumes it's signed data.

 proc comp3tostring {packed_dec_data data_len} {
    set val 0
    set ret_sign {}

    for {set byte_ndx 0} {$byte_ndx < $data_len} {incr byte_ndx} {
       scan [string index $packed_dec_data $byte_ndx] %c byte
       set byte [format "%02.2x" $byte]
       if {$byte_ndx == [expr {$data_len - 1}]} {
          append val [format "%x" [string index $byte 0]]
          if {[string index $byte 1] == {d}} {
             set ret_sign {-}
          }
       } else {
          append val "[format "%x%x" [string index $byte 0] [string index $byte 1]]"
       }
    }

    return "$ret_sign$val"
 }

RS 2009-03-24: Here's a simpler alternative:

 proc comp3int x {
    binary scan $x H* hex
    scan $hex %d int; # make sure it's int size, else use '%ld' for longs.
    switch -- [string index $hex end] {
       c {}
       d {set int -$int}
       default {return -code error "bad comp3 number: $x"}
    }

    return $int
 }

Richard, after looking at the code again, I see you include the sign digit as part of the numbers value. This function would work fine on unsigned data but not signed. Made changes to make that one work.


RS Of course it worked before I posted it... (tested with the examples on the page linked above). I use scan for two purposes: strip off leading zeroes except maybe a single one, and: stop parsing at the first non-decimal character (the last char can according to spec only be "c" or "d"). Demo:

 % scan 01234d %d
 1234

JSB Okay... I see my problem now. My function did not care about return data size. Your code worked for me on most data, but not all. After a quick look at the data in question, I see that it was failing on very large (a long) numbers (bad data, bad data!;). Changing the scan to a '%ld' made it work fine for me. Thanks!


JSB Last night I worked on a new version combining aspects of Richards and my code. In working with packed decimal numbers, you will probably have to add the decimal '.' to the number before display or other use. Not having the leading zeros makes it inherently harder to add the decimal to the number. This version fixes that.

 proc comp3tostring x {
   binary scan $x H* hex
   set digits [string range $hex 0 end-1]

   if {[string index $hex end] == {d}} {
      set digits -$digits
   }

   return $digits
 }