String to Float
I may have posed this question before but I don't think I got an answer.·
I have a string (GPS) and I want to convert this to a floating value.· i can't find anything that converts a string pointer to a floating value.· I wrote a small program to do this but it stops at 4 decimal places.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
·- Ouch, thats not suppose to be hot!··
Michael King
Application Engineer
R&D
Digital Technology Group
I have a string (GPS) and I want to convert this to a floating value.· i can't find anything that converts a string pointer to a floating value.· I wrote a small program to do this but it stops at 4 decimal places.
PUB Str2Float(ptr) :c | a ,i , B ,k
a:=1
c:=0
a:=fp.ffloat(a)
c:=fp.ffloat(c)
repeat i from 0 to strsize(ptr)-1
if byte[noparse][[/noparse]ptr+i]=="."
quit
if byte[noparse][[/noparse]ptr+i]<>"-"
a:=fp.fdiv(a,10.0)
a:=fp.fmul(a,10.0)
k:=0
repeat i from 0 to strsize(ptr)
b:=0.0
if byte[noparse][[/noparse]ptr+i]=="-"
k:=1
if byte[noparse][[/noparse]ptr+i]=="1"
b:=fp.fdiv(1.0,a)
if byte[noparse][[/noparse]ptr+i]=="2"
b:=fp.fdiv(2.0,a)
if byte[noparse][[/noparse]ptr+i]=="3"
b:=fp.fdiv(3.0,a)
if byte[noparse][[/noparse]ptr+i]=="4"
b:=fp.fdiv(4.0,a)
if byte[noparse][[/noparse]ptr+i]=="5"
b:=fp.fdiv(5.0,a)
if byte[noparse][[/noparse]ptr+i]=="6"
b:=fp.fdiv(6.0,a)
if byte[noparse][[/noparse]ptr+i]=="7"
b:=fp.fdiv(7.0,a)
if byte[noparse][[/noparse]ptr+i]=="8"
b:=fp.fdiv(8.0,a)
if byte[noparse][[/noparse]ptr+i]=="9"
b:=fp.fdiv(9.0,a)
if byte[noparse][[/noparse]ptr+i]<>"." and byte[noparse][[/noparse]ptr+i]<>"-"
a:=fp.fmul(a,10.0)
C:=fp.fadd(c,b)
debug.str(fs.floattostring(a))
debug.str(string(" - "))
debug.str(fs.floattostring(c))
debug.str(string(13))
if k==1
c:=fp.fneg(c)
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
·- Ouch, thats not suppose to be hot!··
Michael King
Application Engineer
R&D
Digital Technology Group
Comments
I have been working on this for the last couple of days, but it is low man on the totem pole as far as priorities. Here is where the code is so far, but it's not complete and hasn't been tested.
CON NaN = $7FFFFFFF Infp = $7F800000 Infn = $FF800000 PUB StringToFloat(Str): float_n | int, frac, fraccnt, infrac, msign, expon, esign, inexpon, i inexpon := false 'expon := mantcnt := exponcnt := 0 repeat i from 0 to strsize(Str) case BYTE[noparse][[/noparse]Str][noparse][[/noparse]i] "e", "E" : inexpon := true 'done processing mantissa, move to exponent "-" : if inexpon 'check if in exponent if expon == 0 'check if this is the first digit of exponent esign := 1 'if yes indicate exponent is negative else return NaN 'text is in a wrong format elseif int == 0 and frac == 0 'if - is in mantissa, make sure it's before any numbers msign := 1 'indicate mantissa is negative else return NaN 'invalid placement of - "+" : 'do nothing "0".."9" : if inexpon 'add to exponent expon *= 10 expon += BYTE[noparse][[/noparse]Str][noparse][[/noparse]i] - "0" elseif infrac 'add to fraction portion of mantissa frac *= 10 frac += BYTE[noparse][[/noparse]Str][noparse][[/noparse]i] - "0" fraccnt++ else 'add to integer portion of mantissa int *= 10 int += BYTE[noparse][[/noparse]Str][noparse][[/noparse]i] - "0" "." : if inexpon return NaN 'decimal points not allowed in exponent else infrac := true 'we're in the fraction OTHER: return NaN 'invalid character in string i := lookupz(fraccnt: 0, 10, 100, 1_000, 10_000, 100_000, 1_000_000, 10_000_000, 100_000_000) 'overflow value for fraction fraccnt := 0 'reset fraction count to count binary positions if i 'if i <> 0 ie a fraction exists repeat while frac 'repeat until frac == 0 fraccnt++ 'increment binary digit count frac <<= 1 int <-= 1 'rotate integer if int & 1 'too many digits int ->= 1 'rotate back quit 'quit from repeat loop if frac > i 'overflow occured int++ 'so set fractional bit to 1 frac -= i 'subtract out overflow value to get remaining fraction 'To be done: calculate binary exponent, range check exponent and mantissa, assemble final
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Paul Baker
Propeller Applications Engineer
Parallax, Inc.
Post Edited (Paul Baker (Parallax)) : 9/11/2008 6:09:15 PM GMT
BYTE[noparse][[/noparse]Str][noparse][[/noparse]i]
I have seen this but I don't know how it works.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
·- Ouch, thats not suppose to be hot!··
Michael King
Application Engineer
R&D
Digital Technology Group
' Here's a simple example of a string to float routine. ' It accepts an optional leading "+" or "-" sign followed ' by a string of digits with an optional decimal point. ' The value of the string of digits (ignoring the decimal ' point) must fit into a 32 bit long. There's no error ' checking for this. The number of significant digits ' after the decimal point must be less than 15 and there ' is no error checking for this either. ' This code fragment assumes that one of the floating ' point objects has been included in this program under ' the name "flt". PRI stringToFloat(str) | sign, char, exp exp := 0 ' Check for an optional sign if (sign := byte[noparse][[/noparse]str] == "-") or byte[noparse][[/noparse]str] == "+" str++ ' Accumulate the value of the digits before the decimal repeat while byte[noparse][[/noparse]str] => "0" and byte[noparse][[/noparse]str] =< "9" result := result * 10 + (byte[noparse][[/noparse]str++] - "0") ' If there's a decimal point, accumulate those digits too if byte[noparse][[/noparse]str++] == "." repeat while byte[noparse][[/noparse]str] => "0" and byte[noparse][[/noparse]str] =< "9" result := result * 10 + (byte[noparse][[/noparse]str++] - "0") exp++ ' Count the # digits after the decimal else str-- ' Here could be the code to check for and handle an exponent ' Now convert the integer to floating point and compensate ' for the number of digits after the decimal point. result := flt.fDiv(flt.fFloat(result),powers[noparse][[/noparse]exp]) if sign ' Include the sign result := flt.fNeg(result) DAT powers long 1.0E00, 1.0E01, 1.0E02, 1.0E03, 1.0E04 long 1.0E05, 1.0E06, 1.0E07, 1.0E08, 1.0E09 long 1.0E10, 1.0E11, 1.0E12, 1.0E13, 1.0E14
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Paul Baker
Propeller Applications Engineer
Parallax, Inc.
BYTE[noparse][[/noparse]Str+i]
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
·- Ouch, thats not suppose to be hot!··
Michael King
Application Engineer
R&D
Digital Technology Group
31.48293
-110.246
It seems to me i should be getting a few more places before it goes dumb
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
·- Ouch, thats not suppose to be hot!··
Michael King
Application Engineer
R&D
Digital Technology Group
The IEEE floating point format allows 23 bits for a mantissa which only provides for slightly less than 7 digits of accuracy.
I don't know how you're getting the numbers you're showing in your message, but you should get a little more accuracy.
Here is my output
String we are converting to a floating point value= -110.246233
Floatstring setprecision=7
Multiplier···· ·· Value· multiplied······· New value from Floatstring
················shown from floatString····
100····················· 1··························100
10······················ ·1························ ·110
0······················· ·0························ ·110
.1······················ ·.2······················· ·110.2
.01······················ .04······················ ·110.24
.001···················· .005···················· ·110.245
.0001················ · .0009················· 110.2459
.00001·············· ·.00007················ 110.246
.000001············ ·.000006··············· 110.246
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
·- Ouch, thats not suppose to be hot!··
Michael King
Application Engineer
R&D
Digital Technology Group
There are all sorts of assumptions and round-off errors inherent in that, particularly when you have just slightly
less than 7 significant decimal digits of accuracy. If you want to check the accuracy, you'll have to hand convert
the floating point value (or write your own conversion program) by keeping it in binary for as long as possible,
applying the binary exponent, then converting first the integer portion to decimal, then the fraction portion.
With a value on the order of 110, you will only get between 4 and 5 decimal places. That's all the floating point
routines are capable of providing. If you need more, you may have to create your own arithmetic package.
Post Edited (Mike Green) : 9/11/2008 7:01:18 PM GMT
PUB StrToFloat(strptr) : flt | int,exp,sign int := exp := sign := 0 repeat strsize(strptr) 'string to integer case byte[noparse][[/noparse]strptr] "-": sign~~ ".": exp := 1 "0".."9": int := int*10 + byte[noparse][[/noparse]strptr] - "0" if exp exp++ 'count dec places other: quit strptr++ if sign int := -int flt := f.FFloat(int) if exp repeat exp-1 flt := f.FDiv(flt,10.0) 'adjust float
You need a floating point object included as f.
See also the thread here: http://forums.parallax.com/showthread.php?p=738380
Andy
I disagree: That's all the float32 package is able to provide. There are other implementations around: the 12 digit BCD package and the double precision package (sqrt comes soon) that I described at the propeller.wikispaces.com/MATH
Conversion routines and spin wrappers are not provided, I use them from assembler
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
--Steve
·
PUB mainLoop | MMmm, dddd, latitudeDDdd, latitudeStrPointer repeat latitudeStrPointer := gps.latitude MMmm := strToFloat(latitudeStrPointer + 2) dddd := floatM.fdiv(MMmm, 60) latitudeDDdd := floatM.fadd(40, dddd)) uarts.str(debug_port, latitudeStrPointer) uarts.tx(debug_port, COMMA) uarts.str(debug_port, floatStr.floatToString(MMmm)) uarts.tx(debug_port, COMMA) uarts.str(debug_port, floatStr.floatToString(dddd)) uarts.tx(debug_port, COMMA) uarts.str(debug_port, floatStr.floatToString(latitudeDDdd)) uarts.tx(debug_port, CR) PUB StrToFloat(strptr) : flt | int,exp,sign int := exp := sign := 0 repeat strsize(strptr) 'string to integer case byte[noparse][[/noparse]strptr] "-": sign~~ ".": exp := 1 "0".."9": int := int*10 + byte[noparse][[/noparse]strptr] - "0" if exp exp++ 'count dec places other: quit strptr++ if sign int := -int flt := floatM.FFloat(int) if exp repeat exp-1 flt := floatM.FDiv(flt,10.0) 'adjust float
is resulting in this from the serial terminal:
* See attached image, looks like I cant cut text from the serial terminal I'm using.
Any ideas where that 10^38 is coming from?
Not remotely an expert, but should those lines be ...
dddd := floatM.fdiv(MMmm, Float(60)) latitudeDDdd := floatM.fadd(Float(40), dddd))
.. or ..
dddd := floatM.fdiv(MMmm, 60.0) latitudeDDdd := floatM.fadd(40.0, dddd)
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Cardinal Fang! Fetch the comfy chair.
That is _really_ strange. All floats are single precision IEEE floats. In theory..
40.0
Float(40)
and floatm.ffloat(40)
.. should all produce absolutely identical 32 bit constants. If they don't then I think we have a problem.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Cardinal Fang! Fetch the comfy chair.