Huge #'s
- Michael Vogel
 - Addict

 - Posts: 2821
 - Joined: Thu Feb 09, 2006 11:27 pm
 - Contact:
 
Hi Mike,mike74 wrote:CopyMemory(@t, *s, Len(t))
PB4.02 brings up an error, that a number is expecteted instead of a string...
If I do a debug *s in the procedure, I'll see an (empty) string, s=0, @*s=0, @s=address
Changing the *s parameter in the CopyMemory to anything else, I'll get a memory access error...
Therefore I've never got clear with pointers
- Michael Vogel
 - Addict

 - Posts: 2821
 - Joined: Thu Feb 09, 2006 11:27 pm
 - Contact:
 
Why sorry, you're trying to help memike74 wrote:Michael,
I just realized I had made another change...Take the ".s" off the *s.s in the procedure. Sorry!
Anyway, I'm not clever enough to bring it up running
Actual error: Destination memory pointer is 0
Actual question: would the CopyMemory command reserve new memory for the variable which will be kept (in case of things like garbage collection)?
Code: Select all
Procedure GetNumber(*s,*num.VNF)
	Protected n
	Protected t.s
	If *num\sign
		t="-"
	EndIf
	n=*num\len
	t+Str(*num\num[n])
	While n>0
		n-1
		t+RSet(Str(*num\num[n]),#VNFLen,"0")
	Wend
	CopyMemory(@t,*s,Len(t))
EndProcedure- Michael Vogel
 - Addict

 - Posts: 2821
 - Joined: Thu Feb 09, 2006 11:27 pm
 - Contact:
 
Okaymike74 wrote:Actual answer: I don't know...I would think not, but I really don't know.
Did you initialize your Zahl3 variable with a value like PDwyer suggested?
When I initialize zahl3 (e.g. zahl=space(1000)) than my initial version (*s=t) seems to work fine - but what is going on in the memory?
Do I have produced a memory leak, when assigning the shorter content "t" to the larger "s" space? How can this be done (= programmed) correctly? Where are the real experts?
Because I'm not able to do so, I made the routine Number() :roll:
Michael
@Michael Vogel:
The "GetNumber" procedure can be handled in a simple way. You can replace it with your "Number" procedure and use it this way:
You have a more serious problem with the VNFMul(*a.VNF,*b.VNF,*c.VNF) procedure.
It fails with factors whose digits produce large carry's. This happens with 2 factors as small as "99999" and "99999". This means that if a group of these digits appears in both factors the product will be wrong One example is "10000999990" and "99999000000". The result should be "1000089998000010000000" but your routine returns "1000089998000". The routine I used (provided by Helle acutally) can handle 2 factors of at least 11111 digits that are all 9's.
			
			
													The "GetNumber" procedure can be handled in a simple way. You can replace it with your "Number" procedure and use it this way:
Code: Select all
;instead of 
zahl3.s
GetNumber(@zahl3,@num1)
;use
zahl3.s=Number(num1) ;just rename this to "GetNumber"It fails with factors whose digits produce large carry's. This happens with 2 factors as small as "99999" and "99999". This means that if a group of these digits appears in both factors the product will be wrong One example is "10000999990" and "99999000000". The result should be "1000089998000010000000" but your routine returns "1000089998000". The routine I used (provided by Helle acutally) can handle 2 factors of at least 11111 digits that are all 9's.
					Last edited by Demivec on Thu Jul 19, 2007 11:31 pm, edited 2 times in total.
									
			
									
						Another variation of Demivec's code:
			
			
									
									
						Code: Select all
Procedure.s MultiplyBig(factor_1$,factor_2$) ;returns Prod$ as factor_1$*factor_2$, factors can have leading zeroes and be negative
 
  Protected n.l,len_factor_1.l,len_factor_2.l,len_diff.l
 
  len_factor_1 = Len(factor_1$)
  len_factor_2 = Len(factor_2$)
  If len_factor_2 > len_factor_1 ;make it so larger# is always factor_1
    Swap factor_1$,factor_2$
    Swap len_factor_1,len_factor_2
  EndIf
  n = len_factor_1
  len_diff = n - len_factor_2 + 1
 
  Protected n_2.l = n + n
  Protected Dim factor_1.l(n)
  Protected Dim prod.l(n_2)
  Protected Prod$,I.l,J.l,s.l,temp.l,factor_2_sub_I.l, z.l
  prod$ = Space(n_2)
 
  z = 0
  For I = len_diff To n    ;digit# for factor_1
    factor_2_sub_I = PeekB(@factor_2$ + z) - 48
    z + 1
    If factor_2_sub_I > 0
      For J = 1 To n       ;digit# for factor_2
        prod(I + J) + (PeekB(@factor_1$ + J - 1) - 48) * factor_2_sub_I
      Next J
    EndIf
  Next I
  
  temp = 0
  For I = n_2 To len_diff Step -1 ;digits# of product
    z = prod(I) + temp
    temp = Int(z / 10)
    CopyMemory(Str(z - 10 * temp), @prod$ + I - 1, 1)
  Next I
  For I = 0 To (len_factor_1 + len_factor_2) ;digits# of product
    If (PeekB(@prod$ + I) - 48) > 0 ;find first non-zero digit
      prod$ = Right(prod$, Len(prod$) - I)
      Break
    EndIf
  Next I 
  
  If PeekB(@factor_1$) = 45 ;,1) ;get sign of factor_1
    s = 1
  EndIf
 
  If PeekB(@factor_2$) = 45 ;,1) ;combine with sign of factor_2
    s ! 1
  EndIf
 
  If s = 1  ;=sign will be negative
    prod$ = "-" + Prod$
  EndIf
 
  ProcedureReturn Prod$
EndProcedure
F1$="-123456789098765432198765432123456789"
F2$="5555555556666666666777777777777777778765434567654"
StartTime = ElapsedMilliseconds()
For t = 1 To 10000
P$=MultiplyBig(F1$, F2$)
Next t
  ElapsedTime = ElapsedMilliseconds()-StartTime
Debug ElapsedTime
Debug P$- Michael Vogel
 - Addict

 - Posts: 2821
 - Joined: Thu Feb 09, 2006 11:27 pm
 - Contact:
 
Hi Demivec, you're cool!Demivec wrote:@Michael Vogel:
The "GetNumber" procedure can be handled in a simple way. You can replace it with your "Number" procedure and use it this way:You have a more serious problem with the VNFMul(*a.VNF,*b.VNF,*c.VNF) procedure.Code: Select all
;instead of zahl3.s GetNumber(@zahl3,@num1) ;use zahl3.s=Number(num1) ;just rename this to "GetNumber"
It fails with factors whose digits produce large carry's. This happens with 2 factors as small as "99999" and "99999". This means that if a group of these digits appears in both factors the product will be wrong One example is "10000999990" and "99999000000". The result should be "1000089998000010000000" but your routine returns "1000089998000". The routine I used (provided by Helle acutally) can handle 2 factors of at least 11111 digits that are all 9's.
First, I was amused about your "solution" of GetNumber - but to be honest I'm still interested if someone can give me a hint how to get the GetNumber procedure working...
Then, I was really nervous that I've done something wrong (what still can be), but after a quick check I saw that my routine is not only fast but seems also to work fine (at least with your example)
So please check it once more, maybe there's a bug somewhere. But maybe there are other reasons for your wrong results, maybe the PB version does not support quads or the variable for the third parameter is the same variable like one of the multipliers (so VNFMul(@a,@a,@b) is allowed, but VNFMul(@a,@b,@a) not!)
If you need bigger numbers, you've to imcrease #VNFDim (e.g. #VNFDim=10000 to allow numbers with 90000 ciphers;)
I did not a lot of testing, just some benchmarks - but with all examples (for now) I got the same results like Helle & Co...
Code: Select all
num1.VNF
num2.VNF
num3.VNF
TA = ElapsedMilliseconds()
For i = 1 To 10000
	PutNumber(@F1$,@num1)
	PutNumber(@F2$,@num2)
	VNFMul(@num1,@num2,@num3)
Next
TE = ElapsedMilliseconds() - TA
p$=Number(@num3)
DIGIT=Len(p$)
MessageRequester("BCD-Multiply, Test1", P$ + #LFCR$ + "Time : " + Str(TE) + " ms" + #LFCR$ + "Digits : " + Str(DIGIT))
Global F3$ = "2"
TA = ElapsedMilliseconds()
PutNumber(@F3$,@num1)
For i = 1 To 15
	VNFMul(@num1,@num1,@num2)
	VNFCopy(@num2,@num1)
Next
p$=Number(@num2)
DIGIT=Len(p$)
TE = ElapsedMilliseconds() - TA
MessageRequester("Time : " + Str(TE) + " ms   Digits : " + Str(DIGIT), Mid(P$, 2500, DIGIT-2499) + #LFCR$ + "Time : " + Str(TE) + " ms" + #LFCR$ + "Digits : " + Str(DIGIT))
Here's an update to Helle's code (it has been nicknamed mine for the moment).  This takes into account some of mike74's suggestions as well as a few of my own.  I estimate it's about twice as fast as before.
@Michael Vogel: I think I may have been the bug in your code.  I was trying to improve it's speed and I think I made a wrong turn 
.
I couldn't test your new sample because I didn't have the VFNCopy procedure.
Since you really want GetNumber your way (instead of the easy way
 ) use this:
One additional note, when you pass a structure such as num1 or zahl3 (in my example) you don't need to use @.  The address is all that will be sent whether or not you use @.
			
			
													Code: Select all
Procedure.s MultiplyBig(factor_1$,factor_2$) ;returns Prod$ as factor_1$*factor_2$, handles negative numbers
  
  Protected n.l,len_factor_1.l,len_factor_2.l
  len_factor_1 = Len(factor_1$)
  len_factor_2 = Len(factor_2$)
  If len_factor_2 > len_factor_1 ;make it so larger# is always factor_1
    Swap factor_1$,factor_2$
    Swap len_factor_1 , len_factor_2
  EndIf
  n=len_factor_1
  CallDebugger
  Protected maxNSize.l = n + len_factor_2,len_diff = len_factor_1 - len_factor_2
  Protected Dim prod.l(maxNSize)
  Protected Prod$=Space(maxNSize),I.l,J.l,adj.l,digit.l,carry.l,s.l
 
  adj.l = -len_diff - 1
  For I = 1 + len_diff To n ;digit# for factor_2
    factor_2_sub_I=PeekB(@factor_2$ + I + adj) & $F ;read next digit from factor_2
    If factor_2_sub_I > 0
      For J = 1 To n ;digit# for factor_1
        prod(I - len_diff + J) + (PeekB(@factor_1$ + J - 1) & $F)* factor_2_sub_I
      Next J
    EndIf
  Next I
  
  For I = maxNSize To 1 Step -1 ;digits# of product
    digit = prod(I) + carry
    carry = Int(digit / 10)
    PokeB(@Prod$ + I - 1,(digit - 10 * carry) | $30)
  Next I
  
  For I = 0 To (maxNSize - 1) ;digits# of product
    If PeekB(@Prod$ + I) > 48 ;find first non-zero digit
      Prod$ = Right(Prod$, maxNSize - I)
      Break
    EndIf
  Next I
  
  If PeekB(@factor_1$) = 45 ;get sign of factor_1
    s = 1
  EndIf
  
  If PeekB(@factor_2$) = 45 ;combine with sign of factor_2
    s ! 1
  EndIf
  
  If s = 1  ;=sign will be negative
    Prod$="-"+Prod$
  EndIf
  
  ProcedureReturn Prod$
EndProcedure 
;-test
; zahl1.s="123456789098765432198765432123456789"
; zahl2.s="5555555556666666666777777777777777778765434567654"
zahl1.s="10000999990"
zahl2.s="99999000000"
; zahl1.s="99999"
; zahl2.s="99999999"
n=1000000
Prod$=""
t1=-GetTickCount_()
For I=1 To n
  Prod$=MultiplyBig(zahl1,zahl2)
Next I
t1+GetTickCount_()
MessageRequester(Str(t1)+" ms For "+Str(n)+" multiplications",zahl1+" x "+zahl2+" = "+#LF$+Prod$)I couldn't test your new sample because I didn't have the VFNCopy procedure.
Since you really want GetNumber your way (instead of the easy way
Code: Select all
Procedure GetNumber(*s.String,*num.VNF)
  Protected n
  Protected T.s
  
  If *num\sign
    T="-"
  EndIf
  
  n=*num\len
  T+Str(*num\num[n])
  
  While n>0
    n-1
    T+RSet(Str(*num\num[n]),#VNFLen,"0")
  Wend
  
  *s\s=T;   Does work!!!!
  
EndProcedure
zahl3.String ;use a structure for a string
GetNumber(@zahl3,@num1)
Debug zahl3\s
					Last edited by Demivec on Fri Jul 20, 2007 3:14 am, edited 1 time in total.
									
			
									
						Rather than continue to post new versions here, I've posted my version to a wikispaces page.  I'll still report significant updates here, if there are any.  Also, the page has an RSS feed.  The page is at http://mike74swiki.wikispaces.com/Big+Multiply and the RSS feed is http://mike74swiki.wikispaces.com/page/ ... ?v=rss_2_0.
The latest version works for negative numbers and chooses between two looping methods to reduce the number of operations as much as possible, based on the number of zeros in the numbers and the numbers' lengths.
			
			
									
									
						The latest version works for negative numbers and chooses between two looping methods to reduce the number of operations as much as possible, based on the number of zeros in the numbers and the numbers' lengths.
- Michael Vogel
 - Addict

 - Posts: 2821
 - Joined: Thu Feb 09, 2006 11:27 pm
 - Contact:
 
Demivec wrote: @Michael Vogel:
[1] I couldn't test your new sample because I didn't have the VFNCopy procedure.
[2] Since you really want GetNumber your way (instead of the easy way) use this:
[3] ...you don't need to use @...Code: Select all
Procedure GetNumber(*s.String,*num.VNF) Protected n Protected T.s If *num\sign T="-" EndIf n=*num\len T+Str(*num\num[n]) While n>0 n-1 T+RSet(Str(*num\num[n]),#VNFLen,"0") Wend *s\s=T; Does work!!!! EndProcedure zahl3.String ;use a structure for a string GetNumber(@zahl3,@num1) Debug zahl3\s
Hi Demivec,
thanks for your reply, for [1] you'll can see the actual code below - I think it's about 5 to 10 times faster than all other routines - I would be happy of course if someone will be able to tune it a little bit
I've also include a Add and Sub procedure now (not very well tested for now), so next step would be Div...
Hopefully, all routines work with positive and negative integers.
[2] - I can't get it running, I entered all changes (.String and \s) but there's still an error when running (Null pointer), I'm using PB4.02 Win -- you're right, Number() is doing the same, but there are things I just want to know how it can be done
[3] - Thanks again, looks better when reading the source...
And here's the "full" code (sorry for the long postings, mikes idea with a wiki is a clever thing) - now the benchmark from the last post will run...
Code: Select all
; Define VNF - Vogels Number Format
	#VNFDim=10000; ~90000 Ciphers ;)
	#VNFLen=9
	#VNFMod=1000000000
	#VNFNegative=-1
	#VNFPositive=0
	Structure VNF
		len.w
		sign.w
		num.l[#VNFDim]
	EndStructure
	Declare.s Number(*num.VNF);						String
	Declare GetNumber(*s.s,*num.VNF);			s <- num
	Declare PutNumber(*s.s,*num.VNF);			s -> num
	
	Declare VNFNormalize(*a.VNF);					a = (a)
	Declare VNFCopy(*a.VNF,*b.VNF);				b = a
	Declare VNFMul(*a.VNF,*b.VNF,*c.VNF);	c = a*b
	Declare VNFAdd(*a.VNF,*b.VNF,*c.VNF);	c = a+b
	Declare VNFSub(*a.VNF,*b.VNF,*c.VNF);	c = a-b
	Declare.l VNFGt(*a.VNF,*b.VNF);				true, if a>b
	Declare.l VNFEq(*a.VNF,*b.VNF);				true, if a=b
; EndDefine
Procedure.s Number(*num.VNF)
	Protected n
	Protected t.s
	If *num\sign
		t="-"
	EndIf
	n=*num\len
	t+Str(*num\num[n])
	While n>0
		n-1
		t+RSet(Str(*num\num[n]),#VNFLen,"0")
	Wend
	ProcedureReturn t
EndProcedure
Procedure GetNumber(*s.String,*num.VNF)
	; ACHTUNG! s ist bei dieser Routine vom Typ String!
	; (bei x.string bedeutet dies den Aufruf "GetNumber(x,number)" und die Ausgabe "Debug x\s")
	Protected n
	Protected t.s
	If *num\sign
		t="-"
	EndIf
	n=*num\len
	t+Str(*num\num[n])
	While n>0
		n-1
		t+RSet(Str(*num\num[n]),#VNFLen,"0")
	Wend
	*s\s=t
EndProcedure
Procedure PutNumber(*s.s,*num.VNF)
	Protected n,p
	Protected t.s
	*num\sign=0
	t=PeekS(@*s)
	n=Len(t)-#VNFLen
	While n>0
		*num\num[p]=Val(PeekS(@t+n,#VNFLen))
		p+1
		n-#VNFLen
	Wend
	n=Val(PeekS(@t,9+n))
	If n<0
		*num\sign=#VNFNegative
		n=-n
	EndIf
	*num\num[p]=n
	*num\len=p
EndProcedure
Procedure VNFNormalize(*a.VNF)
	; Strap leading zeros (000.000.000)
	While (*a\len>=0) And (*a\num[*a\len]=0)
		*a\len-1
	Wend
	; Value equal Zero? Positiv sign
	If *a\len<0
		*a\len=0
		*a\sign=#VNFPositive
	EndIf
EndProcedure
Procedure VNFCopy(*a.VNF,*b.VNF)
	Protected n=*a\len
	*b\len=n
	*b\sign=*a\sign
	Repeat
		*b\num[n]=*a\num[n]
		n-1
	Until n<0
EndProcedure
Procedure.l VNFEq(*a.VNF,*b.VNF)
	Protected n,d
	If n<>*b\len
		ProcedureReturn #False
	ElseIf *a\sign<>*b\sign
		ProcedureReturn #False
	Else
		While n>=0
			If *a\num[n]<>*b\num[n]
				ProcedureReturn #False
			EndIf
			n-1
		Wend
		ProcedureReturn #True;	a=b
	EndIf
EndProcedure
Procedure.l VNFGt(*a.VNF,*b.VNF)
	Protected n,d
	If *a\sign<>*b\sign
	
		If *a\sign=#VNFPositive
			ProcedureReturn #True
		Else
			ProcedureReturn #False
		EndIf
	
	Else
	
		n=*a\len
		If n>*b\len
			ProcedureReturn #True
		ElseIf n<*b\len
			ProcedureReturn #False
		Else
			While n>=0
				d=*a\num[n]-*b\num[n]
				If d>0
					ProcedureReturn #True
				ElseIf d<0
					ProcedureReturn #False
				EndIf
				n-1
			Wend
			ProcedureReturn #False;	a=b
		EndIf
		
	EndIf
	
EndProcedure
Procedure VNFMul(*a.VNF,*b.VNF,*c.VNF)
	Protected la
	Protected lb
	Protected n
	Protected s.q
	Protected z.q
	*c\sign=-(*a\sign * *b\sign)
	*c\len=*a\len+*b\len+1
	For n=0 To *c\len
		*c\num[n]=0
	Next n
	la=0
	Repeat
		lb=0
		z=*b\num[lb]
		Repeat
			s=*a\num[la] * *b\num[lb] + *c\num[la+lb]
			;Debug Str(*a\num[la])+" * "+Str(*b\num[lb])+" + "+Str(*c\num[la+lb+1])
			;Debug " = "+StrQ(s)+"  -> "+Str(s/#VNFMod)+" | "+Str(s%#VNFMod)
			*c\num[la+lb]=s%#VNFMod
			*c\num[la+lb+1]+s/#VNFMod
			lb+1
		Until lb>*b\len
		la+1
	Until la>*a\len
	VNFNormalize(*c)
EndProcedure
Procedure VNFAdd(*a.VNF,*b.VNF,*c.VNF)
	Protected n
	Protected s
	If *a\sign<>*b\sign
		;Debug "sub"
		*b\sign=*a\sign
		VNFSub(*a.VNF,*b.VNF,*c.VNF)
	Else
		*c\sign=*a\sign
		If *a\len<*b\len
			Swap *a,*b
		EndIf
		While n<=*a\len
			s+*a\num[n]
			If n<=*b\len
				s+*b\num[n]
			EndIf
			*c\num[n]=s%#VNFMod
			s/#VNFMod
			n+1
		Wend
		If s
			*c\num[n]=s
		Else
			n-1
		EndIf
		*c\len=n
	EndIf
EndProcedure
Procedure VNFSub(*a.VNF,*b.VNF,*c.VNF)
	Protected n
	Protected s
	If *a\sign<>*b\sign
		;Debug "add"
		*b\sign=*a\sign
		VNFAdd(*a.VNF,*b.VNF,*c.VNF)
	Else
		If VNFGt(*a.VNF,*b.VNF)
			*c\sign=*a\sign
		Else
			;Debug "swp"
			Swap *a,*b
			*c\sign=#VNFNegative-*a\sign
		EndIf
		While n<=*a\len
			s+*a\num[n]
			If n<=*b\len
				s-*b\num[n]
			EndIf
			If s<0
				*c\num[n]=s+#VNFMod
				s=-1
			Else
				*c\num[n]=s
				s=0
			EndIf
			n+1
		Wend
		*c\len=n-1
		VNFNormalize(*c)
	EndIf
EndProcedure
num1.VNF
num2.VNF
num3.VNF
;zahl1.s="123456789098765432198765432123456789"
zahl1.s="5555555556666666666777777777777777778765434567654"
zahl2.s="-5555555556666666666777777777777777778765434567653"
PutNumber(@zahl1,num1)
PutNumber(@zahl2,num2)
VNFAdd(num1,num2,num3)
Debug Number(num3)
					Last edited by Michael Vogel on Sun Jul 22, 2007 9:58 am, edited 1 time in total.
									
			
									
						I'm use PB4.02 Win also. What do you mean that there's an error using Null pointer? I thought the example I gave included a Null pointer.Michael Vogel wrote:[2] - I can't get it running, I entered all changes (.String and \s) but there's still an error when running (Null pointer), I'm using PB4.02 Win -- you're right, Number() is doing the same, but there are things I just want to know how it can be done
Regarding long postings, PM me and we'll post highlights here. (I don't know anything about setting up Wiki's). I am interrested in finding a fast and versatile way for using big #'s. Yes, I've heard there's some out there somewhere, but a PB one would be nice. I had done optimizations on PutNumber, GetNumber, and was working on VNFMult before I made a wrong turn.
