Pow() and Abs() for integers

Got an idea for enhancing PureBasic? New command(s) you'd like to see?
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by freak.

Hi All,

The PB Manual says for Abs():
This function works correctly only with float numbers. With integer it will fail if the integer is too big (losse of precision). Another function will be added to do that.
And the Pow() function works that way, too.

When are these other functions going to be added?
They would be very useful, since I don't use floats very much.

Please Fred, can u do that?

That's it...
Timo

--

A debugged program is one for which you have not yet found the conditions that make it fail.
Bonne_den_kule
Addict
Addict
Posts: 841
Joined: Mon Jun 07, 2004 7:10 pm

Post by Bonne_den_kule »

... can you do that?
User avatar
Kaeru Gaman
Addict
Addict
Posts: 4826
Joined: Sun Mar 19, 2006 1:57 pm
Location: Germany

Post by Kaeru Gaman »

second!


@Bonne

as a workaround:
if you need n² or n³, it's better and faster than a Pow()-function to write (n*n) or (n*n*n)
oh... and have a nice day.
User avatar
Dreamland Fantasy
Enthusiast
Enthusiast
Posts: 335
Joined: Fri Jun 11, 2004 9:35 pm
Location: Glasgow, UK
Contact:

Post by Dreamland Fantasy »

Just do something like the following:

Code: Select all

Procedure.q AbsQ(Value.q)
  If Value < 0
    ProcedureReturn -Value
  Else
    ProcedureReturn Value
  EndIf
EndProcedure

Procedure.q PowQ(Number, Exponent)
  Protected Result.q = 1
  For i = 1 To Exponent
    Result * Number
  Next
  ProcedureReturn Result
EndProcedure

Debug AbsQ(-1234567890123456789)
Debug PowQ(3, 3)
Kind regards,

Francis.
eesau
Enthusiast
Enthusiast
Posts: 589
Joined: Fri Apr 27, 2007 12:38 pm
Location: Finland

Post by eesau »

What I'd like to have is a ^ -operator for integer Pow().
User avatar
Kaeru Gaman
Addict
Addict
Posts: 4826
Joined: Sun Mar 19, 2006 1:57 pm
Location: Germany

Post by Kaeru Gaman »

@DF

[edited]
I pimped your Proc a bit...

Code: Select all

Procedure.q PowQ(Number.q, Exponent.q)
  Protected Result.q = Number
  If Exponent = 2
    Result * Number
  ElseIf Exponent > 2
    For i = 2 To Exponent
      Result * Number
    Next
  ElseIf Exponent = 0
    Result = 1
  ElseIf Exponent < 0
    Result = -1
  EndIf
  ProcedureReturn Result
EndProcedure
oh... and have a nice day.
User avatar
Dreamland Fantasy
Enthusiast
Enthusiast
Posts: 335
Joined: Fri Jun 11, 2004 9:35 pm
Location: Glasgow, UK
Contact:

Post by Dreamland Fantasy »

Kaeru Gaman wrote:@DF

[edited]
I pimped your Proc a bit...

Code: Select all

Procedure.q PowQ(Number.q, Exponent.q)
  Protected Result.q = Number
  If Exponent = 2
    Result * Number
  ElseIf Exponent > 2
    For i = 2 To Exponent
      Result * Number
    Next
  ElseIf Exponent = 0
    Result = 1
  ElseIf Exponent < 0
    Result = -1
  EndIf
  ProcedureReturn Result
EndProcedure
Nice! :D

The only thing that's really missing is a check in case you get numerical overflow. I've used -2 as the error code.

Code: Select all

Procedure.q PowQ(Number.q, Exponent.q)
  Protected Result.q = Number
  If Exponent = 2
    Result * Number
  ElseIf Exponent > 2
    For i = 2 To Exponent
      Result * Number
      If Result < 0                  ; Check for numerical overflow
        ProcedureReturn -2
      EndIf
    Next
  ElseIf Exponent = 0
    Result = 1
  ElseIf Exponent < 0
    Result = -1
  EndIf
  ProcedureReturn Result
EndProcedure
Kind regards,

Francis.
User avatar
Kaeru Gaman
Addict
Addict
Posts: 4826
Joined: Sun Mar 19, 2006 1:57 pm
Location: Germany

Post by Kaeru Gaman »

a simple <0 check is not sufficent, since a negative Base to an odd Exponent gives a correct negative result.

Code: Select all

Procedure.q PowQ(Number.q, Exponent.q)
  Protected Result.q = Number
  Protected Sign.l = 1
  If Exponent = 2
    Result * Number
  ElseIf Exponent > 2
    If Number < 0
      Result = -Result
      Number = -Number
      If Exponent % 2
        Sign = -1
      EndIf
    EndIf
    For i = 2 To Exponent
      Result * Number
      If Result < 0                  ; Check for numerical overflow
        ProcedureReturn -2
      EndIf
    Next
    Result * Sign 
  ElseIf Exponent = 0
    Result = 1
  ElseIf Exponent < 0
    Result = -1
  EndIf
  ProcedureReturn Result
EndProcedure
your check is correct to be performed in every step of the loop,
but it will significantly slow down the calculation.
oh... and have a nice day.
User avatar
Dreamland Fantasy
Enthusiast
Enthusiast
Posts: 335
Joined: Fri Jun 11, 2004 9:35 pm
Location: Glasgow, UK
Contact:

Post by Dreamland Fantasy »

Kaeru Gaman wrote:a simple <0 check is not sufficent, since a negative Base to an odd Exponent gives a correct negative result.
Very true.

Thinking about it further having negative numbers as error codes in this case would cause confusion if you get these numbers returned as legitimate results (e.g. PowQ(-1, 1)).

Kind regards,

Francis.
User avatar
Kaeru Gaman
Addict
Addict
Posts: 4826
Joined: Sun Mar 19, 2006 1:57 pm
Location: Germany

Post by Kaeru Gaman »

I thought about it a little more...

I think, #Null would be a practicable result for an Error,
1. it's #False
2. only 0 as Base can cause 0 as Result, that can be easily checked before calling.

also, I tested overflow with <1, because also 0 can be an overflow.

all the rest, including the Sign check, remains the same.

Code: Select all

Procedure.q PowQ(Number.q, Exponent.q)
  Protected Result.q = Number ; Buffer Base to use as result for Exp.=1
  Protected Sign.l = 1        ; default sign for Result is +
  If Exponent = 2             ; for Exp.2
    Result * Number           ; spare Loop
  ElseIf Exponent > 2
    If Number < 0             ; Negative Base?
      Result = -Result        ; calculate with
      Number = -Number        ; Abs. Value
      If Exponent % 2         ; for odd Exponents
        Sign = -1             ; Buffer neg. Sign
      EndIf
    EndIf
    For i = 2 To Exponent     ; Loop for Exp.>2
      Result * Number
      If Result < 1           ; Check for numerical overflow
        ProcedureReturn 0   ; #False is Errorcode
      EndIf
    Next
    Result * Sign             ; sign Result
  ElseIf Exponent = 0         ; for Exp.=0
    Result = 1                ; no Calculation needed
  ElseIf Exponent < 0         ; for neg.Exp.
    Result = 0                ; #False is Errorcode
  EndIf
  ProcedureReturn Result
EndProcedure
oh... and have a nice day.
User avatar
Dreamland Fantasy
Enthusiast
Enthusiast
Posts: 335
Joined: Fri Jun 11, 2004 9:35 pm
Location: Glasgow, UK
Contact:

Post by Dreamland Fantasy »

Kaeru Gaman wrote:I thought about it a little more...

I think, #Null would be a practicable result for an Error,
1. it's #False
2. only 0 as Base can cause 0 as Result, that can be easily checked before calling.

also, I tested overflow with <1, because also 0 can be an overflow.

all the rest, including the Sign check, remains the same.

Code: Select all

Procedure.q PowQ(Number.q, Exponent.q)
  Protected Result.q = Number ; Buffer Base to use as result for Exp.=1
  Protected Sign.l = 1        ; default sign for Result is +
  If Exponent = 2             ; for Exp.2
    Result * Number           ; spare Loop
  ElseIf Exponent > 2
    If Number < 0             ; Negative Base?
      Result = -Result        ; calculate with
      Number = -Number        ; Abs. Value
      If Exponent % 2         ; for odd Exponents
        Sign = -1             ; Buffer neg. Sign
      EndIf
    EndIf
    For i = 2 To Exponent     ; Loop for Exp.>2
      Result * Number
      If Result < 1           ; Check for numerical overflow
        ProcedureReturn 0   ; #False is Errorcode
      EndIf
    Next
    Result * Sign             ; sign Result
  ElseIf Exponent = 0         ; for Exp.=0
    Result = 1                ; no Calculation needed
  ElseIf Exponent < 0         ; for neg.Exp.
    Result = 0                ; #False is Errorcode
  EndIf
  ProcedureReturn Result
EndProcedure
Nice! 8)

However, thinking about this even further you would still have problems under the following circumstance:

Code: Select all

Debug PowQ(9223372036854775806, 2)
This doesn't flag up as an overflow since Result is still positive and gives you an answer of 4. I've revised your code and added a check to catch this:

Code: Select all

Procedure.q PowQ(Number.q, Exponent.q)
  Protected Result.q = Number         ; Buffer Base to use as result for Exp.=1
  Protected Sign.l = 1                ; default sign for Result is +
  If Number < 0                       ; Negative Base?
    Result = -Result                  ; calculate with
    Number = -Number                  ; Abs. Value
    If Exponent % 2                   ; for odd Exponents
      Sign = -1                       ; Buffer neg. Sign
    EndIf
  EndIf
  For i = 2 To Exponent
    Result * Number
    If Result < 1 Or Result < Number  ; Check for numerical overflow
      ProcedureReturn 0               ; #False is Errorcode
    EndIf
  Next
  Result * Sign                       ; sign Result
  If Exponent = 0                     ; for Exp.=0
    Result = 1                        ; no Calculation needed
  ElseIf Exponent < 0                 ; for neg.Exp.
    Result = 0                        ; #False is Errorcode
  EndIf
  ProcedureReturn Result
EndProcedure
Kind regards,

Francis.
User avatar
Kaeru Gaman
Addict
Addict
Posts: 4826
Joined: Sun Mar 19, 2006 1:57 pm
Location: Germany

Post by Kaeru Gaman »

Dreamland Fantasy wrote:This doesn't flag up as an overflow since Result is still positive and gives you an answer of 4. I've revised your code and added a check to catch this:
Fine! 8)
maybe now it's finish... good teamwork.

btw:
it would be freakin' easier in ASM, because an overflow sets a CPU-Flag,
so only one cheap branch-command is needed.

I don't know what's the command in x86-ASM, here's pseudocode:

Code: Select all

  For i = 2 To Exponent
    Result * Number
    !BranchOnCarryClear [Label]
      ProcedureReturn 0
    Label:
  Next 
oh... and have a nice day.
User avatar
Dreamland Fantasy
Enthusiast
Enthusiast
Posts: 335
Joined: Fri Jun 11, 2004 9:35 pm
Location: Glasgow, UK
Contact:

Post by Dreamland Fantasy »

Kaeru Gaman wrote:btw:
it would be freakin' easier in ASM, because an overflow sets a CPU-Flag,
so only one cheap branch-command is needed.
I came up with this code incorporating your suggestion:

Code: Select all

; Make sure Inline ASM is enabled

Procedure.q PowQ(Number.q, Exponent.q)
  Protected Result.q = Number         ; Buffer Base to use as result for Exp.=1
  Protected Sign.l = 1                ; default sign for Result is +
  If Number < 0                       ; Negative Base?
    Result = -Result                  ; calculate with
    Number = -Number                  ; Abs. Value
    If Exponent % 2                   ; for odd Exponents
      Sign = -1                       ; Buffer neg. Sign
    EndIf
  EndIf
  For i = 2 To Exponent
    Result * Number
    !JO l_fail                        ; Jump to fail if overflow flag is set
  Next
  Result * Sign                       ; sign Result
  If Exponent = 0                     ; for Exp.=0
    Result = 1                        ; no Calculation needed
  ElseIf Exponent < 0                 ; for neg.Exp.
    Result = 0                        ; #False is Errorcode
  EndIf
  ProcedureReturn Result
  
  fail:
  ProcedureReturn 0
  
EndProcedure

Debug PowQ(9223372036854775805, 2)
Debug PowQ(1000, 1000)
Both of the example cases should fail, but the first one still gives an answer, which is probably because the overflag hasn't been set since the sign of the number has not changed. But then, I'm not an expert on x86 assembly language so maybe my implementation is flawed.

Kind regards,

Francis.
User avatar
Kaeru Gaman
Addict
Addict
Posts: 4826
Joined: Sun Mar 19, 2006 1:57 pm
Location: Germany

Post by Kaeru Gaman »

you made me getting my old ASM book from '93 out of the dustchest...

meanwhile I'm that far:

Code: Select all

buff1.l
buff2.l

!PUSHFD
!POP [v_buff1]

Debug Bin(buff1)

Debug 9223372036854775805*9223372036854775805

!PUSHFD
!POP [v_buff2]

Debug Bin(buff2)

End
so, there are obviously Flags set by the multiplication,
but I still have to look up wich flag is wich...
oh... and have a nice day.
User avatar
Dreamland Fantasy
Enthusiast
Enthusiast
Posts: 335
Joined: Fri Jun 11, 2004 9:35 pm
Location: Glasgow, UK
Contact:

Post by Dreamland Fantasy »

Kaeru Gaman wrote:you made me getting my old ASM book from '93 out of the dustchest...
:D

I've changed it from checking the overflow flag to the carry flag and that seems to work on the examples I've tried:

Code: Select all

Procedure.q PowQ(Number.q, Exponent.q) 
  Protected Result.q = Number         ; Buffer Base to use as result for Exp.=1 
  Protected Sign.l = 1                ; default sign for Result is + 
  If Number < 0                       ; Negative Base? 
    Result = -Result                  ; calculate with 
    Number = -Number                  ; Abs. Value 
    If Exponent % 2                   ; for odd Exponents 
      Sign = -1                       ; Buffer neg. Sign 
    EndIf 
  EndIf 
  For i = 2 To Exponent 
    Result * Number 
    !JC l_fail                        ; Jump to fail if carry flag is set 
  Next 
  Result * Sign                       ; sign Result 
  If Exponent = 0                     ; for Exp.=0 
    Result = 1                        ; no Calculation needed 
  ElseIf Exponent < 0                 ; for neg.Exp. 
    Result = 0                        ; #False is Errorcode 
  EndIf 
  ProcedureReturn Result 
  
  fail: 
  ProcedureReturn 0 
  
EndProcedure 

Debug PowQ(9223372036854775805, 2)    ; Should return 0 (fail)
Debug PowQ(1000, 1000)                ; Should return 0 (fail)
Debug PowQ(3,3)                       ; Should return 27
Debug PowQ(-3, 3)                     ; Should return -27
Kind regards,

Francis.
Post Reply