Float-to-string and back
I found I wanted to convert a floating-point value to a string, and back, without any loss of precision, if possible. This is what I came up with.
OPTION STRICT
DEF NTOS$(N#)
VAR S$=STR$(N#)
VAR S0$="+",S1$="+",E%=0,NN#,N1%,N2%
IF S$[0]=="-" THEN
S0$="-"
S$=RIGHT$(S$,LEN(S$)-1)
N#=-N#
ENDIF
IF N#==0 THEN
RETURN S0$+"0"
ELSEIF INSTR("in",S$[0])>=0 THEN
RETURN S0$+S$[0]
ENDIF
IF N#<1
S1$="-"
NN#=N#*256
WHILE NN#<2
INC E%,8
N#=NN#
NN#=N#*256
WEND
NN#=N#*2
WHILE NN#<2
INC E%
N#=NN#
NN#=N#*2
WEND
DEC N#
N#=N#*POW(2,28)
N1%=FLOOR(N#)
DEC N#,N1%
N2%=N#*POW(2,24)
RETURN S0$+S1$+FORMAT$("%03X%07X%06X",E%,N1%,N2%)
ELSE
NN#=N#/256
WHILE NN#>=1
INC E%,8
N#=NN#
NN#=N#/256
WEND
NN#=N#/2
WHILE NN#>=1
INC E%
N#=NN#
NN#=N#/2
WEND
DEC N#
N#=N#*POW(2,28)
N1%=FLOOR(N#)
DEC N#,N1%
N2%=N#*POW(2,24)
RETURN S0$+S1$+FORMAT$("%03X%07X%06X",E%,N1%,N2%)
ENDIF
END
DEF NYBBLE#(N$)
RETURN ASC(N$)-48-7*(ASC(N$)>=58)
END
DEF STON#(S$)
VAR S0$=S$[0],RESULT#=0,I%,E%
S$=RIGHT$(S$,LEN(S$)-1)
IF S%=="0" GOTO @ZERO
ON INSTR("in",S$[0]) GOTO @INF,@NAN
@NUM
RESULT#=1
FOR I%=4 TO 16
RESULT#=RESULT#*16+NYBBLE#(S$[I%])
NEXT I%
E%=NYBBLE#(S$[1])*256+NYBBLE#(S$[2])*16+NYBBLE#(S$[3])
IF S$[0]=="-" THEN E%=-E%
RESULT#=RESULT#/POW(2,52)
RESULT#=RESULT#*POW(2,E%)
GOTO @END
@INF
RESULT#=1E200*1E200
GOTO @END
@NAN
RESULT#=1E200*1E200
DEC RESULT#,RESULT#
GOTO @END
@ZERO
@END
IF S0$=="-" THEN RESULT#=-RESULT#
RETURN RESULT#
END
Some edge cases I have tested with:
VAR INF#=1E200*1E200 ' infinity VAR INF_N#=-INF# ' negative infinity VAR NAN#=INF#-INF# ' not-a-number VAR NAN_N#=-NAN# ' negative not-a-number VAR E#=POW(2,-1022) ' smallest positive number that can be represented VAR O#=1+POW(2,-52) ' smallest number greater than 1 that can be represented VAR E1#=E#*O# ' smallest number greater than E# that can be represented VAR T#=2-POW(2,-52) ' greatest number less than 2 that can be represented VAR M2#=POW(2,1023) ' greatest power of 2 that can be represented VAR M#=M2#*T# ' greatest number that can be represented VAR Z#=0 ' zero VAR Z_N#=-Z# ' negative zeroIt's not high-quality code. I may come back and make a neater version. If anyone discovers a value that turns out different after being passed through NTOS$ then STON#, please let me know. NAN does not 'equal' itself, for any other floating point value V# my hope is that STON#(NTOS$(V#))==V# - let me know if you find a counterexample.
Well, there's a lot of code in SmileBasic, specifically to hide details of the internal representation of floats. Makes sense that it would take a lot of code to undo that.
- But, in fairness, yes, it is longer than it could be. Length of code was a lower priority for me than other characteristics the code should have.
That's an impressive amount of code, yes. Though, I was of the impression that a double only had about 17 fractional digits, and that something like FORMAT$("%.17F",NUM#) was enough to represent it properly in a string. Of course that won't handle inf and nan, but CLASSIFY can catch that for you. If you could explain why this isn't enough I'd like to know, doubles have a lot going on.
With E# as above, FORMAT$("%.17F",E#) gives 0.00000000000000000, the same result as if you give it 0, but E#==0 is false.
With E# as above, FORMAT$("%.17F",E#) gives 0.00000000000000000, the same result as if you give it 0, but E#==0 is false.Oh, I see, yeah. That's an issue. Would E# be a denormal number? I could certainly see that as being a potential issue with the formatter.
No, E# is not a denormal number... I tried generating a denormal number with (E#*T#)-E#, but it turns out the result of this calculation is ==0. E# has the same string representation as 0 because it is less than 0.000000000000000005. Perhaps FORMAT$("%.324F",E#) would work, but that's a long string. EDIT: FORMAT$("%.324F",VALUE#) does work, but VAL does not reverse the transformation - for E#, the result is 0.With E# as above, FORMAT$("%.17F",E#) gives 0.00000000000000000, the same result as if you give it 0, but E#==0 is false.Oh, I see, yeah. That's an issue. Would E# be a denormal number? I could certainly see that as being a potential issue with the formatter.
