Page 1 of 1

For/Next with [float] variable Step

Posted: Tue Sep 17, 2013 2:16 pm
by PB
I always wanted a For/Next with a [float] variable for Step,
and it was mandatory for me that it still looked Basic-like.
This is what I came up with quickly. Seems to work okay,
but I may have missed something. Use at your own risk! :)

It uses two macros to do the magic. The macro "Forr" sets
up the loop, and takes 4 parameters: variable name, start,
finish, and step. If start>finish then the loop counts up, but
if start<finish then the loop counts down (like "Step -1").

The step parameter can be a variable, or float, or BOTH! :D

The second macro, "Nextt()", just finishes the loop. Enjoy!

Code: Select all

; For/Next with floats and/or variable for Step.
; By PB -- Do whatever the heck you want with it.
; Yes, I know other float values affect precision!

Macro Forr(var,start,finish,inc)
  CompilerIf start<finish
    var=start-inc : While var<finish : var+inc
  CompilerElse
    var=start+inc : While var>finish : var-inc
  CompilerEndIf
EndMacro

Macro Nextt()
  Wend
EndMacro

stp.f=0.5 ; Float *and* variable for Step!

Forr(a.f,1,5,stp) ; For a = 1 To 5 Step 0.5
  Debug a ; 1.0, 1.5, 2.0, 2.5, 3.0, [...]
Nextt()

Forr(a.f,4.5,1,0.5) ; For a = 4.5 To 1 Step -0.5
  Debug a ; 4.5, 4.0, 3.5, 3.0, 2.5, [...]
Nextt()

Re: For/Next with [float] variable Step

Posted: Tue Sep 17, 2013 4:00 pm
by Thorium
Here is the fix for the incorrect iterations because of inprecisision of floats.

It fixes it by casting the float point to a fixed point and doing a round.
The precision is 3 digits after point. Increase multiply to add higher precision, but remember you trade it off for how high the number can go, it's fixed point comparision. And it's slow as hell.

It still does not fix the infinit loops on small fractions. Thats also possible to fix but makes it even slower.

Code: Select all

Macro Forr(var,start,finish,inc)
  
  CompilerIf start<finish
    
    var=start-inc
    While Round(var * 1000, #PB_Round_Nearest) < Round(finish * 1000, #PB_Round_Nearest)
      var+inc

  CompilerElse
    
    var=start+inc
    While Round(var * 1000, #PB_Round_Nearest) > Round(finish * 1000, #PB_Round_Nearest)
      var-inc

  CompilerEndIf
    
EndMacro

Macro Nextt()
  Wend
EndMacro

Forr(a.f, 1, 1.1, 0.05)
  Debug a
Nextt()

Re: For/Next with [float] variable Step

Posted: Tue Sep 17, 2013 4:07 pm
by Demivec
You can't use 'CompilerIF/CompilerElse/CompilerEndif' in the macros the way you have because they won't be able to check the value of a runtime variable at compiletime.


Also, after the macro's code appears the compiler functions will have become fixed and not changeable. This means that only one of the conditions will be compiled for every call of the macro no matter what parameters are used as part of the call. All the loops will count up or they all will count down.


@Edit: It appears that the compiler functions are evaluated for each use of the macro and I was wrong on this point. I think this is different for previous versions of PureBasic. I'm glad it appears to have been changed.

Re: For/Next with [float] variable Step

Posted: Tue Sep 17, 2013 4:55 pm
by BorisTheOld
It's a bad policy to use floating point variables for loop counters and loop increments. This has been discussed at length in other threads.

Loop counts are, by definition, integral values. So even if floating point values are needed in the loop, the actual counting should always be done using integer values. This will prevent rounding errors from causing incorrect loop counts. So instead of trying to use:

Code: Select all

For A.d = 1 To 1.1 Step 0.05
.
.
Next
Use the following instead and, if needed, scale the value of the loop counter back to a floating point value.

Code: Select all

  
For X.i = 100 To 110 Step 5
.
. A = X / 100
.
Next

Re: For/Next with [float] variable Step

Posted: Tue Sep 17, 2013 4:57 pm
by Thorium
It works as long as you dont use variables for start and finish.

Re: For/Next with [float] variable Step

Posted: Tue Sep 17, 2013 5:03 pm
by jassing
He's not using (the equivilant) of

for x = a to b step c

(which, doesn't work anyway)

The usage is only good for the
for x = 1 to 10 step 2
type of for loop.

Re: For/Next with [float] variable Step

Posted: Tue Sep 17, 2013 5:10 pm
by BorisTheOld
One first creates integer variables for the From, To, and Step values. Then one scales the floating point values (multiplying by 10^n) to create suitable integer values.

But the problem still exists that arbitrary rounding decisions still need to be made when scaling the floating point numbers to integer values. And this can affect the actual number of loop cycles, which may or may not be a problem.

Re: For/Next with [float] variable Step

Posted: Tue Sep 17, 2013 7:02 pm
by minimy
Hi PB and all the people of the post. :)
The first is thank for share. The idea is very interesting.
I think this is the easy way to do this, and no have problems with division by cero.

Code: Select all

;
NumStart= 0
NumEnd  = 5
Stip.f  = 0.5

  NumFinis= NumEnd/Stip
  For X.i = NumStart To NumFinis
    Debug  X * Stip
  Next

Re: For/Next with [float] variable Step

Posted: Wed Sep 18, 2013 1:34 am
by Demivec
Thorium wrote:It works as long as you dont use variables for start and finish.
@Thorium, jassing: You are correct. I think it would be an easy mistake to make though because nothing indicates otherwise.


It also appears that I was wrong about the compiler functions only being evaluated once for all uses of the macro. I recall that was the way it functioned in the past. It appears that it has been changed and the compiler functions at each place the macro code is used. That may open up some possibilities for creating more procedure-like macros that vary depending on the values of compiler-evaluated functions.

Re: For/Next with [float] variable Step

Posted: Sun Sep 22, 2013 1:21 pm
by mk-soft
Maybe so

Code: Select all

Macro ForF(varf,startf,endf,stepf)
 
  If startf < endf
    varf = startf - stepf
  Else
    varf = startf + stepf
  EndIf
  While (startf < endf And Round(varf * 1000, #PB_Round_Nearest) < Round(endf * 1000, #PB_Round_Nearest)) Or (startf > endf And Round(varf * 1000, #PB_Round_Nearest) > Round(endf * 1000, #PB_Round_Nearest))
    If startf < endf
      varf + stepf
    Else
      varf - stepf
    EndIf
EndMacro

Macro Nextf()
  Wend
EndMacro

Debug "ForF(a.f, 1.0, 1.2, 0.05)"
ForF(a.f, 1.0, 1.2, 0.05)
  Debug "a: " + a
  Debug "ForF(a.f, 1.2, 1.0, 0.05)"
  ForF(b.f, 1.2, 1.0, 0.05)
    Debug "b: " + b
  NextF()
  Debug "-------------------------"  
NextF()
  
or

Code: Select all

Macro ForF(varf,startf,endf,stepf)
 
  If startf < endf
    varf = startf - stepf
  Else
    varf = startf + stepf
  EndIf
  While (startf < endf And varf < (endf - stepf * 0.5)) Or (startf > endf And varf > (endf + stepf * 0.5))
    If startf < endf
      varf + stepf
    Else
      varf - stepf
    EndIf
EndMacro

Macro Nextf()
  Wend
EndMacro

Debug "ForF(a.f, 1.0, 1.2, 0.05)"
ForF(a.f, 1.0, 1.2, 0.05)
  Debug "a: " + a
  Debug "ForF(a.f, 1.2, 1.0, 0.05)"
  ForF(b.f, 1.2, 1.0, 0.05)
    Debug "b: " + b
  NextF()
  Debug "-------------------------"  
NextF()
P.S. Best result with double

Code: Select all

Debug "ForF(a.d, 1.0, 1.2, 0.05)"
ForF(a.d, 1.0, 1.2, 0.05)
  Debug "a: " + a
  Debug "ForF(a.d, 1.2, 1.0, 0.05)"
  ForF(b.d, 1.2, 1.0, 0.05)
    Debug "b: " + b
  NextF()
  Debug "-------------------------"  
NextF()
 
GT :wink:

Re: For/Next with [float] variable Step

Posted: Tue Nov 05, 2013 5:31 pm
by blueznl
Call me stupid (hey, I can handle that 8) ) but isn't the regular While / Wend not good enough? And it is very basic like! :lol:
a.f = 1.0
while a < 10
...
...
a = a+.1
wend