538 lines
17 KiB
Plaintext
538 lines
17 KiB
Plaintext
''*****************************************
|
|
''* Floating-Point <-> Strings v 1.2 *
|
|
''* Single-precision IEEE-754 *
|
|
''* Authors: Chip Gracey and Cam Thompson *
|
|
''* (C) 2006 Parallax, Inc. *
|
|
''* See end of file for terms of use. *
|
|
''*****************************************
|
|
|
|
'' v1.0 - 01 May 2006 - original version
|
|
'' v1.1 - 12 Jul 2006 - added FloatToFormat routine
|
|
'' v1.2 - 06 Mar 2009 - added StringToFloat [mpark]
|
|
VAR
|
|
|
|
long p, digits, exponent, integer, tens, zeros, precision
|
|
long positive_chr, decimal_chr, thousands_chr, thousandths_chr
|
|
byte float_string[20]
|
|
|
|
|
|
OBJ
|
|
' The F object can be FloatMath, Float32 or Float32Full depending on the application
|
|
F : "FME"'"BasF32"
|
|
|
|
PUB FloatToString(Single) : StringPtr
|
|
|
|
''Convert floating-point number to string
|
|
''
|
|
'' entry:
|
|
'' Single = floating-point number
|
|
''
|
|
'' exit:
|
|
'' StringPtr = pointer to resultant z-string
|
|
''
|
|
'' Magnitudes below 1e+12 and within 1e-12 will be expressed directly;
|
|
'' otherwise, scientific notation will be used.
|
|
''
|
|
'' examples results
|
|
'' -----------------------------------------
|
|
'' FloatToString(0.0) "0"
|
|
'' FloatToString(1.0) "1"
|
|
'' FloatToString(-1.0) "-1"
|
|
'' FloatToString(^^2.0) "1.414214"
|
|
'' FloatToString(2.34e-3) "0.00234"
|
|
'' FloatToString(-1.5e-5) "-0.000015"
|
|
'' FloatToString(2.7e+6) "2700000"
|
|
'' FloatToString(1e11) "100000000000"
|
|
'' FloatToString(1e12) "1.000000e+12"
|
|
'' FloatToString(1e-12) "0.000000000001"
|
|
'' FloatToString(1e-13) "1.000000e-13"
|
|
|
|
'perform initial setup
|
|
StringPtr := Setup(Single)
|
|
|
|
'eliminate trailing zeros
|
|
if integer
|
|
repeat until integer // 10
|
|
integer /= 10
|
|
tens /= 10
|
|
digits--
|
|
else
|
|
digits~
|
|
|
|
'express number according to exponent
|
|
case exponent
|
|
'in range left of decimal
|
|
11..0:
|
|
AddDigits(exponent + 1)
|
|
'in range right of decimal
|
|
-1..digits - 13:
|
|
zeros := -exponent
|
|
AddDigits(1)
|
|
'out of range, do scientific notation
|
|
other:
|
|
DoScientific
|
|
|
|
'terminate z-string
|
|
byte[p]~
|
|
|
|
|
|
PUB FloatToScientific(Single) : StringPtr
|
|
|
|
''Convert floating-point number to scientific-notation string
|
|
''
|
|
'' entry:
|
|
'' Single = floating-point number
|
|
''
|
|
'' exit:
|
|
'' StringPtr = pointer to resultant z-string
|
|
''
|
|
'' examples results
|
|
'' -------------------------------------------------
|
|
'' FloatToScientific(1e-9) "1.000000e-9"
|
|
'' FloatToScientific(^^2.0) "1.414214e+0"
|
|
'' FloatToScientific(0.00251) "2.510000e-3"
|
|
'' FloatToScientific(-0.0000150043) "-1.500430e-5"
|
|
|
|
'perform initial setup
|
|
StringPtr := Setup(Single)
|
|
|
|
'do scientific notation
|
|
DoScientific
|
|
|
|
'terminate z-string
|
|
byte[p]~
|
|
|
|
|
|
PUB FloatToMetric(Single, SuffixChr1,SuffixChr2) : StringPtr | x, y
|
|
|
|
''Convert floating-point number to metric string
|
|
''
|
|
'' entry:
|
|
'' Single = floating-point number
|
|
'' SuffixChr = optional ending character (0=none)
|
|
''
|
|
'' exit:
|
|
'' StringPtr = pointer to resultant z-string
|
|
''
|
|
'' Magnitudes within the metric ranges will be expressed in metric
|
|
'' terms; otherwise, scientific notation will be used.
|
|
''
|
|
'' range name symbol
|
|
'' -----------------------
|
|
'' 1e24 yotta Y
|
|
'' 1e21 zetta Z
|
|
'' 1e18 exa E
|
|
'' 1e15 peta P
|
|
'' 1e12 tera T
|
|
'' 1e9 giga G
|
|
'' 1e6 mega M
|
|
'' 1e3 kilo k
|
|
'' 1e0 - -
|
|
'' 1e-3 milli m
|
|
'' 1e-6 micro u
|
|
'' 1e-9 nano n
|
|
'' 1e-12 pico p
|
|
'' 1e-15 femto f
|
|
'' 1e-18 atto a
|
|
'' 1e-21 zepto z
|
|
'' 1e-24 yocto y
|
|
''
|
|
'' examples results
|
|
'' ------------------------------------
|
|
'' metric(2000.0, "m") "2.000000km"
|
|
'' metric(-4.5e-5, "A") "-45.00000uA"
|
|
'' metric(2.7e6, 0) "2.700000M"
|
|
'' metric(39e31, "W") "3.9000e+32W"
|
|
|
|
'perform initial setup
|
|
StringPtr := Setup(Single)
|
|
|
|
'determine thousands exponent and relative tens exponent
|
|
x := (exponent + 45) / 3 - 15
|
|
y := (exponent + 45) // 3
|
|
|
|
'if in metric range, do metric
|
|
if ||x =< 8
|
|
'add digits with possible decimal
|
|
AddDigits(y + 1)
|
|
'if thousands exponent not 0, add metric indicator
|
|
byte[p++] := " "
|
|
if x
|
|
byte[p++] := metric[x]
|
|
'if out of metric range, do scientific notation
|
|
else
|
|
DoScientific
|
|
|
|
'if SuffixChr not 0, add SuffixChr
|
|
if SuffixChr1
|
|
byte[p++] := SuffixChr1
|
|
if suffixChr2
|
|
byte[p++] := SuffixChr2
|
|
'terminate z-string
|
|
byte[p]~
|
|
|
|
|
|
PUB FloatToFormat(single, width, dp) : stringptr | n, w2
|
|
|
|
''Convert floating-point number to formatted string
|
|
''
|
|
'' entry:
|
|
'' Single = floating-point number
|
|
'' width = width of field
|
|
'' dp = number of decimal points
|
|
''
|
|
'' exit:
|
|
'' StringPtr = pointer to resultant z-string
|
|
''
|
|
'' asterisks are displayed for format errors
|
|
'' leading blank fill is used
|
|
|
|
' get string pointer
|
|
stringptr := p := @float_string
|
|
|
|
' width must be 1 to 9, dp must be 0 to width-1
|
|
w2 := width := width #> 1 <# 9
|
|
dp := dp #> 0 <# (width - 2)
|
|
if dp > 0
|
|
w2--
|
|
if single & $8000_0000 or positive_chr
|
|
w2--
|
|
|
|
' get positive scaled integer value
|
|
n := F.FRound(F.FMul(single & $7FFF_FFFF , F.FFloat(teni[dp])))
|
|
|
|
if n => teni[w2]
|
|
' if format error, display asterisks
|
|
repeat while width
|
|
if --width == dp
|
|
if decimal_chr
|
|
byte[p++] := decimal_chr
|
|
else
|
|
byte[p++] := "."
|
|
else
|
|
byte[p++] := "*"
|
|
byte[p]~
|
|
|
|
else
|
|
' store formatted number
|
|
p += width
|
|
byte[p]~
|
|
|
|
repeat width
|
|
byte[--p] := n // 10 + "0"
|
|
n /= 10
|
|
if --dp == 0
|
|
if decimal_chr
|
|
byte[--p] := decimal_chr
|
|
else
|
|
byte[--p] := "."
|
|
if n == 0 and dp < 0
|
|
quit
|
|
|
|
' store sign
|
|
if single & $80000000
|
|
byte[--p] := "-"
|
|
elseif positive_chr
|
|
byte[--p] := positive_chr
|
|
' leading blank fill
|
|
repeat while p <> stringptr
|
|
byte[--p] := " "
|
|
|
|
PUB SetPrecision(NumberOfDigits)
|
|
|
|
''Set precision to express floating-point numbers in
|
|
''
|
|
'' NumberOfDigits = Number of digits to round to, limited to 1..7 (7=default)
|
|
''
|
|
'' examples results
|
|
'' -------------------------------
|
|
'' SetPrecision(1) "1e+0"
|
|
'' SetPrecision(4) "1.000e+0"
|
|
'' SetPrecision(7) "1.000000e+0"
|
|
|
|
precision := NumberOfDigits
|
|
|
|
|
|
PUB SetPositiveChr(PositiveChr)
|
|
|
|
''Set lead character for positive numbers
|
|
''
|
|
'' PositiveChr = 0: no character will lead positive numbers (default)
|
|
'' non-0: PositiveChr will lead positive numbers (ie " " or "+")
|
|
''
|
|
'' examples results
|
|
'' ----------------------------------------
|
|
'' SetPositiveChr(0) "20.07" "-20.07"
|
|
'' SetPositiveChr(" ") " 20.07" "-20.07"
|
|
'' SetPositiveChr("+") "+20.07" "-20.07"
|
|
|
|
positive_chr := PositiveChr
|
|
|
|
|
|
PUB SetDecimalChr(DecimalChr)
|
|
|
|
''Set decimal point character
|
|
''
|
|
'' DecimalChr = 0: "." will be used (default)
|
|
'' non-0: DecimalChr will be used (ie "," for Europe)
|
|
''
|
|
'' examples results
|
|
'' ----------------------------
|
|
'' SetDecimalChr(0) "20.49"
|
|
'' SetDecimalChr(",") "20,49"
|
|
|
|
decimal_chr := DecimalChr
|
|
|
|
|
|
PUB SetSeparatorChrs(ThousandsChr, ThousandthsChr)
|
|
|
|
''Set thousands and thousandths separator characters
|
|
''
|
|
'' ThousandsChr =
|
|
'' 0: no character will separate thousands (default)
|
|
'' non-0: ThousandsChr will separate thousands
|
|
''
|
|
'' ThousandthsChr =
|
|
'' 0: no character will separate thousandths (default)
|
|
'' non-0: ThousandthsChr will separate thousandths
|
|
''
|
|
'' examples results
|
|
'' -----------------------------------------------------------
|
|
'' SetSeparatorChrs(0, 0) "200000000" "0.000729345"
|
|
'' SetSeparatorChrs(0, "_") "200000000" "0.000_729_345"
|
|
'' SetSeparatorChrs(",", 0) "200,000,000" "0.000729345"
|
|
'' SetSeparatorChrs(",", "_") "200,000,000" "0.000_729_345"
|
|
|
|
thousands_chr := ThousandsChr
|
|
thousandths_chr := ThousandthsChr
|
|
|
|
|
|
PUB StringToFloat(strptr) : flt | significand, ssign, places, exp, esign
|
|
{{
|
|
Converts string to floating-point number
|
|
entry:
|
|
strptr = pointer to z-string
|
|
|
|
exit:
|
|
flt = floating-point number
|
|
|
|
|
|
Assumes the following floating-point syntax: [-] [0-9]* [ . [0-9]* ] [ e|E [-|+] [0-9]* ]
|
|
┌── ┌───── ┌─────────── ┌───────────────────
|
|
│ │ │ │ ┌──── ┌─────
|
|
Optional negative sign ────────────────────┘ │ │ │ │ │
|
|
Digits ────────────────────────────────────────┘ │ │ │ │
|
|
Optional decimal point followed by digits ────────────┘ │ │ │
|
|
Optional exponent ─────────────────────────────────────────────────┘ │ │
|
|
optional exponent sign ────────────────────────────────────────────────┘ │
|
|
exponent digits ─────────────────────────────────────────────────────────────┘
|
|
|
|
Examples of recognized floating-point numbers:
|
|
"123", "-123", "123.456", "123.456e+09"
|
|
Conversion stops as soon as an invalid character is encountered. No error-checking.
|
|
|
|
Based on Ariba's StrToFloat in http://forums.parallax.com/forums/default.aspx?f=25&m=280607
|
|
Expanded by Michael Park
|
|
}}
|
|
significand~
|
|
ssign~
|
|
exp~
|
|
esign~
|
|
places~
|
|
repeat
|
|
case byte[strptr]
|
|
"-":
|
|
ssign~~
|
|
".":
|
|
places := 1
|
|
"0".."9":
|
|
significand := significand * 10 + byte[strptr] - "0"
|
|
if places
|
|
++places 'count decimal places nach dem Dezimalpunkt
|
|
|
|
"e", "E":
|
|
++strptr ' skip over the e or E
|
|
repeat
|
|
case byte[strptr]
|
|
"+":
|
|
' ignore
|
|
"-":
|
|
esign~~
|
|
"0".."9":
|
|
exp := exp * 10 + byte[strptr] - "0"
|
|
other:
|
|
quit
|
|
++strptr
|
|
quit
|
|
other:
|
|
quit
|
|
++strptr
|
|
|
|
if ssign
|
|
-significand
|
|
flt := f.FFloat(significand)
|
|
|
|
ifnot esign ' tenf table is in decreasing order, so the sign of exp is reversed
|
|
-exp
|
|
|
|
if places
|
|
exp += places - 1
|
|
|
|
flt := f.FMul(flt, tenf[exp]) 'adjust flt's decimal point
|
|
|
|
|
|
PRI Setup(single) : stringptr
|
|
|
|
'limit digits to 1..7
|
|
if precision
|
|
digits := precision #> 1 <# 7
|
|
else
|
|
digits := 7
|
|
|
|
'initialize string pointer
|
|
p := @float_string
|
|
|
|
'add "-" if negative
|
|
if single & $80000000
|
|
byte[p++] := "-"
|
|
'otherwise, add any positive lead character
|
|
elseif positive_chr
|
|
byte[p++] := positive_chr
|
|
|
|
'clear sign and check for 0
|
|
if single &= $7FFFFFFF
|
|
|
|
'not 0, estimate exponent
|
|
exponent := ((single << 1 >> 24 - 127) * 77) ~> 8
|
|
|
|
'if very small, bias up
|
|
if exponent < -32
|
|
single := F.FMul(single, 1e13)
|
|
exponent += result := 13
|
|
|
|
'determine exact exponent and integer
|
|
repeat
|
|
integer := F.FTRUNC(F.FMul(single, tenf[exponent - digits + 1]))
|
|
if integer < teni[digits - 1]
|
|
exponent--
|
|
elseif integer => teni[digits]
|
|
exponent++
|
|
else
|
|
exponent -= result
|
|
quit
|
|
|
|
'if 0, reset exponent and integer
|
|
else
|
|
exponent~
|
|
integer~
|
|
|
|
'set initial tens and clear zeros
|
|
tens := teni[digits - 1]
|
|
zeros~
|
|
|
|
'return pointer to string
|
|
stringptr := @float_string
|
|
|
|
|
|
PRI DoScientific
|
|
|
|
'add digits with possible decimal
|
|
AddDigits(1)
|
|
'add exponent indicator
|
|
byte[p++] := "e"
|
|
'add exponent sign
|
|
if exponent => 0
|
|
byte[p++] := "+"
|
|
else
|
|
byte[p++] := "-"
|
|
||exponent
|
|
'add exponent digits
|
|
if exponent => 10
|
|
byte[p++] := exponent / 10 + "0"
|
|
exponent //= 10
|
|
byte[p++] := exponent + "0"
|
|
|
|
|
|
PRI AddDigits(leading) | i
|
|
|
|
'add leading digits
|
|
repeat i := leading
|
|
AddDigit
|
|
'add any thousands separator between thousands
|
|
if thousands_chr
|
|
i--
|
|
if i and not i // 3
|
|
byte[p++] := thousands_chr
|
|
'if trailing digits, add decimal character
|
|
if digits
|
|
AddDecimal
|
|
'then add trailing digits
|
|
repeat while digits
|
|
'add any thousandths separator between thousandths
|
|
if thousandths_chr
|
|
if i and not i // 3
|
|
byte[p++] := thousandths_chr
|
|
i++
|
|
AddDigit
|
|
|
|
|
|
PRI AddDigit
|
|
|
|
'if leading zeros, add "0"
|
|
if zeros
|
|
byte[p++] := "0"
|
|
zeros--
|
|
'if more digits, add current digit and prepare next
|
|
elseif digits
|
|
byte[p++] := integer / tens + "0"
|
|
integer //= tens
|
|
tens /= 10
|
|
digits--
|
|
'if no more digits, add "0"
|
|
else
|
|
byte[p++] := "0"
|
|
|
|
|
|
PRI AddDecimal
|
|
|
|
if decimal_chr
|
|
byte[p++] := decimal_chr
|
|
else
|
|
byte[p++] := "."
|
|
|
|
|
|
DAT
|
|
long 1e+38, 1e+37, 1e+36, 1e+35, 1e+34, 1e+33, 1e+32, 1e+31
|
|
long 1e+30, 1e+29, 1e+28, 1e+27, 1e+26, 1e+25, 1e+24, 1e+23, 1e+22, 1e+21
|
|
long 1e+20, 1e+19, 1e+18, 1e+17, 1e+16, 1e+15, 1e+14, 1e+13, 1e+12, 1e+11
|
|
long 1e+10, 1e+09, 1e+08, 1e+07, 1e+06, 1e+05, 1e+04, 1e+03, 1e+02, 1e+01
|
|
|
|
tenf long 1e+00, 1e-01, 1e-02, 1e-03, 1e-04, 1e-05, 1e-06, 1e-07, 1e-08, 1e-09
|
|
long 1e-10, 1e-11, 1e-12, 1e-13, 1e-14, 1e-15, 1e-16, 1e-17, 1e-18, 1e-19
|
|
long 1e-20, 1e-21, 1e-22, 1e-23, 1e-24, 1e-25, 1e-26, 1e-27, 1e-28, 1e-29
|
|
long 1e-30, 1e-31, 1e-32, 1e-33, 1e-34, 1e-35, 1e-36, 1e-37, 1e-38
|
|
|
|
teni long 1, 10, 100, 1_000, 10_000, 100_000, 1_000_000, 10_000_000, 100_000_000, 1_000_000_000
|
|
|
|
byte "yzafpnum"
|
|
metric byte 0
|
|
byte "kMGTPEZY"
|
|
|
|
{{
|
|
┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
|
│ TERMS OF USE: MIT License │
|
|
├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
|
|
│Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation │
|
|
│files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, │
|
|
│modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software│
|
|
│is furnished to do so, subject to the following conditions: │
|
|
│ │
|
|
│The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.│
|
|
│ │
|
|
│THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE │
|
|
│WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR │
|
|
│COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │
|
|
│ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │
|
|
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
|
}}
|