Search code examples
bashshdc

dc(1) and leading zeros


I've got a shell script and I do some calculations with dc(1).

I need to have one number printed with leading zeros; I can't find an easy and straightforward way to do this with dc itself, but the manpage does mention:

Z
Pops a value off the stack, calculates the number of digits it has (or number of characters, if it is a string) and pushes that number. The digit count for a number does not include any leading zeros, even if those appear to the right of the radix point.

Which sort of implies there is an easy and straightforward way ...

I know there are a zillion-and-one method of accomplishing this, and I the script is running happily with one of them. I'm just curious ;-)


Solution

  • Give this a try:

    Enter:

    [lc1+dsc0nld>b]sb
    [sddZdscld>bp]sa
    999
    12lax
    

    Output:

    000000999
    

    Enter:

    3lax
    

    Output:

    999
    

    The original number is left on the stack after the macro ends. Registers used: a (macro), b (macro), c (count), d (digits).

    Explanation:

    Macro a does the setup, calls b and prints the original number.

    • sd - store the number of digits to be output in register d
    • dZ - duplicate the original number and push the count of its digits
    • dsc - duplicate that count and store it in register c
    • ld>b - load the desired digits from register d, if it's greater than the count then call macro b
    • p - print the original number

    Macro b outputs zeros until the count is greater than the number of desired digits

    • lc1+ - load the count from register c and increment it
    • dsc - duplicate the count and store it back to register c
    • 0n - output a zero without a newline
    • ld>b - load the desired digits from register d, if it's still greater than the incremented count then loop back to run macro b again, otherwise it will return to the caller (macro a)

    To use an arbitrary leading character:

    [lclkZ+dsclknld>b]sb
    [sksddZdscld>bp]sa
    999 14 [ ] lax
               999
    [abc] 12 [-] lax
    ---------abc
    

    In addition to the other registers, it uses k to store the character (which could actually be more than one):

    [XYZ] 6 [def] lax
    defXYZ
    8 [ab] lax
    abababXYZ
    4 [ghjikl] lax
    ghijklXYZ
    

    The fill strings are used whole so the result may be longer than you asked for if the desired length number is larger than the length of the original string, but smaller than the length of the two strings (or integer multiples).