|
Post by Walt Decker on Jun 2, 2022 15:20:04 GMT -5
' '################################################################ ' RANDOM ACCESS FILE USING A STRUCT DEFINITION ' DEMO BY: WALT DECKER, THURSDAY, 2 JUNE 2022 '################################################################ ' 'The advantage of this system is that one does not have to calculate 'the size of each field in the record. Field sizes are intrinsic in 'the definition of the structure since each field requires a pre-defined 'data type. Unfortunately LB does not have a byte data type, a float data type, 'a wide string data type, or a wide string nul-terminated string data type. ' 'Other difficulties are the inability of creating structure arrays, and 'passing structures to SUBs/FUNCTIONS.
'If you want to play with this using string fields you can use the following 'syntax: 'STRUCT tProto, _ ' Lname$ AS CHAR[36], _ '<--- NUL-TERMINATED STRINGS ' Fname$ AS CHAR[36], _ ' Mi$ AS CHAR[2], _ ' Age AS SHORT ' '################################################################
'<----------------- FILE OPEN PARAMETERS --------------> OPEN.ALWAYS = 4 GENERIC.READ = HEXDEC("&H80000000") GENERIC.WRITE = HEXDEC("&H40000000") FILE.SHARE.READ = HEXDEC("&H00000001") FILE.SHARE.WRITE = HEXDEC("&H00000002") FILE.ATTRIBUTE.NORMAL = HEXDEC("&H00000080") FILE.FLAG.RANDOM.ACCESS = HEXDEC("&H10000000")
RW.PARAMS = GENERIC.READ OR GENERIC.WRITE SH.PARAMS = FILE.SHARE.READ OR FILE.SHARE.WRITE FL.ATTR = FILE.ATTRIBUTE.NORMAL OR FILE.FLAG.RANDOM.ACCESS
STRUCT tDip, _ '<--- RECORD DEFINITION North AS LONG, _ '<--- RECORD FIELDS East AS LONG, _ DipAz AS SHORT, _ Dip AS SHORT
StructSize = LEN(tDip.struct) '<--- OVERALL SIZE OF RECORD IN BYTES Fname$ = DefaultDir$ + "\RndFileTst.RND" '<--- TEST FILE TO WRITE/READ
OPEN "kernel32.dll" FOR DLL AS #KERN '<--- API LIBRART
'<--- CREATE OR OPEN THE FILE ------------------------------------> CALLDLL #KERN, "CreateFileA", Fname$ AS PTR, RW.PARAMS AS ULONG, _ SH.PARAMS AS ULONG, 0 AS ULONG, OPEN.ALWAYS AS ULONG, _ FL.ATTR AS ULONG, 0 AS ULONG, FlHndl AS ULONG PRINT "FILE HANDLE "; FlHndl BytesWritten = 0 '<--- BYTES WRITTEN FOR I = 1 TO 10 '<--- WRITE THE FILE SEQUENTIALLY tDip.North.struct = INT(100000 - RND(0) * 100000) tDip.East.struct = INT(80000 - RND(0) * 80000) tDip.DipAz.struct = INT(RND(0) * 361) tDip.Dip.struct = INT(RND(0) * 91)
CALLDLL #KERN, "WriteFile", FlHndl AS ULONG, tDip AS STRUCT, StructSize AS LONG, _ WrittenCount AS LONG, 0 AS ULONG, RetVal AS BOOLEAN BytesWritten = BytesWritten + StructSize
NEXT I
'<-------- WRITE BUFFERS TO FILE ---------------------> CALLDLL #KERN, "FlushFileBuffers", FlHndl AS ULONG, RetVal AS VOID '<----------- SET END OF FILE MARKER ---------------> CALLDLL #KERN, "SetEndOfFile", FlHndl AS ULONG, RetVal AS VOID '<-------------- CALCULATE # OF RECORDS WRITTEN --------------> CALLDLL #KERN, "GetFileSize", FlHndl AS ULONG, BytesWreitten AS LONG NumRecs = BytesWritten / StructSize PRINT "FILE SIZE = "; BytesWritten; " NumRecs = "; NumRecs CALLDLL #KERN, "CloseHandle", FlHndl AS ULONG, RetVal AS VOID '<--- RELEASE FILE
'<------------------- OPEN FILE TO READ RANDOMLY -----------------> CALLDLL #KERN, "CreateFileA", Fname$ AS PTR, RW.PARAMS AS ULONG, _ SH.PARAMS AS ULONG, 0 AS ULONG, OPEN.ALWAYS AS ULONG, _ FL.ATTR AS ULONG, 0 AS ULONG, FlHndl AS ULONG PRINT "FILE HANDLE "; FlHndl '<---------- CALCULATE # OF RECORDS IN FILE -------------------> CALLDLL #KERN, "GetFileSize", FlHndl AS ULONG, BytesWreitten AS LONG NumRecs = BytesWritten / StructSize PRINT "FILE SIZE = "; BytesWritten; " NumRecs = "; NumRecs
RecNum = INT(RND(0) * 10) '<--- RECORD TO RETRIEVE PRINT "RETRIEVE RECORD "; RecNum
BytePos = RecNum * StructSize '<--- FIND THE RECORD IN THE FILE RetVal = FN.SetFilePos(FlHndl, BytePos) '<--- SET FILE POINTER TO BEGINING OF RECORD PRINT "FILE POINTER IS AT BYTE "; RetVal; " "; BytePos tDip.North.struct = 0 tDip.East.struct = 0 tDip.DipAz.struct = 0 tDip.Dip.struct = 0
'<---------------- READ THE RECORD -----------------------> CALLDLL #KERN, "ReadFile", FlHndl AS ULONG, tDip AS STRUCT, StructSize AS LONG, _ BytesRead AS LONG, 0 AS LONG, RetVal AS BOOLEAN
CALLDLL #KERN, "CloseHandle", FlHndl AS ULONG, RetVal AS VOID '<--- RELEASE FILE
PRINT "RECORD FIELD VALUES = " PRINT tDip.North.struct PRINT tDip.East.struct PRINT tDip.DipAz.struct PRINT tDip.Dip.struct CLOSE #KERN END
'------------------------------------------------------------------ '------------------------------------------------------------------
FUNCTION FN.SetFilePos(FlHndl, BytePos)
FILE.BEGIN = 0
NewPos = 0 CALLDLL #KERN, "SetFilePointer", FlHndl AS ULONG, BytePos AS LONG, 0 AS LONG, _ FILE.BEGIN AS LONG, NewPos AS LONG
FN.SetFilePos = NewPos END FUNCTION
'
|
|
|
Post by Walt Decker on Jun 3, 2022 14:22:28 GMT -5
' '################################################################ ' RANDOM ACCESS FILE USING A STRUCT DEFINITION ' DEMO BY: WALT DECKER, FRIDAY, 3 JUNE 2022 '################################################################ ' ' This demo is essentially the same as the previous one except that ' it uses NUMBERMANDLL to translate single precision float values ' to 4-byte strings and adds fields for the data collector's name. ' If you open the resultant file in NOTEPAD it will look like ' giberish. ' ' Bear in mind that fields can be written and retrieved individulally. ' '################################################################
STRUCT tDip, _ '<--- RECORD DEFINITION North$ AS CHAR[5], _'<--- RECORD FIELDS; CHAR[5] INDICATES CSTR East$ AS CHAR[5], _'BY CONVENTION CSTRS ARE NUL-TERMINATED SO USEABLE DipAz AS SHORT, _'DATA SPACE IS ONE LESS THAN DECLARED. Dip AS SHORT, _ Lname$ AS CHAR[16], _ Fname$ AS CHAR[16], _ Mi$ AS CHAR[2], _ When$ AS CHAR[15]
StructSize = LEN(tDip.struct) '<--- OVERALL SIZE OF RECORD IN BYTES Fname$ = DefaultDir$ + "\RndFileTst.RND" '<--- TEST FILE TO WRITE/READ
OPEN "kernel32.dll" FOR DLL AS #KERN '<--- API LIBRARY OPEN "NUMBERMANDLL" FOR DLL AS #NUM '<--- NUMBER CONVERSION LIBRARY
'<--- CREATE OR OPEN THE FILE ------------------------------------> FlHndl = FN.OpenRandomFile(Fname$) BytesWritten = 0 '<--- BYTES WRITTEN FOR I = 1 TO 10 '<--- WRITE THE FILE SEQUENTIALLY tDip.North$.struct = FN.SetSingle$(100000 - RND(0) * 100000) tDip.East$.struct = FN.SetSingle$(80000 - RND(0) * 80000) tDip.DipAz.struct = INT(RND(0) * 361) tDip.Dip.struct = INT(RND(0) * 91) tDip.Lname$.struct = "Decker" tDip.Fname$.struct = "Walt" tDip.Mi$.struct = "H" tDip.When$.struct = Date$() RetVal = FN.PutRecord(FlHndl, StructSize) '<--- WRITE THE RECORD BytesWritten = BytesWritten + StructSize
NEXT I
BytesWritten = FN.CloseFile(FlHndl) '<--- RELEASE THE FILE NumRecs = BytesWritten / StructSize PRINT "FILE SIZE = "; BytesWritten; " NumRecs = "; NumRecs
'<------------------- OPEN FILE TO READ RANDOMLY -----------------> tDip.North$.struct = "" '<--- ZERO THE STRUCTURE tDip.East$.struct = "" tDip.DipAz.struct = 0 tDip.Dip.struct = 0 tDip.Lname$.struct = "" tDip.Fname$.struct = "" tDip.When$.struct = ""
FlHndl = FN.OpenRandomFile(Fname$) PRINT "FILE HANDLE "; FlHndl TRACE 2 '<---------- CALCULATE # OF RECORDS IN FILE -------------------> BytesWritten = FN.LOF(FlHndl) '<--- FILE SIZE IN BYTES NumRecs = INT(BytesWritten / StructSize) PRINT "FILE SIZE = "; BytesWritten; " NumRecs = "; NumRecs
RecNum = INT(RND(0) * NumRecs) '<--- RECORD TO RETRIEVE PRINT "RETRIEVE RECORD "; RecNum
RetVal = FN.GetRecord(FlHndl, RecNum, StructSize) '<--- READ THE RECORD BytesWritten = FN.CloseFile(FlHndl) '<--- RELEASE THE FILE
PRINT "RECORD FIELD VALUES = " PRINT FN.GetSingle$(tDip.North$.struct) '<--- GET FLOAT VALUES PRINT FN.GetSingle$(tDip.East$.struct) PRINT tDip.DipAz.struct PRINT tDip.Dip.struct PRINT tDip.Lname$.struct PRINT tDip.Fname$.struct PRINT tDip.When$.struct CLOSE #KERN CLOSE #NUM END
'------------------------------------------------------------------ '------------------------------------------------------------------
FUNCTION FN.SetFilePos(FlHndl, BytePos) ' SETS FILE POINTER TO BEGINNING OF RECORD
FILE.BEGIN = 0
NewPos = 0 CALLDLL #KERN, "SetFilePointer", FlHndl AS ULONG, BytePos AS LONG, 0 AS LONG, _ FILE.BEGIN AS LONG, NewPos AS LONG
FN.SetFilePos = NewPos END FUNCTION
'------------------------------------------------------------------- '-------------------------------------------------------------------
FUNCTION FN.OpenRandomFile(FlPath$)
'<----------------- FILE OPEN PARAMETERS --------------> OPEN.ALWAYS = 4 GENERIC.READ = HEXDEC("&H80000000") GENERIC.WRITE = HEXDEC("&H40000000") FILE.SHARE.READ = HEXDEC("&H00000001") FILE.SHARE.WRITE = HEXDEC("&H00000002") FILE.ATTRIBUTE.NORMAL = HEXDEC("&H00000080") FILE.FLAG.RANDOM.ACCESS = HEXDEC("&H10000000")
RW.PARAMS = GENERIC.READ OR GENERIC.WRITE SH.PARAMS = FILE.SHARE.READ OR FILE.SHARE.WRITE FL.ATTR = FILE.ATTRIBUTE.NORMAL OR FILE.FLAG.RANDOM.ACCESS
FlHndl = 0 '<--- CREATE OR OPEN THE FILE ------------------------------------> CALLDLL #KERN, "CreateFileA", FlPath$ AS PTR, RW.PARAMS AS ULONG, _ SH.PARAMS AS ULONG, 0 AS ULONG, OPEN.ALWAYS AS ULONG, _ FL.ATTR AS ULONG, 0 AS ULONG, FlHndl AS ULONG
FN.OpenRandomFile = FlHndl END FUNCTION
'------------------------------------------------------------------ '------------------------------------------------------------------
FUNCTION FN.PutRecord(FlHndl, RecSize) ' WRITES RECORD TO THE FILE
STRUCT tRite, NumBytes AS LONG
CALLDLL #KERN, "WriteFile", FlHndl AS ULONG, tDip AS STRUCT, RecSize AS LONG, _ tRite AS STRUCT, 0 AS ULONG, RetVal AS BOOLEAN
FN.PutRecord = RecSize + tRite.NumBytes.struct END FUNCTION
'------------------------------------------------------------------ '------------------------------------------------------------------
FUNCTION FN.SetSingle$(Value) ' TRANSLATE FLOAT VALUE TO 4-BYTE STRING
SngOut$ = "" SngIn$ = ""
RetVal = 0
SngOut$ = STR$(Value) SngIn$ = SPACE$(5)
CALLDLL #NUM, "FN_SetSingleStr", SngOut$ AS PTR, SngIn$ AS STRUCT, _ RetVal AS LONG
FN.SetSingle$ = LEFT$(SngIn$, RetVal) END FUNCTION
'--------------------------------------------------------------------- '---------------------------------------------------------------------
FUNCTION FN.GetSingle$(Bytes4$) ' TRANSLATE 4-BYTE FLOAT STRING TO MULTI-BYTE FLOAT STRING
ValIn$ = "" NumChrs = 0
ValIn$ = SPACE$(16) CALLDLL #NUM, "FN_GetSingleStr", Bytes4$ AS PTR, ValIn$ AS STRUCT, _ NumChrs AS LONG FN.GetSingle$ = LEFT$(ValIn$, NumChrs) END FUNCTION
'------------------------------------------------------------------- '-------------------------------------------------------------------
FUNCTION FN.CloseFile(BYREF FlHndl) ' RELEASE THE FILE; RETURN FILE SIZE IN BYTES
BytesWritten = 0 '<-------- WRITE BUFFERS TO FILE ---------------------> CALLDLL #KERN, "FlushFileBuffers", FlHndl AS ULONG, RetVal AS VOID '<----------- SET END OF FILE MARKER ---------------> CALLDLL #KERN, "SetEndOfFile", FlHndl AS ULONG, RetVal AS VOID '<-------------- GET FILE SIZE IN BYTES --------------> BytesWritten = FN.LOF(FlHndl) FlHndl = 0
FN.CloseFile = BytesWritten END FUNCTION
'------------------------------------------------------------------ '------------------------------------------------------------------
FUNCTION FN.LOF(FlHndl) ' RETRIEVE FILE SIZE IN BYTES
FileSize = 0 CALLDLL #KERN, "GetFileSize", FlHndl AS ULONG, FileSize AS LONG
FN.LOF = FileSize END FUNCTION
'----------------------------------------------------------------- '-----------------------------------------------------------------
FUNCTION FN.GetRecord(FlHndl, RecNum, RecSize) ' READ A RECORD FROM THE FILE
BytePos = 0 BytesRead = 0 RetVal = 0
BytePos = RecNum * RecSize '<--- FIND THE RECORD IN THE FILE RetVal = FN.SetFilePos(FlHndl, BytePos) '<--- SET FILE POINTER TO BEGINING 'OF RECORD
CALLDLL #KERN, "ReadFile", FlHndl AS ULONG, tDip AS STRUCT, RecSize AS LONG, _ BytesRead AS LONG, 0 AS LONG, RetVal AS BOOLEAN
FN.GetRecord = BytesRead END FUNCTION [div]'
[/div]
The attached zip contains NUMBERMANDLL and the above source code.
|
|
|
Post by pierre on Oct 3, 2022 13:35:59 GMT -5
Recently I played a little bit with the second demo of the 'RANDOM ACCESS FILE USING A STRUCT DEFINITION'. I put the 'read record' part in a loop, in order to show not one, but all the created records.
Processing the code many times in a row, I encountered randomly the following runtime error:
'Float denormalized operand'
at the line:
tDip.North$.struct = FN.SetSingle$(100000 - RND(0) * 100000)
I found that it was not the function call, but the random number generation itself that caused the problem. That is strange because the random expression (100000 - RND(0) * 100000) seems correct.
Did somebody experience this type of error in other circumstances ?
pierre
|
|
|
Post by Walt Decker on Oct 3, 2022 17:41:03 GMT -5
Pierre, where the data is being written you might try this and see if the error persists.
FOR I = 1 TO 10 '<--- WRITE THE FILE SEQUENTIALLY RndNum = 100000 - RND(0) * 100000 tDip.North$.struct = FN.SetSingle$(RndNum) '100000 - RND(0) * 100000) RndNum = 80000 - RND(0) * 80000 tDip.East$.struct = FN.SetSingle$(RndNum) '80000 - RND(0) * 80000)
|
|
|
Post by pierre on Oct 4, 2022 10:38:46 GMT -5
Thanks Walt, I tried something similar to determine where exactly the error occured. However, it does not solve the problem.
The execution fails always at the first line of the write loop: 'RndNum = 100000 - RND(0) * 100000' when the program is relaunched many times in a row. This happens from time to time, rarely for the original version, more often when I read and display all the records, not just one.
Actually, I have replaced the random creation of the record to be read by a 'for - next' loop:
'-------------------- for RecNum = 0 to 9 ..../.... Next RecNum '--------------------
and moved the line 'BytesWritten = FN.CloseFile(FlHndl) '<--- RELEASE THE FILE' out of the loop, just below 'Next RecNum'.
Could it be something that remains in memory after multiple function calls ? By the way, I don't know anything about 'denormalized float values'...
pierre
|
|
|
Post by Walt Decker on Oct 4, 2022 11:01:32 GMT -5
I will look into it. I doubt if there is anything left in memory after multiple function calls provided the call is properly executed and the file is properly closed.
As for the "denormalized float" error, I suspicion that is a catch-all error for an error of undetermined nature. I too do not know what a denormalized float is.
|
|
|
Post by Walt Decker on Oct 4, 2022 14:34:58 GMT -5
Well, Pierre, we are both correct. The denormalized float error is meaningless. Although nothing is left in memory there was a memory over-flow from reading the file. This over-flow came because I apparently had a half-Hiemer's episode and neglected to release the file handle in function FN.CloseFile().
In FUNCTION FN.CloseFile() add the following immediately after BytesWritten = FN.LOF(FlHndl):
'<------------------ CLOSE THE FILE --------------------> CALLDLL #KERN, "CloseHandle", FlHndl AS ULONG, RetVal AS LONG
That will take care of the problem.
Since you are reading all the records back make sure that your FOR loop look like this:
FOR I = 0 TO NumRecs - 1 RetVal = FN.GetRecord(FlHndl, I, StructSize) '<--- READ THE RECORD
FN.GetRecord() calculates the start of the record based on the record number (I) and the size of the record (StructSize).
|
|
|
Post by pierre on Oct 5, 2022 7:52:18 GMT -5
Walt, following your indications, I added the required line in the 'CloseFile' function and adapted the for-next loop in the 'read records' part of the main program.
Unfortunately, it did not solve the problem.
I read some articles on Wikipedia about floating point arithmetic. 'Denormalized' numbers, apparently, fill the 'underflow gap' (?) around zero, according to the IEEE-754 standard. For my poor old brain this is all like chinese, but my understanding is that the runtime error might point to a value that is too small to be represented.
Remember that the error only occurs when I run the program over and over again, something that one normally would not do. It happens after a certain amount of runs, ie between 1 and 20. The original version with only one record displayed at the same time, is much more stable.
Could it be possible that the translations via 'numberman.dll' leave some traces in memory ?
pierre
|
|
|
Post by Walt Decker on Oct 5, 2022 11:35:57 GMT -5
I am working on it. It appears that something is randomly grabeling the communication between LB and the DLL thus the DLL is receiving the wrong value and returning an engineering value smaller than E-40. I may have to send a double and cast it as a single in the DLL.
|
|
|
Post by Walt Decker on Oct 6, 2022 15:39:29 GMT -5
I have run 1/2 million iterations of NUMBERMANDLL functions "FN_SetSingleStr"() and "FN_GetSingleStr"() with values ranging from +8.43 * 10^-37 to -3.40 * 10^38 and they have not failed once. Numbers larger or smaller are out of single precision range and return zero. This leads me to think that the problem is in Liberty Basic and might stem from its non-standard way of representing engineering notation values. It could be something else or a combination of factors.
I cannot pin it down because once it fails, and I try to run in debug mode, the error appears before tokenization is complete and LB crashes.
The best bet, at this point, is to forgo the use of NUMBERMANDLL and set the stringz elements of tDip to something like char[16] and fill them with STR$(Value).
|
|
|
Post by pierre on Oct 6, 2022 16:49:46 GMT -5
In debug mode, the execution stops at the first random generation line: 'RndNum = 100000 - RND(0) * 100000' and does not go any further. At least, it is good to know that the dll is not to blame. I will try your suggestion.
In any case, many thanks for all your efforts.
PS I take the opportunity to join the content of the error.log file. Perhaps Carl might help us out.
pierre
'-----------------------------------------------------------------------
Error log timestamp Thursday 06/10/22 22:56:01
Runtime error: Float denormalized operand
Error Error(Exception)>>activateHandler: <anUndefinedObject> Error(Exception)>>handle Error(Exception)>>signal Error class(Exception class)>>signal: <'Float denormalized o...'> BasicProgram(Object)>>error: <'Float denormalized o...'> BasicProgram>>terminateRun: <anArithmeticError> [] in BasicProgram>>errorHandlerBlock ExceptionHandler>>evaluateResponseBlock: <aBlockClosure> for: <anArithmeticError> [] in ExceptionHandler>>handle: ProtectedFrameMarker(BlockClosure)>>setUnwind: <aBlockClosure> BlockClosure>>invisibleEnsure: <aBlockClosure> ExceptionHandler>>handle: <anArithmeticError> ExceptionHandler>>findHandler: <anArithmeticError> ArithmeticError(Exception)>>activateHandler: <anExceptionHandler> ArithmeticError(Exception)>>handle ArithmeticError(Exception)>>signal ArithmeticError class(Exception class)>>signal: <'Float denormalized o...'> Float>>floatError Float>>* Basic class>>random [] in BasicRnd class>>using:program: [] in BasicExpression>>makeBlockOf: BasicDoubleParameterContextHolder>>value [] in BasicExpression>>makeBlockOf: BasicDoubleParameterContextHolder>>value BasicVariableAssigner>>value [] in BasicProgram>>begin ExceptionHandler>>evaluateProtectedBlock: <aBlockClosure> [] in ExceptionHandler>>activateDuring: ProtectedFrameMarker(BlockClosure)>>setUnwind: <aBlockClosure> BlockClosure>>invisibleEnsure: <aBlockClosure> ExceptionHandler>>activateDuring: <aBlockClosure> ExceptionHandler class>>handle: <anError class> with: <aBlockClosure> during: <aBlockClosure> BlockClosure>>on: <anError class> do: <aBlockClosure> BasicProgram>>begin BasicProgram>>run [] in BasicSourcePane>>run BasicCompiler class>>compileFromString: <' ''#################...'> notify: <aBasicSourcePane> ifSuccess: <aBlockClosure> BasicSourcePane>>run BasicSourceWindow>>run BasicEditorModel>>run: <aToolbarButton> BasicEditorModel(Object)>>perform: <#run:> with: <aToolbarButton> ToolbarButton>>triggerClickedEvent Message>>perform NotificationManager>>runPendingEvents NotificationManager>>runEventLoop Message>>perform Message>>evaluate Process>>safelyEvaluate: <aMessage> '-------------------------------------------------------------------------------------
|
|
|
Post by Walt Decker on Oct 6, 2022 18:17:27 GMT -5
Thank you for taking the time, Pierre. It is strange that particular statement would produce an error. 100000 is well in the range of a single precision number.
In my testing with LB, it never produced an error unless I performed a translation from the string representation back to a number.
|
|
|
Post by pierre on Oct 7, 2022 7:32:57 GMT -5
It is crazy, but I think I have found it !
It is tortuous, but it works. It is actually the same trick as the one I found for the Personal Vital Signs, which was inspired by Brandon's observations about double precision and structs.
I should have tried this before ! My apologies, it would have spared us time and some headache...
'---------------------------------------------------------------
In the function FN.GetSingle$, replace the line :
FN.GetSingle$ = LEFT$(ValIn$, NumChrs)
with the following :
FN.GetSingle$ = USING("#####.####", VAL(LEFT$(ValIn$, NumChrs)))
'-----------------------------------------------------------------
Before making the change, I compared the input values generated by the RND formula with the output values of the program after processing through the NUMBERMANDLL.dll. LB always works with double precision, but shows by default a maximum of 9 digits. The GetSingle$ function returns 7 digits, with the last digit correctly rounded. It seems that LB is internally working with the 7 digits of this result ... plus or minus 'something' ....and perhaps that 'something' remains and gets accumulated in memory, causing a runtime error after a certain amount of runs.
Forcing the result by a form of 'USING' is apparently the solution.
The 'USING' I propose is set for a range of 5 digits to the left (0 to 99,999) and 4 to the right of the decimal point. The results are equal to the previously obtained values.
I launched and relaunched the program a hundred of times in a row. It now runs without any error.
QED
pierre
|
|
|
Post by Walt Decker on Oct 7, 2022 10:33:46 GMT -5
Good catch, Pierre. I did not think of that.
I will run it through my LB test routine so it does not have to be relaunched multiple times.
As a side note, I am not sure that LB uses doubles internally. SmallTalk is an object oriented language. As such it probably uses VARIANTs, which are 16 byte values that describe the data type along with its value. There may be a glitch in LB because of that.
|
|
|
Post by pierre on Oct 7, 2022 16:22:34 GMT -5
About double precision, this is what the LB Help file says:
"In Liberty BASIC, numeric variables hold either a double-precision floating point value, or an integer."
|
|