Author Topic: Errors because some DOUBLE PRECISION numbers are wrong  (Read 5030 times)

0 Members and 1 Guest are viewing this topic.

Offline PROFESPAN@frontier.com

  • Newbie
  • Posts: 1
    • View Profile
Errors because some DOUBLE PRECISION numbers are wrong
« on: April 09, 2022, 10:57:41 am »
Below please see a QB64 program that evokes mathematical errors in some DOUBLE PRECISION numbers. In particular, PRINT 1.D-40 produces 1.D-41, which is ten times smaller than the correct answer 1.D-40.

Here is the test program:

Data 1D-40#,1E-40
Read x#, y!
Print .0000000000000000000000000000000000000001#: Rem:      1D-41 is the result
Print 1# / 10000000000000000000000000000000000000000#: Rem: 1D-41 is the result
Print x#
Print 10000000000000000000000000000000000000000#: Rem:      1D+40 is the result
Print 1# / 10000#: Rem:                                     .0001 is the result
Print .0001#: Rem:                                          .0001 is the result
Print 1D0 / 1.D40: Rem:                                     1D-41 is the result
Print 1D-40: Rem:                                           1D-41 is the result
Print 1D0 / 1D20: Rem:                                      1D-21 is the result
Print 1D-20: Rem:                                           1D-21 is the result
Print 1D0 / 1D19: Rem:                                      1D-19 is the result
Print 1D-19: Rem:                                           1D-19 is the result
Print 0.0000000000000000000000000000000000000001:  Rem:     1D-41 is the result
Print 1 / 10000000000000000000000000000000000000000.: Rem:  1D-41 is the result
Print y!: Rem:                                       9.999946E-41 is the result
Print 10000000000000000000000000000000000000000.: Rem:      1D+40 is the result
Print 1 / 10000:                                       Rem: .0001 is the result
Print .0001: Rem:                                           .0001 is the result
Print 1E0 / 1E40: Rem:                               9.999946E-41 is the result
Print 1E-40: Rem:                                    9.999946E-41 is the result
Print 1E0 / 1E20: Rem:                                      .0001 is the result
Print 1E-20: Rem:                                           .0001 is the result
Print 1E0 / 1E19: Rem:                                      1D-19 is the result
Print 1E-19: Rem:                                           1D-19 is the result
End

Note that 1D-19 prints correctly but 1D-20 does not. SINGLE precision seems to work ok.

It may make a difference if the decimal point is included in some of the numbers, for instance, 1.D-40 instead of 1.D-40. However, the errors may be worse.

Please fix this problem.
Sincerely yours,
Dr. Edwin L. Kerr, Retired NASA physicist and engineer with 60 years of computer programming experience.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Errors because some DOUBLE PRECISION numbers are wrong
« Reply #1 on: April 09, 2022, 12:47:09 pm »
Welcome to forum Edwin,

Double only has 8 bytes precision about 15 decimals give or take and according to others Print has a mind of it's own.
https://wiki.qb64.org/wiki/Variable_Types

jack might have something for you and those that need precision math. I can give you some slow String math routines with arbitrary precision for add, subtr and mult, 100 decimal precision for division oh I have Sqr now too if I recall.

Original author of QB64 left and those that maintain QB64 complain of code mess. QB64 really a hobbiest language for Retro gamers.
« Last Edit: April 09, 2022, 12:52:59 pm by bplus »

Offline jack

  • Seasoned Forum Regular
  • Posts: 408
    • View Profile
Re: Errors because some DOUBLE PRECISION numbers are wrong
« Reply #2 on: April 09, 2022, 01:09:02 pm »
hello Dr. Edwin and bplus
as you noted the QB64 print functions are buggy in some cases, until they get fixed you could use the crt's printf function
let me modify an example, then I'll post
not the best example, but you can modify the code to your needs
Code: QB64: [Select]
  1.     Function snprintf& (Dest As String, Byval l As Long, frmt As String, Byval x As Double)
  2.  
  3. Screen _NewImage(768, 768, 32)
  4.  
  5. Locate 3, 1:
  6.  
  7. Color &HFF00FFFF~&&
  8. Locate , 1: Print "QB64 Print";
  9. Locate , 25: Print "printf";
  10.  
  11. Color &HFFFF8000~&&
  12. Print "c# = ... :": Color &HFFFFFFFF~&&
  13. c# = 10#: Locate , 1: Print c#;: Locate , 25: Print strf(c#)
  14. c# = 100#: Locate , 1: Print c#;: Locate , 25: Print strf(c#)
  15. c# = 1000#: Locate , 1: Print c#;: Locate , 25: Print strf(c#)
  16. c# = 10000#: Locate , 1: Print c#;: Locate , 25: Print strf(c#)
  17. c# = 100000#: Locate , 1: Print c#;: Locate , 25: Print strf(c#)
  18. c# = 1000000#: Locate , 1: Print c#;: Locate , 25: Print strf(c#)
  19. c# = 10000000#: Locate , 1: Print c#;: Locate , 25: Print strf(c#)
  20. c# = 100000000#: Locate , 1: Print c#;: Locate , 25: Print strf(c#)
  21. c# = 1000000000#: Locate , 1: Print c#;: Locate , 25: Print strf(c#)
  22. c# = 10000000000#: Locate , 1: Print c#;: Locate , 25: Print strf(c#)
  23. c# = 100000000000#: Locate , 1: Print c#;: Locate , 25: Print strf(c#)
  24. c# = 1000000000000#: Locate , 1: Print c#;: Locate , 25: Print strf(c#)
  25. c# = 10000000000000#: Locate , 1: Print c#;: Locate , 25: Print strf(c#)
  26. c# = 100000000000000#: Locate , 1: Print c#;: Locate , 25: Print strf(c#)
  27. c# = 1000000000000000#: Locate , 1: Print c#;: Locate , 25: Print strf(c#)
  28. c# = 10000000000000000#: Locate , 1: Print c#;: Locate , 25: Print strf(c#)
  29. c# = 100000000000000000#: Locate , 1: Print c#;: Locate , 25: Print strf(c#)
  30. c# = 1000000000000000000#: Locate , 1: Print c#;: Locate , 25: Print strf(c#)
  31. c# = 10000000000000000000#: Locate , 1: Print c#;: Locate , 25: Print strf(c#)
  32. c# = 100000000000000000000#: Locate , 1: Print c#;: Locate , 25: Print strf(c#)
  33. c# = 1000000000000000000000#: Locate , 1: Print c#;: Locate , 25: Print strf(c#)
  34. c# = 10000000000000000000000#: Locate , 1: Print c#;: Locate , 25: Print strf(c#)
  35. c# = 100000000000000000000000#: Locate , 1: Print c#;: Locate , 25: Print strf(c#)
  36. c# = 1000000000000000000000000#: Locate , 1: Print c#;: Locate , 25: Print strf(c#)
  37. c# = 10000000000000000000000000#: Locate , 1: Print c#;: Locate , 25: Print strf(c#)
  38. c# = 100000000000000000000000000#: Locate , 1: Print c#;: Locate , 25: Print strf(c#)
  39. c# = 1000000000000000000000000000#: Locate , 1: Print c#;: Locate , 25: Print strf(c#)
  40. c# = 10000000000000000000000000000#: Locate , 1: Print c#;: Locate , 25: Print strf(c#)
  41. c# = 100000000000000000000000000000#: Locate , 1: Print c#;: Locate , 25: Print strf(c#)
  42. c# = 1000000000000000000000000000000#: Locate , 1: Print c#;: Locate , 25: Print strf(c#)
  43. c# = 10000000000000000000000000000000#: Locate , 1: Print c#;: Locate , 25: Print strf(c#)
  44. c# = 100000000000000000000000000000000#: Locate , 1: Print c#;: Locate , 25: Print strf(c#)
  45. c# = 1000000000000000000000000000000000#: Locate , 1: Print c#;: Locate , 25: Print strf(c#)
  46. c# = 10000000000000000000000000000000000#: Locate , 1: Print c#;: Locate , 25: Print strf(c#)
  47.  
  48. Function strf$ (x As Double)
  49.     Dim As String s
  50.     Dim As String frmt
  51.     Dim As Long l, ex, sign
  52.     sign = Sgn(x)
  53.     If sign < 0 Then x = -x
  54.     s = Spc(64)
  55.     frmt = "%.15g" + Chr$(0)
  56.     l = snprintf(s, Len(s), frmt, x)
  57.     s = _Trim$(s)
  58.     If InStr(s, ".") > 0 And Left$(s, 1) = "0" Then s = Mid$(s, 2)
  59.     If sign < 0 Then s = "-" + s Else s = " " + s
  60.     ex = InStr(s, "e")
  61.     If ex > 0 Then Mid$(s, ex, 1) = "D"
  62.     strf = s
  63.  
« Last Edit: April 09, 2022, 01:26:06 pm by jack »

Offline OldMoses

  • Seasoned Forum Regular
  • Posts: 469
    • View Profile
Re: Errors because some DOUBLE PRECISION numbers are wrong
« Reply #3 on: April 09, 2022, 01:16:46 pm »
Welcome Dr. Kerr,

Having coded a space flight simulator for a table top role playing game, that bears NO resemblance to a real guidance system, I can attest to the fact that precision has its limits in QB64. Particularly with respect to print formatting. As Bplus says, I've occasionally had to resort to formatting functions and other such expedients to get where I want to go.

That said, QB64 is a modern upgrade of QBasic/QB4.5 and adds gobs of functionality, previously undreamed of for us QBasic users. I just wouldn't get on a space ship that used it...yet.

Offline jack

  • Seasoned Forum Regular
  • Posts: 408
    • View Profile
Re: Errors because some DOUBLE PRECISION numbers are wrong
« Reply #4 on: April 09, 2022, 02:42:34 pm »
another example
Code: QB64: [Select]
  1.     Function snprintf& (Dest As String, Byval l As Long, frmt As String, Byval x As Double)
  2.  
  3. Screen _NewImage(768, 768, 32)
  4.  
  5. Locate 3, 1:
  6.  
  7. Color &HFF00FFFF~&&
  8. Locate , 1: Print " i";
  9. Locate , 10: Print "QB64 Print";
  10. Locate , 22: Print "printf";
  11. Color &HFFFF8000~&&
  12. Color &HFFFFFFFF~&&
  13. For i = 16 To 50
  14.     c = Val("." + String$(i, 48) + "1")
  15.     Locate , 1: Print i;: Locate , 10: Print c;: Locate , 20: Print strf(c)
  16.  
  17. Function strf$ (x As Double)
  18.     Dim As String s
  19.     Dim As String frmt
  20.     Dim As Long l, ex, sign
  21.     sign = Sgn(x)
  22.     If sign < 0 Then x = -x
  23.     s = Spc(64)
  24.     frmt = "%.15g" + Chr$(0)
  25.     l = snprintf(s, Len(s), frmt, x)
  26.     s = _Trim$(s)
  27.     If InStr(s, ".") > 0 And Left$(s, 1) = "0" Then s = Mid$(s, 2)
  28.     If sign < 0 Then s = "-" + s Else s = " " + s
  29.     ex = InStr(s, "e")
  30.     If ex > 0 Then Mid$(s, ex, 1) = "D"
  31.     strf = s
  32.  
the qbs_str function in libqb.cpp which is called by the print statement is a bit complex, at least for the non-C-expert, it could be simplified a lot if you want give up certain formatting rules imposed by trying to match the output of QB45
« Last Edit: April 09, 2022, 02:58:38 pm by jack »

Offline Cobalt

  • QB64 Developer
  • Forum Resident
  • Posts: 878
  • At 60 I become highly radioactive!
    • View Profile
Re: Errors because some DOUBLE PRECISION numbers are wrong
« Reply #5 on: April 09, 2022, 04:39:34 pm »
Please fix this problem.

Its open source on Github, have at it.

Though if I were planning on landing a rover on Pluto I would probably go with an assembly language that matches the processors used in its onboard computer(s). Then you would have much more control over how you want floating point numbers handled.  But if you just want to play around with simulating such events and learning how to overcome, sidestep or work around certain limitations then Welcome To BASIC.
Granted after becoming radioactive I only have a half-life!

Offline Dimster

  • Forum Resident
  • Posts: 500
    • View Profile
Re: Errors because some DOUBLE PRECISION numbers are wrong
« Reply #6 on: April 09, 2022, 06:17:12 pm »
Quote
Dr. Kerr designed a laser instrument for measuring the height of volcanoes and the depth of valleys and rifts on Mars.  The finished instrument, the Mars Global Surveyor, is still orbiting Mars.  It was the first successful use of a laser beyond the orbit of the Moon.  Thanks to the instrument our contour maps of Mars are better than the ones we have of the Earth.  Another instrument like the first mapped the Asteroid Eros and reposes on it.  A third instrument is aboard the MESSENGER mission to Mercury.  The Japan Aerospace Exploration Agency put a similar instrument on the Hayabusa space probe, which recently returned rock samples from the Asteroid Itokawa.

N. A. S. A. awarded Dr. Kerr a prize for his design of a telescope (shown at right) adapted to receive laser communications from space probes going to Jupiter, Uranus, and Neptune, into the Kuiper Belt as far as 35 times the distance to Pluto, or beyond.  The telescope will be one of the largest in the world.


That's pretty cool.

Offline jack

  • Seasoned Forum Regular
  • Posts: 408
    • View Profile
Re: Errors because some DOUBLE PRECISION numbers are wrong
« Reply #7 on: April 09, 2022, 06:26:06 pm »
Cobalt, the problem is that for some values the printed result is off by a factor of 10, it's not just a minor flaw
to fix the qbs *qbs_str(double value) is complicated by the fact that QB64 want to match the output of qb45 and the code is rather complex due to that
I mentioned this before that _Floats are converted to double then passed to qbs_str(double value)
Code: [Select]
qbs *qbs_str(long double value){
    //not fully implemented
    return qbs_str((double)value);
}
I propose a simple alternative
Code: [Select]
qbs *qbs_str(long double value){
    //not fully implemented
    static qbs *tqbs;
    static int32 l, i;
    static char c;
    tqbs=qbs_new(32,1);
    l=__mingw_sprintf((char*)&qbs_str_buffer,"% .18Lg",value);
for (i=0;i<l;i++) {
c=qbs_str_buffer[i];
if(c==101) c=70;
tqbs->chr[i]=c;}
tqbs->chr[l]=0;
    return tqbs;
}

example
Code: QB64: [Select]
  1.  
  2. d = 1D17
  3. f = 1F17
  4.  
  5. For i = 17 To 40
  6.     d = d * 10#
  7.     f = f * 10##
  8.     Print "Double "; d
  9.     Print "_Float "; f
  10.     Print "------------"
  11.  

output
Code: [Select]
Double  1D+18
_Float  1F+18
------------
Double  1D+19
_Float  1F+19
------------
Double  1D+20
_Float  1F+20
------------
Double  1D+21
_Float  1F+21
------------
Double  1D+22
_Float  1F+22
------------
Double  1D+22
_Float  1F+23
------------
Double  1D+24
_Float  1F+24
------------
Double  1D+24
_Float  1F+25
------------
Double  1D+25
_Float  1F+26
------------
Double  1D+26
_Float  1F+27
------------
Double  1D+28
_Float  1F+28
------------
Double  1D+28
_Float  1F+29
------------
Double  1D+29
_Float  1F+30
------------
Double  1D+30
_Float  1F+31
------------
Double  1D+31
_Float  1F+32
------------
Double  1D+32
_Float  1F+33
------------
Double  1D+33
_Float  1F+34
------------
Double  1D+35
_Float  1F+35
------------
Double  1D+35
_Float  1F+36
------------
Double  9.999999999999998D+36
_Float  1F+37
------------
Double  9.999999999999998D+37
_Float  1F+38
------------
Double  9.999999999999998D+38
_Float  1F+39
------------
Double  9.999999999999998D+39
_Float  1F+40
------------
Double  9.999999999999998D+40
_Float  1F+41
------------
« Last Edit: April 09, 2022, 08:16:35 pm by jack »