Announcement

Collapse
No announcement yet.

Converting Numbers to Strings

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • #21
    Mike,
    this appears to work.
    The main code does the job on a full sized LONG in about 40clks. On a 5 digit LONG it's about half that. You can save a bit more if you don't need signs by cutting out that part.
    The problem is that if you want to call it as a PB function with that function returning a dynamic string as the answer then the call/return process is taking 8 times longer than the conversion.
    If you really want speed you need to send the answer to the function as one of the parameters so there's only 1 string being created instead of 3.
    Ideally, you'd use ASCIIZ stings as the're faster.
    You could also try inlining the code and you'll then get the best possible speed.

    Paul.
    Code:
    FUNCTION FastLong2Str( BYVAL LongVar AS LONG ) AS STRING
    STATIC a AS ASCIIZ*20
    STATIC CalledBefore AS LONG
    IF NOT CalledBefore THEN
        a$="00000000000000"
        CalledBefore=-1
    END IF
     
    !mov eax,LongVar      'get number
    !lea ecx,a$           'get pointer to answer string
     
    !jmp over
     
    chartab:
    !dd "00","10","20","30","40","50","60","70","80","90"
    !dd "01","11","21","31","41","51","61","71","81","91"
    !dd "02","12","22","32","42","52","62","72","82","92"
    !dd "03","13","23","33","43","53","63","73","83","93"
    !dd "04","14","24","34","44","54","64","74","84","94"
    !dd "05","15","25","35","45","55","65","75","85","95"
    !dd "06","16","26","36","46","56","66","76","86","96"
    !dd "07","17","27","37","47","57","67","77","87","97"
    !dd "08","18","28","38","48","58","68","78","88","98"
    !dd "09","19","29","39","49","59","69","79","89","99"
     
    
    over:
    'on entry eax=number to convert, ecx=pointer to answer buffer (minimum 12 bytes)
    'on exit, eax,ecx,edx are undefined, all other registers are preserved.
    'answer is in location pointed to by ecx on entry
     
    sdword:
    'do a signed DWORD to ASCII
    !or eax,eax                 'test sign
    !jns udword                 'if +ve, continue as for unsigned
    !neg eax                    'else, make number positive
    !mov byte ptr [ecx],"-"     'include the - sign
    !inc ecx                    'update the pointer
     
     
    udword:
    'unsigned DWORD to ASCII
    !push edi                   'save registers that need to be saved
    !push esi
      
    !mov esi,ecx                'get pointer to answer
    !mov edi,eax                'save a copy of the number
     
    !mov edx, &hD1B71759        '=2^45\10000    13 bit extra shift
    !mul edx                    'gives 6 high digits in edx
     
    !mov eax,&h68DB9            '=2^32\10000+1
     
    !shr edx,13                 'correct for multiplier offset used to give better accuracy
    !jz short skiphighdigits    'if zero then don't need to process the top 6 digits
     
     
    !mov ecx,edx                'get a copy of high digits
    !imul ecx,10000             'scale up high digits
    !sub edi,ecx                'subtract high digits from original. EDI now = lower 4 digits
     
     
    !mul edx                    'get first 2 digits in edx
    !mov ecx,100                'load ready for later
     
    !jnc short next1            'if zero, supress them by ignoring
    !cmp edx,9                  '1 digit or 2?
    !ja  short ZeroSupressed    '2 digits, just continue with pairs of digits to the end
     
    !mov edx,chartab[edx*4]     'look up 2 digits
    !mov [esi],dh               'but only write the 1 we need, supress the leading zero
    !inc esi                    'update pointer by 1
    !jmp short ZS1              'continue with pairs of digits to the end
     
    next1:
    !mul ecx                    'get next 2 digits
    !jnc short next2            'if zero, supress them by ignoring
    !cmp edx,9                  '1 digit or 2?
    !ja  short ZS1a             '2 digits, just continue with pairs of digits to the end
     
    !mov edx,chartab[edx*4]     'look up 2 digits
    !mov [esi],dh               'but only write the 1 we need, supress the leading zero
    !inc esi                    'update pointer by 1
    !jmp short ZS2              'continue with pairs of digits to the end
     
    next2:
    !mul ecx                    'get next 2 digits
    !jnc short next3            'if zero, supress them by ignoring
    !cmp edx,9                  '1 digit or 2?
    !ja  short ZS2a             '2 digits, just continue with pairs of digits to the end
     
    !mov edx,chartab[edx*4]     'look up 2 digits
    !mov [esi],dh               'but only write the 1 we need, supress the leading zero
    !inc esi                    'update pointer by 1
    !jmp short ZS3              'continue with pairs of digits to the end
     
    next3:
     
    skiphighdigits:
    !mov eax,edi                'get lower 4 digits
     
    !mov ecx,100
     
    !mov edx,&h28F5C29          '2^32\100 +1
    !mul edx
    !jnc short next4            'if zero, supress them by ignoring
    !cmp edx,9                  '1 digit or 2?
    !ja  short ZS3a             '2 digits, just continue with pairs of digits to the end
     
    !mov edx,chartab[edx*4]     'look up 2 digits
    !mov [esi],dh               'but only write the 1 we need, supress the leading zero
    !inc esi                    'update pointer by 1
    !jmp short  ZS4             'continue with pairs of digits to the end
     
    next4:
    !mul ecx                    'this is the last pair so don't supress a single zero
    !cmp edx,9                  '1 digit or 2?
    !ja  short ZS4a             '2 digits, just continue with pairs of digits to the end
     
    !mov edx,chartab[edx*4]     'look up 2 digits
    !mov [esi],dh               'but only write the 1 we need, supress the leading zero
    !mov byte ptr [esi+1],0     'zero terminate string
     
    !jmp short  xit               'all done
     
     
    ZeroSupressed:
    !mov edx,chartab[edx*4]     'look up 2 digits
    !mov [esi],dx
    !add esi,2                  'write them to answer
      
    ZS1:
    !mul ecx                    'get next 2 digits
    ZS1a:
    !mov edx,chartab[edx*4]     'look up 2 digits
    !mov [esi],dx               'write them to answer
    !add esi,2
     
    ZS2:
    !mul ecx                    'get next 2 digits
    ZS2a:
    !mov edx,chartab[edx*4]     'look up 2 digits
    !mov [esi],dx               'write them to answer
    !add esi,2
     
    ZS3:
    !mov eax,edi                'get lower 4 digits
    !mov edx,&h28F5C29          '2^32\100 +1
    !mul edx                    'edx= top pair
    ZS3a:
    !mov edx,chartab[edx*4]     'look up 2 digits
    !mov [esi],dx               'write to answer
    !add esi,2                  'update pointer
     
    ZS4:
    !mul ecx                    'get final 2 digits
    ZS4a:
    !mov edx,chartab[edx*4]     'look them up
    !mov [esi],dx               'write to answer
     
    !mov byte ptr [esi+2],0     'zero terminate string
     
     
    xit:
    !pop esi                    'restore used registers
    !pop edi
    sdwordend:
     
    
    FUNCTION=a$
    '!ret
    END FUNCTION
    ------------------


    [This message has been edited by Paul Dixon (edited July 31, 2007).]

    Comment


    • #22
      Mike,
      <<I am learning a ton of usefull techniques and ways to approach my coding.>>

      Then you won't mind a few comments on your latest code.
      Code:
          IF LongVar < 0 THEN 
            LongVar = ABS(LongVar)
            NegFlag = 1
          END IF
      could probably be better written as
      Code:
          IF LongVar < 0 THEN 
            LongVar = -LongVar
            NegFlag = 1
          END IF
      as it saves a function call.
      Your Do..LOOP is using lots of divides, 2 each loop as MOD is an integer divide.
      It's usually faster to work the other way and divide once, then multiply back up by 10 each loop. Something like this:
      Code:
      num##=LongVar/100000 +1e-8   'your number is only 6 digits so shift it 5 places leaving only one ahead of the point. add the small offset to guarantee there are no rounding errors
      FirstDigit=int(num##)
      num##=(num##-FirstDigit)*10  'remove the first digit and shift the next digit into place
      SecondDigit=int(num##)
      num##=(num##-SecondDigit)*10  'remove the Second digit and shift the next digit into place
      
      etc for all 6 digits. This way, instead of 12 slow divides you have 1 divide and 5 multplies.
      What's this "MoveMemory" about? You're still allocating strings which is the slow part so I can't see how this will save much time over just assinging your answewr as
      Code:
      FUNCTION=zDigits
      after the digits are shuffled into place within zDigits.

      Paul.

      ------------------

      Comment


      • #23
        >The main code does the job on a full sized LONG in about 40clks
        WHAT!!!!!
        simply stunning...

        I have learned so much here.


        Added:
        Testing reveals a problem with the LONG - 2147483646

        FastLONG2STR failed at:-2147483646 >>>-2147483646<>-2147483646 00<<<

        Code:
            FOR i = -2147483646 TO 2147483646
                sLongNum = FastLONG2STR(i)  ' 360 
                IF FORMAT$(i) <> sLongNum THEN 
                  PRINT #hDbg,  "FastLONG2STR failed at:" + STR$(i) + "   >>>" + FORMAT$(i) + "<>" + sLongNum + "<<<"
                  EXIT FOR 
                END IF 
            NEXT


        [This message has been edited by Mike Trader (edited July 30, 2007).]

        Comment


        • #24
          Just last week I was looking for an application, but I just could not find one which converted a string of decimal characters to an integer fast enough.


          Michael Mattias
          Tal Systems (retired)
          Port Washington WI USA
          [email protected]
          http://www.talsystems.com

          Comment


          • #25
            MCM,
            then what a fortunate coincidence that we've just been discussing such conversion in this thread. You can now proceed with your application. .. unless you need a faster version still?


            Paul.

            ------------------

            Comment


            • #26
              Michael,
              This is an exercise in enginuity. I thought we all gravitate to
              PB because it is so flexible, fast and reliable. Look at what we
              have done here. Its amazing. If you are content with slow bloat
              then go to VB or .net. I dont think those guys bat an eye at the
              speed of anything.

              What you dont know, is that I have a dozen or more custom functions
              that require similar very specific conversions. They have all
              benefited from this.

              I have one process that uses many of these thousands of times.
              The progress bar moves along and we are done in a few seconds.
              I suspect when I am done, I wont need a progress bar. What that
              means is I can put this process inline with the main process and
              loose a secondary loop all together. Thats really big.

              For LONG variables I think this is important as they are the
              defacto for speed. I guess I could just use QUADs or GUIDs or
              some other slow bloated windows structure for Date and Time,
              but why when these tools are within reach.

              Apart from that its just fun
              40 Clocks for this conversion is an amazing feat.
              Thats bragging rights.


              ------------------
              Kind Regards
              Mike

              Comment


              • #27
                Mike T

                Your first post talked about converting time values.

                I've used the following. Testing on my system shows it
                running about 3000 clocks.

                I'm sure it could be made faster ...

                -- Eric


                Code:
                #COMPILE EXE "SECs2.exe" '
                #DIM ALL
                
                TYPE tTimeString
                    tsHH AS STRING * 2
                    tsD1 AS STRING * 1
                    tsMM AS STRING * 2
                    tsD2 AS STRING * 1
                    tsSS AS STRING * 2
                END TYPE
                
                UNION uTimeString
                    ts AS tTimeString
                    tsData AS STRING * 8
                END UNION
                
                '¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤'
                SUB time_stamp_count(tick AS QUAD) ' CPU Clock count    Charles E V Pegge
                
                  '---------------------------'
                  '                           ' approx because it is not a serialised instruction
                  '                           ' it may execute before or after other instructions
                  '                           ' in the pipeline.
                  ! mov ebx,tick              ' var address where count is to be stored.
                  ! db  &h0f,&h31             ' RDTSC read time-stamp counter into edx:eax hi lo.
                  ! mov [ebx],eax             ' save low order 4 bytes.
                  ! mov [ebx+4],edx           ' save high order 4 bytes.
                  '---------------------------'
                
                END SUB
                
                '¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤'
                ' Converts Time (HH:MM:SS) TO Seconds since midnight - 86400 max
                FUNCTION TimeToSecs( BYVAL sTime AS tTimeString ) AS LONG
                
                  FUNCTION = (VAL(sTime.tsHH) * 3600) + (VAL(sTime.tsMM) * 60) + VAL(sTime.tsSS)
                
                END FUNCTION
                
                '¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤'
                ' Converts Seconds since midnight TO Time (HH:MM:SS)
                FUNCTION SecsToTime( nSecs AS LONG ) AS STRING
                
                    LOCAL temp AS LONG
                    LOCAL h AS LONG
                    LOCAL m AS LONG
                    LOCAL s AS LONG
                
                    LOCAL t AS uTimeString
                    
                    t.tsData = "  :  :  "
                
                    h = nSecs \ 3600 ' find hours
                    
                    t.ts.tsHH = CHOOSE$(h + 1, "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", _
                                               "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", _
                                               "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", _
                                               "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", _
                                               "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", _
                                               "50", "51", "52", "53", "54", "55", "56", "57", "58", "59")
                    
                    temp = nSecs - (h * 3600)
                    
                    m = temp \ 60 ' find minutes
                
                    t.ts.tsMM = CHOOSE$(m + 1, "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", _
                                               "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", _
                                               "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", _
                                               "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", _
                                               "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", _
                                               "50", "51", "52", "53", "54", "55", "56", "57", "58", "59")
                
                    s = temp - (m * 60) ' find seconds
                
                    t.ts.tsSS = CHOOSE$(s + 1, "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", _
                                               "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", _
                                               "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", _
                                               "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", _
                                               "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", _
                                               "50", "51", "52", "53", "54", "55", "56", "57", "58", "59")
                 
                    FUNCTION = t
                
                END FUNCTION
                
                
                FUNCTION PBMAIN()
                
                  LOCAL cBeg, cEnd AS QUAD  ' for time stamp, measuring cpu clock cycles
                  LOCAL k, nSec, nLoops AS LONG
                  LOCAL sNumRem, sNum, s, sTime AS STRING
                  LOCAL x AS LONG
                
                    time_stamp_count(cBeg) ' measuring cpu clock cycles. The overhead just for making this call is about 25 clocks
                
                    nLoops = 24*60*60 ' Seconds in a day
                    FOR k = 0 TO nLoops-1
                        sTime = SecsToTime( k )
                        nSec  = TimeToSecs( sTime )
                    NEXT
                
                    time_stamp_count(cEnd) ' measuring cpu clock cycles. The overhead just for making this call is about 25 clocks
                
                    ? "Sec =" + STR$(k) + ",   Clock Cycles =" + STR$( (cEnd-cBeg)\nLoops )
                    WAITKEY$
                    
                END FUNCTION
                ------------------

                Comment


                • #28
                  Mike,
                  <<Testing reveals a problem with the LONG - 2147483646>>

                  I just ran your test and it revealed no problems for any input value.

                  Paul.

                  ------------------

                  Comment


                  • #29
                    Mike,
                    there was a problem. I posted the wrong version of the code!
                    It should have used ASCIIZ*12 as the string and not STRING.
                    That's now fixed in the above code.

                    It would be far better to avoid the use of all these stings anyway so see below for the fastest BASIC version. It does need you to use ASCIIZ strings and pass the answer as a parameter rather than returning it as the result of a FUNCTION.

                    Paul.


                    ------------------

                    Comment


                    • #30
                      Mike,
                      I think this'll be my last attempt. I need to do some proper work.
                      The code below is about as fast as I can get it. It no longer uses a FUNCTION, instead, it uses a SUB.
                      The answer string is declared as ASCIIZ in the calling program once only so it doesn't need to be re-created for each call. I suppose a GLOBAL could also be used here. The answer string is then passed as a parameter to the SUB so the SUB doesn't need to create a string either, it just writes the answer into the answer string directly.
                      This avoids all string creation when calling the SUB.

                      If a FUNCTION was used then you can't avoid the creation of the return string which takes far longer than the whole of the rest of the code takes to run.

                      A couple of tweaks have been made. Since it's a self contained SUB, I no longer need to preserve registers (ESI,EDI).

                      The code is now called like this:
                      Code:
                      DIM answer as ASCIIZ*12
                      DIM Number as long
                      ..
                      ..
                      FastLong2Str(Number,Answer)
                      ..
                      and the answer will appear in Answer in about 50 clks including the call overhead.
                      The only way to speed it up more apart from a HUGE lookup table is to call it in ASM when it runs in about 14clks for small numbers (4 digits or less) and about 35clks for big ones.

                      Paul.
                      Code:
                      SUB FastLong2Str( BYVAL LongVar AS LONG , answer AS ASCIIZ*12)
                      
                      !mov eax,LongVar      'get number
                      !mov ecx,answer       'get pointer to answer string
                      !jmp over
                        
                      chartab:
                      !dd "00","10","20","30","40","50","60","70","80","90"
                      !dd "01","11","21","31","41","51","61","71","81","91"
                      !dd "02","12","22","32","42","52","62","72","82","92"
                      !dd "03","13","23","33","43","53","63","73","83","93"
                      !dd "04","14","24","34","44","54","64","74","84","94"
                      !dd "05","15","25","35","45","55","65","75","85","95"
                      !dd "06","16","26","36","46","56","66","76","86","96"
                      !dd "07","17","27","37","47","57","67","77","87","97"
                      !dd "08","18","28","38","48","58","68","78","88","98"
                      !dd "09","19","29","39","49","59","69","79","89","99"
                       
                        
                      over:
                      'on entry eax=number to convert, ecx=pointer to answer buffer (minimum 12 bytes)
                      'on exit, eax,ecx,edx are undefined, all other registers are preserved.
                      'answer is in location pointed to by ecx on entry
                       
                      sdword:
                      'do a signed DWORD to ASCII
                      !or eax,eax                 'test sign
                      !jns udword                 'if +ve, continue as for unsigned
                      !neg eax                    'else, make number positive
                      !mov byte ptr [ecx],"-"     'include the - sign
                      !inc ecx                    'update the pointer
                       
                       
                      udword:
                      'unsigned DWORD to ASCII
                      !mov esi,ecx                'get pointer to answer
                      !mov edi,eax                'save a copy of the number
                       
                      !mov edx, &hD1B71759        '=2^45\10000    13 bit extra shift
                      !mul edx                    'gives 6 high digits in edx
                       
                      !mov eax,&h68DB9            '=2^32\10000+1
                        
                      !shr edx,13                 'correct for multiplier offset used to give better accuracy
                      !jz short skiphighdigits    'if zero then don't need to process the top 6 digits
                       
                       
                      !mov ecx,edx                'get a copy of high digits
                      !imul ecx,10000             'scale up high digits
                      !sub edi,ecx                'subtract high digits from original. EDI now = lower 4 digits
                       
                       
                      !mul edx                    'get first 2 digits in edx
                      !mov ecx,100                'load ready for later
                        
                      !jnc short next1            'if zero, supress them by ignoring
                      !cmp edx,9                  '1 digit or 2?
                      !ja  short ZeroSupressed    '2 digits, just continue with pairs of digits to the end
                      
                      !mov edx,chartab[edx*4]     'look up 2 digits
                      !mov [esi],dh               'but only write the 1 we need, supress the leading zero
                      !inc esi                    'update pointer by 1
                      !jmp short ZS1              'continue with pairs of digits to the end
                       
                      next1:
                      !mul ecx                    'get next 2 digits
                      !jnc short next2            'if zero, supress them by ignoring
                      !cmp edx,9                  '1 digit or 2?
                      !ja  short ZS1a             '2 digits, just continue with pairs of digits to the end
                       
                      !mov edx,chartab[edx*4]     'look up 2 digits
                      !mov [esi],dh               'but only write the 1 we need, supress the leading zero
                      !inc esi                    'update pointer by 1
                      !jmp short ZS2              'continue with pairs of digits to the end
                       
                      next2:
                      !mul ecx                    'get next 2 digits
                      !jnc short next3            'if zero, supress them by ignoring
                      !cmp edx,9                  '1 digit or 2?
                      !ja  short ZS2a             '2 digits, just continue with pairs of digits to the end
                       
                      !mov edx,chartab[edx*4]     'look up 2 digits
                      !mov [esi],dh               'but only write the 1 we need, supress the leading zero
                      !inc esi                    'update pointer by 1
                      !jmp short ZS3              'continue with pairs of digits to the end
                       
                      next3:
                       
                      skiphighdigits:
                      !mov eax,edi                'get lower 4 digits
                       
                      !mov ecx,100
                       
                      !mov edx,&h28F5C29          '2^32\100 +1
                      !mul edx
                      !jnc short next4            'if zero, supress them by ignoring
                      !cmp edx,9                  '1 digit or 2?
                      !ja  short ZS3a             '2 digits, just continue with pairs of digits to the end
                       
                      !mov edx,chartab[edx*4]     'look up 2 digits
                      !mov [esi],dh               'but only write the 1 we need, supress the leading zero
                      !inc esi                    'update pointer by 1
                      !jmp short  ZS4             'continue with pairs of digits to the end
                       
                      next4:
                      !mul ecx                    'this is the last pair so don't supress a single zero
                      !cmp edx,9                  '1 digit or 2?
                      !ja  short ZS4a             '2 digits, just continue with pairs of digits to the end
                       
                      !mov edx,chartab[edx*4]     'look up 2 digits
                      !mov [esi],dh               'but only write the 1 we need, supress the leading zero
                      !mov byte ptr [esi+1],0     'zero terminate string
                       
                      !jmp short  xit               'all done
                       
                       
                      ZeroSupressed:
                      !mov edx,chartab[edx*4]     'look up 2 digits
                      !mov [esi],dx
                      !add esi,2                  'write them to answer
                        
                      ZS1:
                      !mul ecx                    'get next 2 digits
                      ZS1a:
                      !mov edx,chartab[edx*4]     'look up 2 digits
                      !mov [esi],dx               'write them to answer
                      !add esi,2
                        
                      ZS2:
                      !mul ecx                    'get next 2 digits
                      ZS2a:
                      !mov edx,chartab[edx*4]     'look up 2 digits
                      !mov [esi],dx               'write them to answer
                      !add esi,2
                       
                      ZS3:
                      !mov eax,edi                'get lower 4 digits
                      !mov edx,&h28F5C29          '2^32\100 +1
                      !mul edx                    'edx= top pair
                      ZS3a:
                      !mov edx,chartab[edx*4]     'look up 2 digits
                      !mov [esi],dx               'write to answer
                      !add esi,2                  'update pointer
                       
                      ZS4:
                      !mul ecx                    'get final 2 digits
                      ZS4a:
                      !mov edx,chartab[edx*4]     'look them up
                      !mov [esi],dx               'write to answer
                       
                      !mov byte ptr [esi+2],0     'zero terminate string
                       
                      xit:
                      sdwordend:
                      END SUB
                      ------------------

                      Comment


                      • #31
                        >unless you need a faster version still?

                        Of course I don't. I would never write an application which has to format that many numbers that fast; I'd only format 'as needed' which would be when the value has to be printed or displayed, in which case (printing) there is already an I-O delay, and for display there is a small finite number - i.e,, what fits on one screenful and is currently visible - which need to be formatted.

                        BTW, instead of
                        ? FIX(F\L) 'returns 10 wrong
                        try
                        ? FIX(F)\L

                        If you are going to do mixed mode math, at least put the parens where they need to be to generate the results desired.

                        MCM


                        Michael Mattias
                        Tal Systems (retired)
                        Port Washington WI USA
                        [email protected]
                        http://www.talsystems.com

                        Comment


                        • #32
                          'Eric, this uses trusty ol' translation arrays to pre-calculate the $CHOOSE and VAL conversions.
                          'It improves the speed to under 1000 ticks on my machine.
                          '
                          Code:
                          #COMPILE EXE "SECs3.exe" '
                          #DIM ALL
                          
                          TYPE tTimeString
                              tsHH AS WORD
                              tsD1 AS STRING * 1
                              tsMM AS WORD
                              tsD2 AS STRING * 1
                              tsSS AS WORD
                          END TYPE
                          
                          UNION uTimeString
                              ts AS tTimeString
                              tsData AS STRING * 8
                          END UNION
                          
                          GLOBAL hmsArr() AS WORD
                          GLOBAL revArr() AS LONG
                          
                          '¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤'
                          SUB time_stamp_count(tick AS QUAD) ' CPU Clock count    Charles E V Pegge
                          
                            '---------------------------'
                            '                           ' approx because it is not a serialised instruction
                            '                           ' it may execute before or after other instructions
                            '                           ' in the pipeline.
                            ! mov ebx,tick              ' var address where count is to be stored.
                            ! db  &h0f,&h31             ' RDTSC read time-stamp counter into edx:eax hi lo.
                            ! mov [ebx],eax             ' save low order 4 bytes.
                            ! mov [ebx+4],edx           ' save high order 4 bytes.
                            '---------------------------'
                          
                          END SUB
                          
                          '¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤'
                          ' Converts Time (HH:MM:SS) TO Seconds since midnight - 86400 max
                          FUNCTION TimeToSecs( BYVAL sTime AS tTimeString ) AS LONG
                          
                            FUNCTION = revArr(sTime.tsHH) * 3600 + revArr(sTime.tsMM) * 60 + revArr(sTime.tsSS)
                          
                          END FUNCTION
                          
                          '¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤'
                          ' Converts Seconds since midnight TO Time (HH:MM:SS)
                          FUNCTION SecsToTime( nSecs AS LONG ) AS STRING
                          
                              LOCAL temp AS LONG
                              LOCAL h AS LONG
                              LOCAL m AS LONG
                              LOCAL s AS LONG
                              LOCAL t AS uTimeString
                          
                          
                              t.tsData = "  :  :  "
                          
                              h = nSecs \ 3600 ' find hours
                              t.ts.tsHH = hmsArr(h + 1)
                          
                              temp = nSecs - (h * 3600)
                          
                              m = temp \ 60 ' find minutes
                              t.ts.tsMM = hmsArr(m + 1)
                          
                              s = temp - (m * 60) ' find seconds
                              t.ts.tsSS = hmsArr(s + 1)
                          
                          
                              FUNCTION = t
                          
                          END FUNCTION
                          
                          FUNCTION PBMAIN()
                          
                            LOCAL cBeg, cEnd AS QUAD  ' for time stamp, measuring cpu clock cycles
                            LOCAL k, nSec, nLoops AS LONG
                            LOCAL sNumRem, sNum, s, sTime AS STRING
                            LOCAL x AS LONG
                            DIM hmsArr(1 TO 60) AS WORD           'hour min sec array
                            DIM revArr(&h3030 TO &h03939) AS LONG 'reverse hmsArr, indexes "00" to "99"
                          
                              time_stamp_count(cBeg) ' measuring cpu clock cycles. The overhead just for making this call is about 25 clocks
                              FOR x = 1 TO 60
                                 hmsArr(x) = CVWRD(FORMAT$(x - 1, "00")) 'load hour min sec array
                              NEXT
                              FOR x = &h3030 TO &h03939
                                 revArr(x) = VAL(MKWRD$(x))              'load reverse hmsArr, indexes "00" to "99" << 0 to 99 because base 10 not hms
                              NEXT
                          
                              nLoops = 24*60*60 ' Seconds in a day
                              FOR k = 0 TO nLoops-1
                                  sTime = SecsToTime( k )
                                  nSec  = TimeToSecs( sTime )
                              NEXT
                          
                              time_stamp_count(cEnd) ' measuring cpu clock cycles. The overhead just for making this call is about 25 clocks
                          
                              ? "Sec =" + STR$(k) + ",   Clock Cycles =" + STR$((cEnd-cBeg)\nLoops )
                          
                          END FUNCTION
                          '
                          ------------------

                          Comment


                          • #33
                            Paul,

                            Thx i will test it tonite.
                            Its certainly fast enough!
                            AS Michael points out going in this direction is only done a few
                            times for display. Going the other way is much more significant
                            (VAL). BTW the ASM you posted for VAL( sNum AS STRING ) returns
                            07-14152 as -714152.
                            I ignore the minus sign if it appears after the first digit.

                            ------------------
                            Kind Regards
                            Mike

                            Comment

                            Working...
                            X