StrToFloat()
cgracey
Posts: 14,206
I made a StrToFloat method using the built-in Spin2 floating-point operators.
It accepts numbers in all formats and converts them to single-precision IEEE-754 floats:
123
123.456
0.00000000000000000000000012345
1.0
1.0e9
1.0e+9
1.0e-9
1e12
9e+45
1_000_000_000
So, no decimal point is needed. "E" is allowed for power-of-ten exponents. Underscores are allowed for grouping in the mantissa, but not in the exponent. It also checks for errors and ABORTS with messages.
' ' Get floating-point value - source_ptr must point to first digit ' PRI get_float() : fp | n, dflag, nflag, exp repeat 'MAC mantissa digits fp := fp *. 10.0 +. fp_dig[source_chr() & $0F] repeat while source_test("_") 'skip any "_" chrs if dflag 'hold magnitude after decimal point n++ if source_test(".") 'check for decimal point, one only if dflag~~ abort(@"Extra decimal point") while source_digit_next() 'another digit? if source_test_uppercase("E") 'check for exponent if source_test("-") 'if "-", set negative flag nflag~~ else 'else, skip any "+" source_test("+") if not source_digit_next() 'make sure exponent digit abort(@"Expected exponent digit") repeat 'MAC exponent digits exp := exp * 10 + source_chr() & $0F while source_digit_next() exp <#= 99 'limit exponent, still under/overflows n -= (nflag ? -exp : exp) repeat while n 'adjust magnitude exp := n #> -12 <# 12 fp := fp /. fp_exp[exp] n -= exp if NaN(fp) 'check overflow abort(@"Floating-point overflow") DAT fpstr byte "1.175494e+9",0 'test string fp_dig long 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0 long 1e-12, 1e-11, 1e-10, 1e-09 long 1e-08, 1e-07, 1e-06, 1e-05, 1e-04, 1e-03, 1e-02, 1e-01 fp_exp long 1e+00, 1e+01, 1e+02, 1e+03, 1e+04, 1e+05, 1e+06, 1e+07 long 1e+08, 1e+09, 1e+10, 1e+11, 1e+12
Comments
Added to OBEX : https://obex.parallax.com/obex/strtofloat/
Once you finish the intense video driver work Chip, we'll get you setup with an OBEX account and have these objects moved to your name.
@cgracey : Consider making a source pointer argument so that the method can be used with any string.
Sounds good.
I'm not sure I appreciated that Spin2 supports floating point much more than Spin1 until just now...
String to float could be very useful.
Have to figure out how to use floats in Spin2 first...
I see this in the docs:
x *. y
Does this mean multiplying signed integer with float? Is this possible to do?
Can I multiply two floats?
Can I convert integer to float?
Some things to figure out...
Because Spin2 is type-agnostic at runtime, you must use special floating-point operators to do floating-point operations. And be sure to do them on LONGs that can hold 32 bits:
So, just use these operators and you're doing floating-point.
Here is a new version that works on a string you point it to. It is self-contained and doesn't call any other methods.
FLOAT(integer) : float
ROUND(float) : integer
TRUNC(float) : integer
The floating-point operators work on floats, only. Use FLOAT/ROUND/TRUNC to go between integers and floats.
@cgracey
It appears your routine won't work with negative floating values (i.e. "-1.02"), it complains that it is expecting a digit as the first string character. It works OK with positive and negative exponents, but not with negative values.
Thanks for noticing this. I just added that...
UPDATED
@cgracey
Thanks Chip, it works for negative values now.
StrToFloat OBEX updated with the negative fix : https://obex.parallax.com/obex/strtofloat/
Thanks, Michael.
Could you please put the latest file I posted in place of the older one in there? I posted a new file 3 posts up which handles regular strings. It needs to be renamed from StrToFloat2.spin2 to StrToFloat.spin2. That is the one people should be using. Thanks a lot.
Since the string could come from outside the P2, you might consider stripping all leading whitespace characters:
Is there an inverse function? FloatToStr()?
BTW: Seeing how the floating point ops work now. At first, looked like the "." was operating on y and not the operator.
When I first looked at it, was not seeing the spaces before and after the operator. A lot clearer now.
x *. y
Also, I see the Float() and Round() methods that make using all this viable..
I made a small variation of Chip's object that uses a second return value for the error code instead of using the abort mechanism. For testing, there is a method in the object that converts the error code to the strings Chip used. The test program demonstrates this -- though I don't know how to make the object return the NaN error.