Page 1 of 1

PB5 : Convert Value to Base N

Posted: Tue Feb 26, 2013 11:24 pm
by eddy
- Base N conversion functions

BaseN.pbi

Code: Select all

Procedure.s  IntToBaseN(value.i, baseChars.s)
   ; convert integer number to base N (returns EMPTY if error)
   Protected targetBase = Len(baseChars)
   Protected result.s = ""
   If targetBase < 2 Or targetBase > SizeOf(value)*4 : ProcedureReturn "" : EndIf
   
   Repeat  
      result = PeekS(@baseChars + value % targetBase, 1) + result
      value = value/targetBase
   Until (value <= 0)
   ProcedureReturn result
EndProcedure

Procedure.i BaseNtoInt(strValue.s, baseChars.s)
   ; convert base N number to integer (returns -1 if error)
   Protected targetBase = Len(baseChars)
   Protected result = 0
   If targetBase < 2 Or targetBase > SizeOf(result)*4 : ProcedureReturn -1 : EndIf
   
   For i = 1 To Len(strValue)
      v = FindString(baseChars, Mid(strValue, i, 1)) - 1
      If v = -1 : ProcedureReturn -1 : EndIf ; 
      result * targetBase + v
   Next 
   
   ProcedureReturn result
EndProcedure

Code: Select all

Debug IntToBaseN(101,"01") ;<---- Bin = base2

For i=0 To 15
   Debug IntToBaseN(i,"0123456789ABCDEF") ;<---- Hexa = base16 
Next 

customBase.s="ABCDEFGHIJKLMNOPQRSTUVWXYZ_"
Debug IntToBaseN(7561231232,customBase); <--- custom Base
Debug BaseNtoInt("TNZVJIF",customBase)

CompilerIf #PB_Compiler_Processor=#PB_Processor_x64
   If (BaseNtoInt(IntToBaseN(9223372036854775807,customBase),customBase)=9223372036854775807)
      Debug "Result is correct - BaseN functions work!"
   EndIf 
CompilerElse 
   If (BaseNtoInt(IntToBaseN(2147483647,customBase),customBase)=2147483647)
      Debug "Result is correct - BaseN functions work!"
   EndIf 
CompilerEndIf

Re: Convert Value to Base N

Posted: Wed Feb 27, 2013 7:37 am
by Joakim Christiansen
Better/shorter than the one I wrote some time ago, very clever :D

EDIT:
It can be made even shorter if you replace:

Code: Select all

CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
  ;64 is the worst cast buffer size For base 2 And int.MaxValue
  If targetBase>64 : ProcedureReturn "" : EndIf 
CompilerElse 
  ;32 is the worst cast buffer size For base 2 And int.MaxValue
  If targetBase>32 : ProcedureReturn "" : EndIf 
CompilerEndIf
With:

Code: Select all

If targetBase>SizeOf(value)*4 : ProcedureReturn "" : EndIf

Re: Convert Value to Base N

Posted: Wed Feb 27, 2013 8:04 am
by eddy
code updated :wink:

Re: Convert Value to Base N

Posted: Sun Mar 03, 2013 5:21 am
by BasicallyPure
This was a nice addition to my bag of tricks.
Thanks.

So I was thinking how could the process be reversed to convert a Base N to integer.
This is what I came up with.
It seems to be working as far as I have tested it.
I could use some help with checking for error conditions.
I didn't fully understand the use of 'SizeOf()' in the error check.

In theory using both procedures together could convert any base to any other base.

BP.

Code: Select all

Procedure.s  IntToBaseN(value.i, baseChars.s)
   ; convert integer number to base N
   Protected targetBase = Len(baseChars)
   Protected result.s = ""
   
   ; returns EMPTY if error
   If targetBase < 2 Or targetBase > SizeOf(value)*4 : ProcedureReturn "" : EndIf
   
   Repeat  
      result = PeekS(@baseChars + value % targetBase, 1) + result
      value = value/targetBase
   Until (value <= 0)
   
   ProcedureReturn result
EndProcedure

Procedure BaseNtoInt(strValue.s, baseChars.s)
   ; convert base N number to integer
   Protected base = Len(baseChars)
   Protected j = Len(strValue)
   Protected exp = j
   Protected value, v
   
   For i = 1 To j
      exp - 1
      v = FindString(baseChars, Mid(strValue, i, 1)) - 1
      If v = -1 : ProcedureReturn -1 : EndIf ; if error return -1
      value + (v * Pow(base, exp))
   Next i
   
    ProcedureReturn value
EndProcedure



Debug IntToBaseN(309,"01") ;<---- Bin = base2
Debug BaseNtoInt("100110101", "01")
Debug ""

Debug IntToBaseN(7561231232,"ABCDEFGHIJKLMNOPQRSTUVWXYZ_"); <--- custom Base
Debug BaseNtoInt("TNZVJIF", "ABCDEFGHIJKLMNOPQRSTUVWXYZ_")
Debug ""

Debug IntToBaseN(55, "01234567") ;<--- Octal
Debug BaseNtoInt("67", "01234567")
edit: added error check

Re: Convert Value to Base N

Posted: Sun Mar 03, 2013 5:57 am
by skywalk

Re: Convert Value to Base N

Posted: Tue Mar 05, 2013 1:07 am
by eddy
BasicallyPure wrote:In theory using both procedures together could convert any base to any other base.
There is a minor bug in your last function

Code: Select all

 If v < 0 : ProcedureReturn -1 : EndIf

Re: Convert Value to Base N

Posted: Tue Mar 05, 2013 2:53 am
by BasicallyPure
eddy wrote:There is a minor bug in your last function
oops, here is what it should be.

Code: Select all

If v = -1 : ProcedureReturn -1 : EndIf
Your way, v < 0 works too.

Re: Convert Value to Base N

Posted: Tue Mar 05, 2013 8:45 am
by wilbert
Here's a different way using a global table.
It's faster if you are not switching base all the time.
Also the Repeat/Until and While/Wend blocks inside the conversion procedures should be easy to convert to ASM if speed it really important.

Code: Select all

Structure BaseN_Table
  n.a[256]
EndStructure

Global IntToBaseN_Table.BaseN_Table
Global BaseNtoInt_Table.BaseN_Table
Global BaseN_TargetBase

Procedure SetBaseChars(baseChars.s)
  Protected *c.Character = @baseChars
  FillMemory(@IntToBaseN_Table, 256, 255, #PB_Ascii)
  FillMemory(@BaseNtoInt_Table, 256, 255, #PB_Ascii)
  BaseN_TargetBase = 0
  While *c\c
    IntToBaseN_Table\n[BaseN_TargetBase] = *c\c
    BaseNtoInt_Table\n[*c\c] = BaseN_TargetBase
    BaseN_TargetBase + 1
    *c + SizeOf(Character)
  Wend
EndProcedure

Procedure.s IntToBaseN(value.i, baseChars.s = "")
  Protected result.s, *c.Character, v
  
  If baseChars : SetBaseChars(baseChars) : EndIf
  If BaseN_TargetBase < 2 : ProcedureReturn "" : EndIf
  
  If value : v = Round(Log(value)/Log(BaseN_TargetBase), #PB_Round_Up) : Else : v = 1 : EndIf
  
  result = Space(v)
  *c = @result + (v - 1) * SizeOf(Character)
  Repeat
    v = value
    value / BaseN_TargetBase
    v - value * BaseN_TargetBase
    *c\c = IntToBaseN_Table\n[v]
    *c - SizeOf(Character)
  Until value <= 0
  
  ProcedureReturn result
EndProcedure

Procedure BaseNtoInt(strValue.s, baseChars.s = "")
  Protected result.i = 0, *c.Ascii, v
  
  If baseChars : SetBaseChars(baseChars) : EndIf
  If BaseN_TargetBase < 2 : ProcedureReturn -1 : EndIf
  
  *c = @strValue
  While *c\a
    v = BaseNtoInt_Table\n[*c\a]
    If v = 255
      result = -1
      Break
    Else
      result * BaseN_TargetBase
      result + v
      *c + SizeOf(Character)
    EndIf
  Wend
  
  ProcedureReturn result
EndProcedure


SetBaseChars("01")
Debug IntToBaseN(309) ;<---- Bin = base2
Debug BaseNtoInt("100110101")
Debug ""

SetBaseChars("ABCDEFGHIJKLMNOPQRSTUVWXYZ_")
Debug IntToBaseN(7561231232); <--- custom Base
Debug BaseNtoInt("TNZVJIF")
Debug ""
 
SetBaseChars("01234567")
Debug IntToBaseN(55) ;<--- Octal
Debug BaseNtoInt("67")