Re: convert numbers
- Posted by Derek Parnell <ddparnell at bigpond.com> Oct 12, 2003
- 493 views
----- 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