Board Logo
« saving number as string WITH PRECISION »

Welcome Guest. Please Login or Register.
Nov 23rd, 2009, 03:25am



Conforums Terms of Service | Membership Rules | Home | Help | Search | Recent Posts | Notification | Format Your Message | Vista/Win7 FAQ

Please use the forums Search feature before asking. For best results select only the categories, which describe your need best.
Please post code using the code box described in Format Your Messages.
This will keep indentation, separate it better form the message and prevent gibberish.
If the code is to long for one post or additional files are needed, upload a ZIP archive to the Just BASIC Files Archive Site.
To register at the Just BASIC Files Archive Site, please follow the instructions given in File Archives - Important Registration Information.

Pages: 1  Notify Send Topic Print
 thread  Author  Topic: saving number as string WITH PRECISION  (Read 86 times)
tsh73
Senior Member
ImageImageImageImageImage

member is offline

Avatar




PM

Gender: Male
Posts: 932
xx saving number as string WITH PRECISION
« Thread started on: Oct 29th, 2009, 09:50am »

The problem:
sometimes we have to convert number to string.
Will it be just string containing many different values to be used as object
Or just number to be saved to the file in order to be read later.
So, JB supplies two nice and easy functions:
val(x$) and str$(x).
Now, where is the problem?
Let's see.
Code:
x = 1.2345
x$ = str$(x)
x1=val(x$)
print x, x$, x-x1
 

results:
Code:
1.2345        1.2345        0.0
 

Everything looks OK.
Now, let's take something more interesting.
(as you should remember, 1/3 in decimal is infinite periodic fraction, that is 0.3333333333333 ... ad infinum)
Code:
x = 1/3
x$ = str$(x)
x1=val(x$)
print x, x$, x-x1
 

results:
Code:
0.33333333    0.33333333    0.33333333e-8
 

If we get rid of conversion and just put that long fraction themselves
Code:
x = 1/3
x1=0.3333333333333333333
print x, x-x1
 

we'll get:
Code:
0.33333333    -0.55511151e-16
 


That is, then converting to string and back, we lost precision from 16 significant digits to just 8 (from double precision to single, pardon my Klatchian QBasic).

What function is responcible, val() or str$()?
Obviously str$() is guilty - it show us only 8 '3's, not 16.
Check if val() works ok:
Code:
x = 1/3
x$ = "0.3333333333333333333"
x1=val(x$)
print x, x$, x-x1
 

results:
Code:
0.33333333    0.3333333333333333333       -0.55511151e-16
 


Indeed, given long string, val() returns full supported precision.

So, in order to save and restore number to / from string, we need replacement for str$().

Take 1
Wait! We already have a function that returns more decimal. It is using. Let's try that:
Code:
x = 1/3
x$ = using("#.###############",x)
x1=val(x$)
print x, x$, x-x1
 

results:
Code:
0.33333333    0.333333333333333           0.33306691e-15
 


It seems that with 15 '#' we got all right digits ('3'), if adding more '#' we just getting garbage (unrelated digits).
(well, I do not know how to explain this but with 18 '#' or more x-x1 prints as 0. Probably we should use 18 '#' then?)

But we used format string exactly tailored to our number.
What happens if we multiply it to 1000000?
Code:
x = 1/3*1000000
x$ = using("#.###############",x)   '15 #
x1=val(x$)
print x, x$, x1, x-x1, (x-x1)/x
 

Now I add to output to see what we get as converted value (x1) and how difference relates to x (relative error. It should be around 1e-15.)
results:
Code:
333333.333    %333333.333333333311488     0             333333.333    1.0
 

It breaks - because number did not fit to format (too big), JB added '%' in front, and that made val() return 0. Total failure, data lost.
Well, can't it be helped? We can just chage
#.###############
to
#######.###############
Result will became
Code:
333333.333     333333.333333333311488     333333.333    0.0           0.0
 

- that is, converted value exactly equal source one.
(but pay attention to last digits of x$ printed: obviously garbage).
This again was modification for this particular case. As you should remember, JB handles real numbers up to somewhere 10^307. So to make it work for 1/3*10^100, we need to add 100 '#'? Indeed. But will it work?
Code:
fmt$ = ""
for i = 1 to 308
    fmt$ = fmt$ +"#"
next
x = 1/3*10^100
x$ = using(fmt$+".###############",x)   '15 #
x1=val(x$)
print x, trim$(x$), x1, x-x1, (x-x1)/x
 

Indeed it does work. Results are:
Code:
3.33333333e99               3333333333333333020656266301054156018810300136799977219037663452567373199587417942557784766487504288.335158099050496          3.33333333e99               0.0           0.0
 

Of that very long number only first 16 digits valid, rest is garbage. And indeed if we will save it to file, it will be there taking space. (just not nice).

Take 2
Actually we can shorten our program a bit.
Instead of busily building 308 '#' we can allow error happen then strip that %.
I'll show it for 1/3*1000000, just because that 10^100 string is too long ;) (and I'll use 18 '#' after decimal point).
Code:
x = 1/3*1000000
x$ = using("#.##################",x)    '18 #
if left$(x$, 1) = "%" then x$ = mid$(x$,2)  'strip first %
x1=val(x$)
print x, trim$(x$), x1, x-x1, (x-x1)/x
 

Results are
Code:
333333.333    333333.333333333260632064 333333.333    0.58207661e-10              0.17462298e-15
 

- realtive error ~1e-15, so we consider it working. And all the garbage still here (it's just not as much as for 10e100).
So, can we live with it? Probably NO:
if we try to put really small number, like 1/3*1e-20, using will return bunch of 0's.
Code:
x = 1/3*1e-20
x$ = using("#.##################",x)    '18 #
if left$(x$, 1) = "%" then x$ = mid$(x$,2)  'strip first %
x1=val(x$)
print x, trim$(x$), x1, x-x1, (x-x1)/x
 

Result:
Code:
0.33333333e-20              0.000000000000000000        0             0.33333333e-20              1.0
 

Total failure, data lost.

Take 3
So, are we doomed? ;)
Not quite. There is a function here
Scientific format in printing
what will save us:
Code:
x = 1/3*10^100
x$ = usingS$(x,15)
x1=val(x$)
print x, trim$(x$), x1, x-x1, (x-x1)/x

x = 1/3*1e-20
x$ = usingS$(x,15)
x1=val(x$)
print x, trim$(x$), x1, x-x1, (x-x1)/x


function usingS$(n,prec)
  if n = 0 then usingS$="0e+0":exit function
  fmt$ = "#"+left$(".",prec>0)+left$("#################",prec)   'fmt of mantissa
  s$=left$("-",n<0)
  n=abs(n)
  log10=log(n)/log(10)
  e=int(log10)-(log10<0)    'QB like INT. Makes mantissa for negative exponents start from digit (not 0 as JB do)
  p=10^e
  if left$(using(fmt$,n/p),1)="%" then p=p*10:e = e+1
  usingS$=s$+using(fmt$,n/p) +"e"+left$("+",e>0) +str$(e)   'Excel always shows "+" for exponent
End function
 

Results:
Code:
3.33333333e99               3.333333333333333e+99       3.33333333e99               0.0           0.0
0.33333333e-20              3.333333333333333e-21       0.33333333e-20              0.0           0.0
 

Additional bonus - no garbage digits in output.

Hope it will be of use to someone ;)
« Last Edit: Oct 29th, 2009, 09:55am by tsh73 » User IP Logged

Q: "And if I took your codes and compile them, and sell them for a profit"?
A: Go ahead. I had my share of good then I coded it for fun, if you can make better use of it - please do.
Pages: 1  Notify Send Topic Print
« Previous Topic | Next Topic »

Conforums Terms of Service | Membership Rules | Home | Help | Search | Recent Posts | Notification | Format Your Message | Vista/Win7 FAQ


New Monthly Ad-Free Plan!

$6.99 Gets 50,000 Ad-Free Pageviews!
| Hookah | Free Shoutboxes |

This Board Hosted For FREE By Conforums ©
Get Your Own Free Message Board!