Re: convert numbers

new topic     » goto parent     » topic index » view thread      » older message » newer message

----- Original Message ----- 
From: <irvm at ellijay.com>
To: "EUforum" <EUforum at topica.com>
Subject: Re: convert numbers


> 
> 
> On Sunday 12 October 2003 04:37 am, Louis wrote:
> 
> > The value() function does not simply return a number.  It returns two
> > things:  a success/failure message and the number.  Not as easy to use in a
> > calculation as above.  Although value() is more rigorous than s2n(), I
> > suspect s2n() is much faster.
> 
> Indeed, your function is 5 to 8 x faster than value().
> Which makes me think that I should take a look at some of my 
> programs which use value() heavily - maybe I could speed them up 
> significantly by writing my own conversion routines.

But be careful as that routine allows any string to be 'converted', even those
that are not 'numeric'.

Here is the Text-To-Number routine used by win32lib's getNumber() routine.

--/func W32_TextToNumber( sequence text )
--/ret Atom or Sequence: The number represented by the text, or
{Number,ErrorPosition}
--/desc This converts the text into a number.
-- If the text contains invalid characters, zero is returned. The text can
-- have leading and trailing whitespace characters.
--
-- /b "Note 1:" You can supply Hexadecimal values if the value is preceded by
-- a '#' character, Octal values if the value is preceded by a '@' character,
-- and Binary values if the value is preceded by a '!' character. With
-- hexadecimal values, the case of the digits 'A' - 'F' is not important. Also,
-- any period character embedded in the number is used with the correct base.
--
-- /b "Note 2:" Any underscore or comma characters, that are embedded in the
text
-- number are ignored. These can be used to help visual clarity for long
numbers.
--
--/b "Note 3:" You can supply a leading or trailing, minus or plus sign.
--
--/b "Note 4:" You can supply trailing percentage sign(s). Each one present
causes
-- the resulting value to be divided by 100.
--
-- This function can optionally return information about invalid numbers. If
-- /i text has the form of {sequence, integer} then if the integer is nonzero,
-- a sequence is returned. The first element is the value converted, and the
-- second is the position in the text where conversion stopped. If no errors
-- were found then this is zero.
--
--/code
--     sequence rc
--     atom   val
--     rc = W32_TextToNumber({"12.34a", 1})
--     --  rc ---> {12.34, 6} -- Error at position 6
--     rc = W32_TextToNumber({"12.34", 1})
--     --  rc ---> {12.34, 0} -- No errors.
--
--     val = W32_TextToNumber("12.34a")
--     --  val ---> 0
--
--      val = W32_TextToNumber("#f80c") --> 63500
--      val = W32_TextToNumber("#f80c.7aa") --> 63500.47900390625
--      val = W32_TextToNumber("@1703") --> 963
--      val = W32_TextToNumber("!101101") --> 45                            
--      val = W32_TextToNumber("12_583_891") --> 12583891
--      val = W32_TextToNumber("12_583_891%") --> 125838.91
--      val = W32_TextToNumber("12,583,891%%") --> 1258.3891
--
--/endcode                 
constant vDigits = ".0123456789ABCDEF"
global function W32_TextToNumber( sequence text)
    -- get the numeric value of text
    integer dot,sign,tstart,tend, v, note, notify
    atom lhs, rhs, lh, rh 
    integer base, pc
    atom value

    dot = 0
    lh = 0
    lhs = 0
    rh = 0
    rhs = 1
    sign = 0
    note = 0
    base = 10       
    pc = 1

    if  length(text) = 2  and
        sequence(text[1]) and
        integer(text[2])
    then
        notify  = text[2]
        text = text[1]
    else
        notify = 0
    end if

    -- convert the value of the text    
    text = upper(text)
    tstart = 1
    tend = length(text)
    -- Ignore leading whitespace
    while tstart <= tend do
        if equal(text[tstart], '-') and sign = 0 then
            sign = -1
        elsif equal (text[tstart],'+') and sign = 0 then
            sign = 1
        elsif equal (text[tstart],'#') then
            base = 16
        elsif equal (text[tstart],'@') then
            base = 8
        elsif equal (text[tstart],'!') then
            base = 2
        elsif find(text[tstart], {'\t', ' '}) = 0 then
            exit
        end if
        tstart += 1
    end while   
    
    -- Ignore trailing whitespace
    while tstart <= tend do
        if equal(text[tend], '-') and sign = 0 then
            sign = -1
        elsif equal(text[tend],'+') and sign = 0 then
            sign = 1
        elsif equal(text[tend],'%') then
            pc *= 100
        elsif find(text[tend], {'\t', ' '}) = 0 then
            exit
        end if
        tend -= 1
    end while
    
    -- Set the default sign.
    if sign = 0 then
        sign = 1
    end if
    
    for i = tstart to tend do
        if lhs > 0 and find(text[i],"_,") > 0 then
            -- ignore an embedded grouping characters.
        else
            v =  find(text[i], vDigits)
            -- Invalid char so force a zero return.
            if v = 1 then -- A dot found.
                if dot = 0 then
                    dot = 1
                else
                    note = i
                    if notify = 0 then
                        sign = 0
                    end if
                    exit
                end if
            else       
                v -= 1
                if v < 0 or v > base then
                    -- Illegal char found.
                    note = i
                    if notify = 0 then
                        sign = 0
                    end if
                    exit
                else
                    if dot = 1 then
                        rhs *= base
                        rh = (rh * base) + v - 1
                    else
                        lhs += 1
                        lh = (lh * base) + v - 1
                    end if
                end if
             end if
        end if
        
        -- I got to the end without error!
        if i = tend then
            note = 0
        end if
         
    end for
    
    if rh = 0 and pc = 1 then
        -- Common situation optimised for speed.
        value = lh * sign
    else                
        value = ((lh + (rh / rhs)) * sign) / pc
    end if
    
    if notify = 0 then
        return value
    else
        return {value, note}
    end if
end function

-- 
Derek

new topic     » goto parent     » topic index » view thread      » older message » newer message

Search



Quick Links

User menu

Not signed in.

Misc Menu