Hi Michal, Joel,

I've had format.r up on rebol.org for a long time, hoping someone
would give me a good idea on how to improve it. Joel's ROUND is a
great idea. First, here's a version of ROUND that can handle values
where to-integer chokes, fractions less than one, and negative numbers:

round: func [n [number!] p [integer!] /local factor neg] [
        if negative? n [n: (- n)  neg: true]
    factor: 10.0 ** p
        n: n * factor + 0.5
        n: n - (n // 1) / factor
        either neg [- n][n]
]

Now I've made a new version of FORMAT, which uses the logic of ROUND and
produces a string representation in decimal format, pads with zeros, etc.
It also will format all the values in a block in one go. Note that REBOL
forms any value less than .1 into exponential notation, you have to use a
special formatting function to get it into decimal format. It's really
incredible that a language that prides itself on being readable to humans
doesn't have a standard way to do this.

>> pi / 100
== 3.14159265358979E-2
>> round pi / 100 5
== 3.142E-2
>> format pi / 100 5
== "0.03142"

Note that FORMAT has a refinement /full which won't work without the
function FULL-FORM. I've been working on this with Larry Palmiter and
Gerald Goertzel. It'll be available on rebol.org in a couple of days.
FULL-FORM is meant to give the simplest possible string representation
of a decimal value that will load back to exactly the same value.

>> form pi
== "3.14159265358979"
>> pi - load form pi
== 3.10862446895044E-15       ; not equal!
>> full-form pi
== "3.141592653589793"
>> pi - load full-form pi
== 0                          ; equal!

Have fun,
Eric

===========

format: func [
    {CONVERTS Rebol data into formatted strings.}
    item "value or block of values to format - block values are reduced first"
    decimal [integer! block! unset!]
        {optional decimal places to leave (2 by default)}
    width [integer! block! unset!]
        {optional width of formatted string}
    /full  "use extra precision in forming decimal values"
    /local n neg p factor exponent
;
; Examples:
;
;>> print format [pi exp 1]
;3.14 2.72
;>> print format [pi exp 1] 4      ; specifying precision
;3.1416 2.7183
;>> print format [pi exp 1][4 8]   ; using different precisions
;3.1416 2.71828183
;>> print format [pi exp 1] 4 10   ; pad with spaces
;    3.1416     2.7183
;>> format pi * 1000 -2            ; round off to nearest hundred
;== "3100"
;>> print format pi 16
;3.1415926535897900                ; precision given by FORM
;>> print format/full pi 16
;3.1415926535897930                ; maximum precision
;>> print format exp 1 16
;2.7182818284590500
;>> print format/precise exp 1 16
;2.7182818284590452
;
; Note: DECIMAL must be specified before specifying WIDTH. Making DECIMAL and
; WIDTH optional is for convenience when entered at the prompt. Both should
; be specified within programs.
;
][
    if not value? 'decimal [ decimal: 2 ]  ; supply defaults if needed
    if not value? 'width   [ width: 0 ]
    if object? item        [ item: next second item ]
    either any-block? item [
        if integer? decimal    [ decimal: reduce [decimal] ]
        if integer? width      [ width: reduce [width] ]
        n: to block! reduce item   ; won't change values in lists and hashes
        while [not tail? n][
            either any-block? first n [   ; recurse with all format values
                change/only n format first n head decimal head width
            ][                                  ; use current format value
                change/only n format first n first decimal first width
            ]
            n: next n
                            ; get next format values, or reuse the last one
            if 1 < length? decimal [ decimal: next decimal ]
            if 1 < length? width   [ width:   next width   ]
        ]
    ][
        if block? decimal      [ decimal: first decimal ]
        if block? width        [ width:   first width ]
        if not number? decimal [ decimal: 2 ]
        if not number? width   [ width: 0 ]
        either number? item [
            if negative? item [item: (- item)  neg: true]
            factor: 10 ** decimal
            item: item * factor + 0.5
            item: item - (item // 1)
            n: either full [full-form item][form item]
            if p: find n "." [
                if 2 <> index? p [make error! "FORMAT: funny decimal point"]
                remove p
                p: remove find n "E"
                exponent: to integer! p
                clear p
                insert/dup tail n "0" exponent + 1 - length? n
            ]
            if positive? decimal [
                insert/dup n "0" 1 + decimal - length? n
                insert skip tail n (- decimal) "."
            ]
            if all [
                negative? decimal
                n <> "0"
            ][insert/dup tail n "0" (- decimal)]
            if neg [insert n "-"]
        ][
            n: form item
        ]
        ; pad with spaces
        either width < 0 [
            insert/dup tail n " " 0 - width - length? n
        ][
            insert/dup n " " width - length? n
        ]
    ]
    head n
]

Reply via email to