News:

PlayBASIC2DLL V0.99 Revision I Commercial Edition released! - Convert PlayBASIC programs to super fast Machine Code. 

Main Menu

64Bit Floating Point Math Library for PlayBASIC (Dev Blog)

Started by kevin, July 20, 2020, 08:31:54 AM

Previous topic - Next topic

kevin





PlayBASIC Math 64 - 64Bit Scripting LILbrary




Releases


        PlayBASIC Math64  V010 - Public Test   (2021-04-17)
          PlayBASIC Math64  V009 - Public Test  (2021-02-05)





Blog





 Ok.. PlayBASIC coders ..  interested in a 64Bit math library ??   We used to have one, but it's lost..  So lets make another one..



 Update:


     Just putting this together as a sort of secondary unit that can do Float64 math operations within it's own mini VM.     So the idea being is you have a variable table and then write the opcodes into memory with the variable offsets and then call the solver.    Much like the Vector unti experiment some years back.  


    Here's the current test code.  It's running in PB1.65, but it should run in 1.64 editions also..  at least in the short term

    Since this lives outside of the PB parser, we use strings to encode float64 values,  see example bellow.  



PlayBASIC Code: [Select]
      Dim VariableTable(1024)

for lp= 0 to 10
PB64_StringToF64(Readdata$(),PtrToVar(lp))
print PB64_F64ToString(PtrToVar(lp))
next

Sync
waitkey

// Some Sample 64 bit strings
Data " 01234567890123456789"
Data "01.234567890123456789"
Data "012.34567890123456789"
Data "0123.4567890123456789"
Data "01234.567890123456789"
Data "012345.67890123456789"
Data "0123456.7890123456789"
Data "01234567.890123456789"
Data "012345678.90123456789"
Data "0123456789.0123456789"
Data "01234567890.123456789"
Data "012345678901.23456789"
Data "0123456789012.3456789"









stevmjon

since we used to have one, may as well make a new one. might come in handy.
It's easy to start a program, but harder to finish it...

I think that means i am getting old and get side tracked too easy.

kevin


  Well, there is the Extended Float library which is tucked away at the end of the LIBs forum,  And does seem to still be here..    Might just drop it into the slibs folder anyway just in case.


Extended Floating Point Library (login required) (November 30, 2005)


 

kevin

   PB Math64 vs  Extended Math

      I'd actually assumed the extended math was one of the lost libraries,  of which there are a few from the early days of PB (2003->2006/7)   sort of era.    Glad it's still here, but after looking it's implementation is not that quick to execute at runtime,   Given than each operation is performed by a externally DLL call.   Which is fine, but the call over head from the runtime side is way higher than the operation in this case.    

      So a better idea is to build a solver, or a mini runtime that just performs 64 bit math.   Initially I was think of a simple assembly styled interface were we'd have a simple script and it's lex and parse/convert that into the solvers opcodes.   So basically how the vector library worked.   But it might be better to do that n the library side so things are synchronous.    So the lexer and it's runtime are the same version.   What could happen is they'd they misaligned and the parser would generate the wrong opcodes for the run time.  


        Math64 Assembly

        Var A,B,C   ; Declare three 64 bit variables

        Add A ,B,C  ; A=B+C
        Sub A ,B,C  ; A=B-C
        Div  A ,B,C  ; A=B/C
        Mult A ,B,C  ; A=B*C




        You prolly could roll out single operation parser just a easy tho

     You still need to define your variables manually tho, which would be a stack made up of purely 64bit values  These are floats by the way,  the Idea here is to solve those situation were high precision is required.     The more operations you stacked, the better than gain over extended math library.


    For the time being this is novelty (like most things) but If we can get the method sorted then this opens doors to other impossible extensions..  

   




kevin

    PB Math64 - Lexical Scanner -> Parser


      So had a go at this last night and have decided to embed to have a go at a sort of reduce functionality parser.    This means something can the user can declare variables in and perform operations and limited 64 maths  upon, but that's about it.  

      So the syntax is basically PB syntax but there's only a single data type..  64bit floats,  so to declare a variable at this point i've used VAR, but DIM / LOCAL would equally do.   It'll treat each line separately,  so the parser just falls through expecting everything to map to a line.    


Var A,B,C,D
Var Result

A=123
B=456
C=999999

Result=((A+B)/C)




      Picture bellow we pass this to the lexer and display the results on the Pb side.      

PlayBASIC Code: [Select]
function TestParser()

Eol$=chr$(13)+chr$(10)

Code$=""
Code$+=" Var A,B,C,D" +eol$
Code$+=" Var Result" +eol$
Code$+=" A=123" +eol$
Code$+=" B=456" +eol$
Code$+=" C=999999" +eol$
Code$+=" Result=((A+B)/C)" +eol$

#print code$
dim me as P64_LEXED_CODE pointer
me=PB64_LexCode(Code$)


for lp=0 to Me.TokenCount

Dim CurrentToken as P64_LEXER_TOKEN pointer
CurrentToken = Me.TokenArray_P64_LEXER_TOKEN_PTR + (lp * sizeof(P64_LEXER_TOKEN))


Select CurrentToken.Token

case LEXTOKEN_WORD
ThisWord$ =Mid$(Code$,1+CurrentToken.StringOffset,CurrentToken.StringSize)
DrawWord("Token["+str$(lp)+"] = ",ThisWord$)


case LEXTOKEN_ENDOFLINE
DrawWord("Token["+str$(lp)+"] = ","[EOL]",$ff0000)

default
// Assume Single ASC CHARACTER
if CurrentToken.StringSize =1
ThisWord$ = Chr$(CurrentToken.Token)
DrawWord("Token["+str$(lp)+"] = ",ThisWord$,$ffFF00)
endif

EndSelect
next
EndFunction


Function DrawWord(Message$,Word$,Col=$00ff00)
ypos =getcursory()
th =gettextHeight("|")
Text 0,ypos,Message$
Xpos=GettextWidth(Message$)
ink Col
Text xpos,ypos,Word$
ink -1
SetCursorY ypos+th
EndFunction






                 


kevin


   PB Math64 - Lexical Scanner -> Syntax highlight


     Updated the lexer to pick up some missing tidbits.  Display bellow is the result, which gives a sort of syntax high lighted result, without white space. 


     Here's the sample to display the token buffer.

PlayBASIC Code: [Select]
      Eol$      =chr$(13)+chr$(10)
Code$ =""
Code$+=" Var A,B,C,D" +eol$
Code$+=" VaR Result" +eol$
Code$+=" result=cos(a)*1000" +eol$
Code$+=" s=-12345" +eol$
Code$+=" s= - 12345" +eol$
Code$+=" s= -12345" +eol$

Code$+="A" +eol$
Code$+="1" +eol$
Code$+=" A=1" +eol$
Code$+=" A=1." +eol$
Code$+=" A=11.22 " +eol$
Code$+=" A=33.e10" +eol$
Code$+=" A=1234567890.1234567890" +eol$
Code$+=" C=999999" +eol$
Code$+=" Result=((A+B)/C)" +eol$

#print code$



TestSyntaxHighlighted(Code$)





function TestSyntaxHighlighted(Code$)

dim me as P64_LEXED_CODE pointer
me=PB64_LexCode(Code$)


Th=GetTextHeight("|")
Xpos =GetCursorX()
Ypos =GetCursorY()
PadX =4

for lp=0 to Me.TokenCount

Dim CurrentToken as P64_LEXER_TOKEN pointer
CurrentToken = Me.TokenArray_P64_LEXER_TOKEN_PTR + (lp * sizeof(P64_LEXER_TOKEN))


Select CurrentToken.Token

case LEXTOKEN_WORD
ThisWord$ =Mid$(Code$,1+CurrentToken.StringOffset,CurrentToken.StringSize)
Ink $00FF00
Text Xpos,Ypos,ThisWord$
Xpos +=GetTextWidth(ThisWord$)+PadX

case LEXTOKEN_LITERAL

ThisWord$=PB64_F64ToString(int(CurrentToken.LiteralValue))
Ink $ff00ff
Text Xpos,Ypos,ThisWord$
Xpos +=GetTextWidth(ThisWord$)+PadX


case LEXTOKEN_ENDOFLINE

ink $ff0000
Text Xpos,Ypos,"[EOL]"
Ypos=Ypos+TH+10
Xpos = 0

// Pick up keywords ----------------------------------
case 1100 to 2000
// -------------------------------------------------


ThisWord$ =Mid$(Code$,1+CurrentToken.StringOffset,CurrentToken.StringSize)

ink -1
Text Xpos,Ypos,ThisWord$
Xpos +=GetTextWidth(ThisWord$)+PadX


default
// Assume Single ASC CHARACTER
if CurrentToken.StringSize =1
ThisWord$ = Chr$(CurrentToken.Token)
// DrawWord("Token["+str$(lp)+"] = ",ThisWord$,$ffFF00)

ink $ffff00
Text Xpos,Ypos,ThisWord$
Xpos +=GetTextWidth(ThisWord$)+PadX

endif

EndSelect
next

setcursory Ypos
EndFunction





    Next chore is write a parser that spits out the byte code for the solver.    It'll include a few common math operations  and some basic 64bit functions  (no point doing 32bit ones now is there)...







kevin


PB Math64 - Expression Solving  - Getting out of control ;)



     So last night focus moved onto to building a mini compiler of sorts, which is something i'd not expected to be doing this week, but hey there you go..   We're basically at point now where i'm parsing the expressions and generating custom byte code to solve said operations.   The operations back end I wrote last week and has been sitting around gather dust ever since.   The idea of solver is that you can define chunks of basic like code, without having to resort to single operation functions or machine code like user code (as mentioned about),  meaning the code will look mostly like regular PlayBASIC code, making it easier to understand.  Using it, well.... that's another story.


     Let's go through what this is trying to be and what it's NOT going to do.. 



     1)  PB Math64  is built to solve 64BIT operations in FLOATING POINT.      (No integer support is planned)

     2)  PB Math64 will support basic math operations  ( ADD/ SUB/DIV/MULT)

     3)  PB Math64  will include various 64bit math functions (Cos/Sin/Sqrt/Exp etc).   
       
     4)  PB Math64 can not call internal PlayBASIC functions.. 
         


     and that's about it for today...



kevin

   PB Math64 - Compiling

       Had a wishy washy week in terms of free time, but have finally got a mini compiler -> expression -> virtual machine fleshed out.   Taking the opportunity to try and fee different ideas during the process.  If you look at the screen shot in the previous post, you'll noticed that it see some negative literals and not others.    In PB a negative literal is actually handled as a negated positive literal.   Which means there's a little 'cleaning' up of the imported expression before we start solving it.  Which is OK, as that clean process does a few other things too (like recasting stuff), but if this occurs at token level, then we can't look back at the original source, so it's legal to write -1 as -      1    which always felt strange to me.    So this version of the parser is the subtraction symbol is next to literal (constant in other words), the lexer checks back to try and understand if this is a subtraction or a part of the number.  

       Another thing that i've been tinkering with, is using a structures for byte and code generation patterns.   In PB the opcode patterns are a list of offsets from the instruction.  Which is hard coded into the byte code creator and decoder.    What I've been wanting to try tho, is what type of code does C/C++ produce if we use structs for each instruction type.   On first test, this does produce different machine code for the virtual machine, but in some places it seems to pack the registers better, so it's not hitting memory as much per opcode.   For something that single use, that should mean that this would work better as external solver than ot ever could embedded into PB.
 
        The nice about using structures (TYPES in PB speak)  is that IF i change the field orders, then I don't have to change the decoder or creation routines.   In PB the byte patterns are dropped in a serial form.    


        Anyway, last night there's a lexer -> compiler and runtime set up..   Now it's just making sure it's working / then dropping in more operations.


kevin


PlayBASIC LIVE -  Developing Math64 Library (2020-08-06  )

   Today we're take a quick tour at the new Math64 library for PlayBASIC.  The idea behind the library is to build an external solver for 64bit floating point math operations.  The solver will feature a PB styled reduced syntax but with only one data type, 64bit floats.  The library will compile and solve the expressions at runtime.    Those are where we're going tho, the current version barely parses and compiles a simple expression, but we're getting there.




Video:


   





Credits:



      Video By:
   Kevin Picone
   http://PlayBASIC.com 
   https://underwaredesign.com

   Music:
  Spirit of Fire by  Jesse Gallagher





PlayBASIC LIVE PLAYLIST
https://www.youtube.com/playlist?list=PL_dvm0gvzzIVGlAhx34N6z2ce0ffMMOZ8

PlayBASIC BLOG PLAYLIST
https://www.youtube.com/playlist?list=PL_dvm0gvzzIU0Hnr6veV5UvwkSapHCo1J

PlayBASIC on FACEBOOK
http://www.facebook.com/pages/PlayBasic/127905400584274   (Facebook Page)

PlayBASIC on TWITTER
https://twitter.com/PlayBasic



#PlayBASIC #coding #basic #programming #sourcecode #64bit #floatingpoint

kevin


  PlayBASIC Math64  - Expression Evaluation - Brackets with Mults/Divisions  (2020-08-06)

      So expanded the code to handle firstly handle multiplies and divisions and then dropped in support for brackets.   It should be noted that there's unary operators in this, so you can't go  A= -B   rather you'd have to write it as  A=B * -1 and of course as mentioned before there's little to no error detection.   Will drop that in once I know what the limits are.   


      Another change is this version is parsing the entire program.   So the code bellow is lexed and fully compiled as seen in the attched picture.  I've added some lines to show what the input code is and what it produces.  You'll notice some lines produce no code (as as VAR declartions) and those that do have some pre-evaluation going on, but only a TINY amount of it.   So pretty much whatever you write ends up in the output code.



Var A,B,C,D
VaR Result,s
Result=1234.5678 * 122121.54
Result=a+100+(b+c-d)*(10/2) - (A-A)
s=-12345
s=0 - -12345
s= -12345



 


      attached is a display of syntax highlighted input code compared to the compiled byte code..



kevin

  PlayBASIC Math64  - Compiling & Executing 64bit Expressions (2020-08-13)


     Wow,  it's been almost a month, but today i can announce that the library has finally sprung to life.  In that it can parse/compile and execution the expressions on it's own custom runtime.   Almost I've added support for  various function, some limited literal pre-solving of operations and most math functions too.    So code like A= cos(45)  , sees the compiler compute Cos(45) and export only a move into the byte code.  
   

     Functions it currently supports

           ABS()
           CEIL()
           FLOOR()
           COS()
           SIN()
           ACOS()
           ASIN()



    The trig functions are direct wrappers so they expect RADIANS not angles.    I'll add some functions to convert Radian to Degree and vice versa later.    What I might do is have them work with ANGLES by default, then add versions that expect Radians.   Prolly with a R attached or something.   Was going to do this to PB anyway, but just haven't as yet.  


     For usage,  you pass the Compile function a block of code (in string form) to be compiled into a mini program.  The function returns a program pointer, that is then passed to EXECUTE PRG function that will run this logic.    That's all good and all, but there's not easy way to read and write the variables from an executed program.   So that's something we'll have to iron out but trying to make use of the library structure.     

     So the usage structure looks like


     Prg=PB64_Compile(Code$)

     if PRG

            PB64_Execute(PRG)
     endif



        If you have math functions you would like to see implemented in the library, then do so BELLOW


kevin

   PlayBASIC Math64  - Public Test  (2020-08-18)

     Ok, guys here the state of the library today.  It supports basic math operators . Brackets and mostly simple trig functions..   I'm editing a video about it today, so that will cover this a little more.  But for now you can play !


      Note: the protect is being built using PlayBASIC V1.65,  but you should be able to get it running from V1.64P  editions.  (untested)



   Video:

   




  Download
 
     Attached

kevin

Just adding support for comparison operators and doing a little bench marking.   So running the following code takes approximately 380 milliseconds to run it 1,000,000 times.  So that gives us around 100 million mixed operations per second.  

--[ TEST CODE ] -----------------------------------------------

Var A,B,C,D
VaR Result,s
Result = 100<>200
 Result = A<A + A<=B + A<>B
 Result = A>A + A>=B + A><B
D =0 + B
Result=abs(1234.567)
A=floor(cos(90))
B=floor(sin(90))
A=floor(acos(90))
B=floor(asin(90))
A=floor(cosradius(90,100))
B=floor(sinradius(90,100))
A=floor(cosradius(a,a))
B=floor(sinradius(a,a))
A=floor(newcosvalue(100,90,100))
B=floor(newsinvalue(100,90,100))
Result=tan(90)
Result=atan(90)
Result=sqrt(1234567)
Result=sqrt(123456)
Result=a+100+(b+c-d)*(10/2) - (A-A-A)
s=(10/2)






  Today's Edit:



  It seems we're been a bit conservative with our math libraries runtime performance as today's tweaked version runs the same test code (including two more functions) consistently at around 210 milliseconds per 1,000,000 calls.

  There's about 40 instructions being processed per call, so our per second performance is pushing 200 million opcodes per second (40 * 5) =200 * 1 Million

  I doubt it'll stay at that level, but it's nice to see at least.




kevin

  PlayBASIC Math64  V002 - Public Test  (2020-08-24)

       Here's the second release of the Math64 library.  In this one we've a few new addition such as support for comparison and boolean logic operations (AND , OR , XOR)  as wellas some helper functions so you get data from the executed script.   Look at the test for what's available.  


      Performance wise, well it's pretty quick.. Check out the performance estimates bellow..  200 odd millions instructions per second  :)




---[ EXECUTION  ]---------------------------------

  251 Ticks TO Execute [1000000] Times  
  59 Instructions in byte code
 Approx 235.0598 MILLION 64bit Opcodes Per Second


  80 Ticks - Read / Write Variable Result [1000000] Times  



---------[ TEST CODE ] -------------------------------------------------
Var A,B,C,D
VaR Result,s,count
VaR CopyOfResult
Result = 0.0 and 0.0
Result = 1.0 and 0.0
Result = 0.0 and 1.0
Result = 1.0 and 1.0
Result = 0.0 or 0.0
Result = 1.0 or 0.0
Result = 0.0 or 1.0
Result = 1.0 or 1.0
Result = 0.0 xor 0.0
Result = 1.0 xor 0.0
Result = 0.0 xor 1.0
Result = 1.0 xor 1.0
Result = A=A and b=b
Count=Count+1
Result = 100<>200
Result = 100=A
Result = 100<>A
 Result = A=A
 Result = A<A + A<=B + A<>B
 Result = A>A + A>=B + A><B
D =DEgreeToRAdian(90)
D =RAdianToDegree(90)
D =0 + B
Result=abs(1234.567)
A=floor(cos(90))
B=floor(sin(90))
A=floor(acos(90))
B=floor(asin(90))
A=floor(cosradius(90,100))
B=floor(sinradius(90,100))
A=floor(cosradius(a,a))
B=floor(sinradius(a,a))
A=floor(newcosvalue(100,90,100))
B=floor(newsinvalue(100,90,100))
Result=tan(90)
Result=atan(90)
Result=sqrt(1234567)
Result=sqrt(123456)
Result=a+100+(b+c-d)*(10/2) - (A-A-A)
s=(10/2)




      Note: the protect is being built using PlayBASIC V1.65,  but you should be able to get it running from V1.64P  editions.  (untested)





  Video:

 




  Download
 
     Attached

kevin

    PlayBASIC Math64  - Import /Exporting Structures

   It's been a few weeks since the last build of PBMath64 was issued and there's a couple of things that need to be sorted before calling it a library.   One issue is how to move data from the 32Bit side into the library and back again in the most efficient way possible.  While you can read/write variables directly which is fine for small fragments, but not great if there's a cluster of variables that need to be imported constantly, as it'd chew through most of the performance gains.  So i was thinking about setting up some way the user could import from their structure into the script, run the script and have it dump the results back into the structures pointer if needed, as sometimes you wouldn't.


   How would this look ?  I dunno, but I was the simplest approach possible that can be wrapped into a single instruction on the math64 runtime side.  One idea might be to set a starting pointer of where it'll copy the fields form as if it was a data statements.  

   So we'd set the source structure pointer (pointer to the type with the FLOAT fields in it) then in the script have some sort of READ or COPY command in the script.


   If we use the type particle example,  our PB loop might loop like this.
PlayBASIC Code: [Select]
     Type tParticle
Status
x,y
vx,vy
clippingRectX1
clippingRectY1
clippingRectX1
clippingRectY1
endtype

For lp=0 to NumberOfParticles

if Particle(lp)
Me =Particle(lp).tParticle

// Calll our script and pass it the address of this structure to copy out of
CallMathScript PointerMath64Program , int(me)

endif
next




    Then the import script could be




     ImportFromStructure  1 , Status, X,Y,VX,VY,ClipX1,ClipY1,ClipX2,ClipY2

     X=X+VX
     Y=Y+VY
     VY =VY +0.25

     Status = (X>=ClipX1) and (X<ClipX2) and Y>=0 and Y<ClipY2