Next / Previous / Contents / TCC Help System / NM Tech homepage

10.5. MixedUnits.format(): Format mixed units

This method takes a mixed-units value as a sequence of numbers, and returns a list of strings containing those numbers formatted in the way described in the specification.

sidereal.py
# - - -   M i x e d U n i t s . f o r m a t

    def format ( self, coeffs, decimals=0, lz=False ):
        """Format mixed units.

          [ (coeffs is a sequence of numbers as returned by
            MixedUnits.singleToMix()) and
            (decimals is a nonnegative integer) and
            (lz is a bool) ->
              return a list of strings corresponding to the values
              of coeffs, with all the values but the last formatted
              as integers, all values zero padded iff lz is true,
              and the last value with (decimals) digits after the
              decimal point ]
        """

The important feature of this method is to prevent ever returning a last string which is greater than equal to self.factors[-1]. For example, in the degrees-minutes-seconds system, if the seconds value is 59.9999, we want to avoid ever showing a value of "60", "60.0", or anything like that.

We'll implement this constraint by truncating any extra digits, so 59.999 will display as "59", "59.9", "59.99", and so forth for the various values of decimals.

For the decimals=0 case, if the fractional part of the last value is 0.5 or greater, formatting with "%.0f" will round the value undesirably; in that case, we subtract 0.5 from the last value to effectively truncate instead of rounding.

For decimals=1, if the fractional part is 0.95 or greater, formatting with "%.1f" will round. In that case, we subtract 0.05 from the last value.

Progressing toward a general case, here is a table showing the first few values of decimals, the range of fraction values that will cause rounding, and the fudge factor to be subtracted from the last value to prevent rounding.

decimalsRounding occursFudge factor
0>= 0.50.5
1>= 0.950.05
2>= 0.9950.005
3>= 0.99950.0005

Generalizing, whenever the fractional part of the last value in the coeffs sequence is greater than or equal to 1-0.5×10-decimals, we must subtract 0.5×10-decimals from the last value.

First, we set coeffList to a copy of coeffs, right-padded with zero elements to standard length. Then we use a list comprehension to format all the values but the last as integers.

sidereal.py
        #-- 1 --
        coeffList  =  self.__pad ( coeffs )

        #-- 2 --
        # [ result  :=  the values from coeffList[:-1] formatted
        #               as integers ]
        if  lz:  fmt = "%02d"
        else:    fmt = "%d"
        result  =  [ fmt % x
                     for x in coeffList[:-1] ]

Next we separate the last value into whole and fractional parts, and set fuzz to the quantity 0.5×10-decimals.

sidereal.py
        #-- 2 --
        # [ whole  :=  whole part of coeffList[-1]
        #   frac   :=  fractional part of coeffList[-1]
        #   fuzz   :=  0.5 * (10 ** (-decimals) ]
        whole, frac  =  divmod ( float(coeffList[-1]), 1.0 )
        fuzz = 0.5 * (10.0 ** (-decimals))

Now apply the test and adjust if necessary.

sidereal.py
        #-- 3 --
        # [ if  frac >= (1-fuzz) ->
        #     result  +:=  [whole+frac-fuzz], formatted with
        #                  (decimals) digits after the decimal
        #   else ->
        #     result  +=   coeffList[-1], formatted with (decimals)
        #                  digits after the decimal ]
        if  frac >= (1.0-fuzz):
            corrected  =  whole + frac - fuzz
        else:
            corrected  =  coeffList[-1]

Next, the formatting. There are three cases.

sidereal.py
        #-- 4 --
        # [ if lz ->
        #     s  :=  corrected, formatted with 2 digits of left-zero
        #            padding and (decimals) precision
        #   else ->
        #     s  :=  corrected, formatted with (decimals) precision ]
        if  lz:
            if  decimals:  n = decimals+3
            else:          n = decimals+2

            s  =  "%0*.*f" % (n, decimals, corrected)
        else:
            s  =  "%.*f" % (decimals, corrected)
              
        #-- 5 --
        result.append ( s )

        #-- 6 --
        return result