Bug im Zusammenhang mit Global Dim Array?

Fragen und Bugreports zur PureBasic 4.0-Beta.
Benutzeravatar
Nalfein
Beiträge: 29
Registriert: 22.08.2005 13:14

Bug im Zusammenhang mit Global Dim Array?

Beitrag von Nalfein »

Hallo zusammen!

Ich hatte (bisher... :() ein gut funktionierendes Programm mit selbstgeschriebenen 3D-Abbildungen (reichlich Gebrauch von Sin/Cos).
Sin/Cos hab ich daher in ein Array von 0-90 (oder in der primitiven, aber schnelleren Version von 0-360) oder so geschrieben und je nach Winkel, den ich will (wird an Proc NSin übergeben) wird der jeweilige Wert ausgeben (eventuell um Vorzeichen korrigiert bei der "verfeinerten", die aber langsamer ist....).
Da ich mit Ganzzahlen rechne, habe ich kurzerhand einen um x größeren Wert, als den tatsächlichen Sin-Wert in das Array gespeichert. x ist auch tatsächlich eine Variable (oder vielmehr Konstante, da sie nicht mehr während der Laufzeit geändert wird) und ist der Genauigkeit wegen auf einem Wert von 300 (100 bewirkte zu große Sprünge bei der Abbildung).
Der Typ der einzelnen Array-Elemente war bisher .w (-32000 bis 32000 reicht dicke für jeden denkbaren Faktor...)

Jetzt kommts:

Seit ich das Programm auf PB4 umgeschrieben habe, funktioniert es nicht mehr! Als ich neu anfing, ist mir aufgefallen, daß Sin/Cos irgendwie seltsame Werte enthielten! Nach einem Wert von 120 steht in dem Array eine -120 (in etwa...).
Habe mir das Problem auch mit for a=0 to 360 : plot (a,nsin(a)) : next a anzeigen lassen.
So als ob man den Graph der Sin-Cos-Schwingungen (vertikal) mit einem Faktor multipliziert und alles, was dann oben übersteht (über 128) unten wieder anfügt!
Kann es sein, daß bei Global Arrays der Typ irgendwie ignoriert wird? Oder ist .w nicht mehr 2 byte groß und von -32000 bis 32000?
Oder ist das ein Zugriffsfehler auf Global Arrays?

Das Beste ist noch: wenn ich das Ergebnis von array(a)=sin(a*f)*x durch 3 teile, stimmt wieder alles, nur daß die werte um faktor 3 zu klein sind, aber der Fehler des "zu große Werte=wieder ganz kleine" ist dadurch weg; nur will ich große Werte speichern/korrekt drauf zugreifen!

PS: unter PB 3.94 lief es so!
If OSVersion() <> #PB_OS_Windows_ME : End : EndIf !!!
Benutzeravatar
#NULL
Beiträge: 2237
Registriert: 20.04.2006 09:50

Beitrag von #NULL »

du hast geschrieben

Code: Alles auswählen

a=0 to 360 : plot (a,nsin(a)) : next
Sin() und Cos() erwarten bogenmaß!
-->also:

Code: Alles auswählen

a=0 to 360 : plot (a,n*sin(a*#PI/180.0)) : next
my pb stuff..
Bild..jedenfalls war das mal so.
Kaeru Gaman
Beiträge: 17389
Registriert: 10.11.2004 03:22

Beitrag von Kaeru Gaman »

@NULL

quark, er spricht von seinem Sin-Array. :roll:


@Nalfein

poste doch mal bitte einen kurzen Code:
erzeugung deines Arrays, und z.b. Plotten einer Kurve, sonst nix,
und zwar so, wie es unter 3.94 lief.

ich werde es mal in 3.94 und in 4.00 testen.
Der Narr denkt er sei ein weiser Mann.
Der Weise weiß, dass er ein Narr ist.
Benutzeravatar
Nalfein
Beiträge: 29
Registriert: 22.08.2005 13:14

Code...

Beitrag von Nalfein »

Also da habe ich einmal einen Teil, den ich mittels IncludeFile() bisher am Anfang in alle Programme eingebaut habe, die Sin-Tabelle benötigen könnten:

Code: Alles auswählen

Dim Sinustabelle.w(360)

For a=0 To 359
  Sinustabelle(a)=Round(Sin(a*0.01745329)*cSinCosFaktor,0)
Next

Procedure.b NSin(Grad)
  Shared Sinustabelle
  While Grad>=360
    Grad-360
  Wend
  While Grad<0
    Grad+360
  Wend
  ProcedureReturn Sinustabelle(Grad)
EndProcedure

Procedure.b NCos(Grad)
  ProcedureReturn NSin(Grad+90)
EndProcedure
cSinCosFaktor wird davor natürlich auf einen Wert gesetzt (z.B. 100 für 2 Nachkommastellen, wenn ich dann mit den um 100 zu großen Werten rechne und anschließend für jedes in der Formel vorkommende NSin oder NCos durch 100 teile...)
Bisher hatte ich 300 gewählt, so daß das Array von -300 bis 300 Werte enthalten sollte; aber das geht eben irgendwie nicht mehr, bei Wert<=100 taucht der Effekt nicht auf.

Code: Alles auswählen

WindowWidth=800
Autostretch=1
WindowHeight=600
Options=#pb_window_minimizegadget
If OpenWindow(0,0,0,WindowWidth,WindowHeight,"Programm",Options)
	If OpenWindowedScreen(WindowID(0),0,0,WindowWidth,WindowHeight-20,Autostretch,0,0)
		ShowWindow_(WindowID(0),#SW_MAXIMIZE) ;- Bild maximieren!
		Repeat
flipbuffers()
clearscreen(rgb(0,0,0))
event=windowevent()
if event
; .... irgendwas über Mausbuttons...
else
delay(1)
endif
StartDrawing(ScreenOutput())
For a=0 To 360
PSet(a,300-NSin(a),RGB(0,255,0))
PSet(a,300-NCos(a),RGB(255,0,0))
Next a
stopdrawing()
until (Event=#PB_Event_CloseWindow)
(zugegebenermaßen PB4 inzwischen...) ist meine Anzeige (PSet ist ein in Sicherheitsabfragen gebauter Plot(), weil der konsequent abschmiert, wenn ich auch nur 1 Pixel außerhalb plotte...).
Das ganz natürlich in nem OpenScreen() bzw. OpenWindowedScreen() + Repeat + StartDrawing(ScreenOutPut()) und vor nem StopDrawing() + Until (eigentlich egal; Fenster schließen oder so...)

Den Faktor hinter a oben aus 3.14159 (oder so..) /180 am Anfang vom Programm ausrechnen zu lassen (in f gespeichert) und dann *f zu schreiben brachte auch nix, aber wie gesagt, es lief mal...

Da PB4 ja neuerdings Arrays übergeben haben möchte (klasse Idee übrigens (Ironie) - das fand' ich als ehemaliger TurboPascal'ler am besten an Pb, daß man Arrays NICHT übergeben mußte....) habe ich am Anfang vor Dim natürlich Global geschrieben (das Shared dafür weggelassen).

In PB3.94 sah das etwa so aus: (ohne den Teil der NSin/NCos nutzt)

Code: Alles auswählen

cSinCosFaktor=200
Dim Sinustabelle.w(360)

For a=0 To 359
  Sinustabelle(a)=Round(Sin(a*0.01745329)*cSinCosFaktor,0)
Next

Procedure.b NSin(Grad)
  Shared Sinustabelle
  While Grad>=360
    Grad-360
  Wend
  While Grad<0
    Grad+360
  Wend
  ProcedureReturn Sinustabelle(Grad)
EndProcedure

Procedure.b NCos(Grad)
  ProcedureReturn NSin(Grad+90)
EndProcedure

If InitSprite()=0
End
EndIf

WindowWidth=800
Autostretch=1
WindowHeight=600
Options=#PB_Window_MinimizeGadget
If OpenWindow(0,0,0,WindowWidth,WindowHeight,options,"Programm")
	If OpenWindowedScreen(WindowID(0),0,0,WindowWidth,WindowHeight,Autostretch,0,0)
      ShowWindow_(WindowID(),#SW_MAXIMIZE) ;- Bild maximieren!
      Repeat
FlipBuffers()
ClearScreen(0,0,0)
event=WindowEvent()
If event
; .... irgendwas über Mausbuttons...
Else
Delay(1)
EndIf
StartDrawing(ScreenOutput())
For a=0 To 360
Plot(a,300-NSin(a),RGB(0,255,0))
Plot(a,300-NCos(a),RGB(255,0,0))
Next a
StopDrawing()
Until (Event=#PB_Event_CloseWindow)
EndIf
EndIf
If OSVersion() <> #PB_OS_Windows_ME : End : EndIf !!!
Kaeru Gaman
Beiträge: 17389
Registriert: 10.11.2004 03:22

Beitrag von Kaeru Gaman »

> (ohne den Teil der NSin/NCos nutzt)
> (zugegebenermaßen PB4 inzwischen...)

sorry, du hast mich nicht richtig verstanden.

hast du mal nen kurzen code,
der so wie er ist unter 3.94 läuft und unter 4.0 den von dir beschriebenen fehler produziert?
der muss noch nicht mal was plotten, der kann das auch gerne alles debuggen,
wichtig ist nur, dass der fehler auftritt und sichtbar ist.

darum geht es mir, das ich dir helfen kann, herauszufinden, wo der umsetzungsfehler liegt.

ich werde mir zwar in aller ruhe dein komplettes posting mal durchlesen
und die codes durchdenken, ob mir da was auffällt,
aber eigentlich wollte ich dir bei dem konkreten fehler helfen,
und nicht dir zeigen, wie man ein sin-array aufstellt,
dass es funktioniert unter 3.94 und 4.0

natürlich kann ich das zusätzlich machen, aber ich wollte vorerst nicht deinen kompletten code umschmeißen.

außerdem interessiert mich einfach dieser Bug, denn so ein effekt hat einen Grund,
und wenn man den findet, kann man die fallschlinge auch bei anderen konstrukten vermeiden.

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

also, als allererstes fällt mir auf, dass du
Procedure.b
schreibst, dein Array aber als Word deklarierst.
die Proc kann niemals ein Word zurückliefern, wenn sie Byte deklariert ist.
Der Narr denkt er sei ein weiser Mann.
Der Weise weiß, dass er ein Narr ist.
Benutzeravatar
Nalfein
Beiträge: 29
Registriert: 22.08.2005 13:14

Au!

Beitrag von Nalfein »

Ok, das hat weh getan!

Wenn man den Typ auf denselben stellt wie im Array geht es natürlich. Hatte das .b ganz überlesen (weil es bisher auch ging...). Das .b stammt noch aus ner Zeit, als ich der Meinung war "von -100 bis 100 reicht!"....

Und ich muß mal wieder feststellen, daß das unter TP nicht vorgekommen wäre, da er das als Typenverletzung erkannt hätte! :)

Ich frage mich nur, warum es unter 3.94 lief (siehe letzer Code-Teil im letzten Post von mir...).

Wenn man den Return Typ auf denselben Typ stellt wie im Array, funktioniert es natürlich auch in PB4. Aber warum tritt die Typenverletzung erst da (PB4) als Fehler auf?

Naja, Hauptsache funzt wieder....

Der Vollständigkeit halber einmal den kompletten Code bis jetzt (lauffähig):
Da ist eine Sprungmarke zu der Variable, die man ändern muß, damit in der .b-Variante der Fehler auftritt... (hier korrigiert...)

Code: Alles auswählen

;- Grundlegende Variablen für das Fenster unten; Ein/Aus-Variablen u.a.
; Abbruch=0; unbenutzt=-9999 und sowas...
unbenutzt=-9999
Errorname.s=ProgramFilename()
If Errorname<>""
	Errorname=Left(Errorname,Len(Errorname)-4)
	For a=1 To Len(Errorname)
		If a=1
			temp.s=UCase(Mid(Errorname,a,1))
		Else
			temp.s+LCase(Mid(Errorname,a,1))
		EndIf
	Next a
EndIf
Errorname=temp.s
Errorname+"_error"
If CreateFile(0,Errorname.s+".txt")
	Error=1
	WriteStringN(0,"Error.txt Datei erfolgreich erstellt! (wenn nicht, läsen Sie das hier jetzt nicht...)")
EndIf
If InitSprite()=0
	MessageRequester("Fehler","Sprite Engine konnte nicht initialisiert werden!",0)
	If Error
		WriteStringN(0,"Sprite Engine Start fehlgeschlagen!")
	EndIf
	End
EndIf
If InitKeyboard()=0
	MessageRequester("Fehler","Keyboard konnte nicht initialisiert werden!",0)
	If Error
		WriteStringN(0,"Keyboard Start fehlgeschlagen!")
	EndIf
	End
EndIf
If InitSprite3D()=0
	MessageRequester("Fehler","Sprite 3D Engine konnte nicht initialisiert werden!",0)
	If Error
		WriteStringN(0,"Sprite 3D Engine Start fehlgeschlagen!")
	EndIf
	End
EndIf
If InitMouse()=0
	MessageRequester("Fehler","Mouse lib konnte nicht initialisiert werden!",0)
	If Error
		WriteStringN(0,"Mouse lib Start fehlgeschlagen!")
	EndIf
	End
EndIf
Verstecken=0
Autostretch=1
Zeigen=1
Abbruch=0
Global WindowWidth
Global WindowHeight
WindowWidth=800
WindowHeight=600-20
Options=#PB_Window_MinimizeGadget

Procedure Pset(a,b,c)
	If (a>=0)
		If (a<WindowWidth)
			If (b>=0)
				If (b<WindowHeight)
					Plot(a,b,c)
				EndIf
			EndIf
		EndIf
	EndIf
EndProcedure
;- die Procedure für die FPS-Anzeige
Global Bremsen
Global Wartezeit
Global WunschFPS
Global Blinken
Global Analysieren

Procedure GetFPS()
	Static GetFPS_Count,GetFPS_FPS,GetFPS_Start,BlaCount
	GetFPS_Count+1
	If GetTickCount_()-GetFPS_Start>=1000
		Blinken=1-Blinken
		GetFPS_FPS=GetFPS_Count
		GetFPS_Count=0
		Analysieren=1
		GetFPS_Start=GetTickCount_();+1000 ; neu ermitteln besser! Ansonsten geht man davon aus daß zwischen Abfrage und hier keine Zeit vergeht!
	Else
		If Bremsen=1
			Repeat
				GetFPS_Count+1
			Until GetTickCount_()-GetFPS_Start>=Wartezeit
			BlaCount+1
			If BlaCount>=WunschFPS
				Blinken=1-Blinken
				Analysieren=1
				BlaCount=0
			EndIf
			GetFPS_FPS=WunschFPS ; Wir gehen mal davon aus, daß er das
			GetFPS_Count=WunschFPS ; Richtige tut, da das Messen nicht mehr funktioniert...
			GetFPS_Start=GetTickCount_()
		EndIf
	EndIf
	ProcedureReturn GetFPS_FPS
EndProcedure
Global cSinCosFaktor
;- Fehler tritt auf, wenn man hier 300 statt 100 eingibt:
cSinCosFaktor=300
Global Dim Sinustabelle.w(360)
f.f=(3.141593/180)
For a=0 To 359
  Sinustabelle(a)=Round(Sin(a*f.f)*cSinCosFaktor,0)
Next

Procedure.b NSin(Grad)
  ;Shared Sinustabelle
  While Grad>=360
    Grad-360
  Wend
  While Grad<0
    Grad+360
  Wend
  ProcedureReturn Sinustabelle(Grad)
EndProcedure

Procedure.b NCos(Grad)
	;Shared Sinustabelle
  ProcedureReturn NSin(Grad+90)
EndProcedure
;- Hauptprogramm
If OpenWindow(0,0,0,WindowWidth,WindowHeight,"Programm",Options)
	If OpenWindowedScreen(WindowID(0),0,0,WindowWidth,WindowHeight,Autostretch,0,0)
		ShowWindow_(WindowID(0),#SW_MAXIMIZE) ;- Bild maximieren!
		Repeat
			;- Grundlegende Inputs u.a.
			FlipBuffers()
ClearScreen(RGB(0,0,0))
ExamineKeyboard()
gedruecktesZeichen.s=KeyboardInkey()
x=WindowMouseX(0)
y=WindowMouseY(0)
Event=WindowEvent()
If Event
	Select Event
		Case 513 : Button1=1
		Case 514 : Button1=0
		Case 516 : Button2=1
		Case 517 : Button2=0
		Case 519 : Button3=1 ;: Button1=1 : Button2=1
		Case 520 : Button3=0
		;Case #PB_Event_Gadget
		;Select EventGadgetID()
		;EndSelect
	EndSelect
Else
	Delay(1)
EndIf
			;- Abfrage zum Verstecken des Cursors, falls im Zeichenbereich
			; (benutzt die Variable "Verstecken" aus "InitsUndVars")
			If Verstecken=1
				If (x<>-1) And (y<>-1)
					If (x>-1) And (y>-1) And (x+ScreenX<WindowWidth(0)-RightOffset) And (y+ScreenY<WindowHeight(0)-BottomOffset)
						If Zeigen=1
							ShowCursor_(0)
							Zeigen=0
						EndIf
					Else
						If Zeigen=0
							Zeigen=1
							ShowCursor_(1)
						EndIf
					EndIf
				Else
					If Zeigen=0
						Zeigen=1
						ShowCursor_(1)
					EndIf
				EndIf
			EndIf
			If (button1=1) Or (button2=1)
				MouseDeltaX=x-letztesx
				MouseDeltaY=y-letztesy
				letztesx=x
				letztesy=y
				If button2=1
					Dreh+MouseDeltaX
					Dreh2-MouseDeltaY
				EndIf
				If 0=1
				If dreh2<0 : dreh2=0 : EndIf
				If dreh2>90 : dreh2=90 : EndIf
				EndIf
			Else
				MouseDeltaX=x-letztesx
				MouseDeltaY=y-letztesy
				letztesx=x
				letztesy=y
			EndIf
			obenx=(NSin(Dreh)*NCos(Dreh2))/cSinCosFaktor
obeny=NSin(Dreh2)
obenz=(NCos(Dreh)*NCos(Dreh2))/cSinCosFaktor

seitex=NCos(Dreh)
seitey=0
seitez=-NSin(Dreh)

vornex=-(NSin(Dreh)*NSin(Dreh2))/cSinCosFaktor
vorney=NCos(Dreh2)
vornez=-(NCos(Dreh)*NSin(Dreh2))/cSinCosFaktor
			;- StartDrawing
			StartDrawing(ScreenOutput())
			
				FrontColor(RGB(255,255,255))
				BackColor(RGB(0,0,0))
				DrawingMode(1)
				
				DrawText(0,0,Str(Dreh))
				DrawText(0,20,Str(nsin(Dreh)))
				DrawText(0,40,Str(ncos(Dreh)))
				
				For a=0 To 360
					PSet(a,300-NSin(a),RGB(0,255,0))
					PSet(a,300-NCos(a),RGB(255,0,0))
				Next a
				
				LineXY(100,500,100+vornex,500+vornez,RGB(0,0,255))
				LineXY(100,500,100+seitex,500+seitez,RGB(255,0,0))
				LineXY(100,500,100+obenx,500+obenz,RGB(0,255,0))
				DrawText(0,400,"Oben")
				
				LineXY(400,500,400+vornex,500-vorney,RGB(0,0,255))
				LineXY(400,500,400+seitex,500-seitey,RGB(255,0,0))
				LineXY(400,500,400+obenx,500-obeny,RGB(0,255,0))
				DrawText(300,400,"Von Vorne")
				
				LineXY(700,500,700-vornez,500-vorney,RGB(0,0,255))
				LineXY(700,500,700-seitez,500-seitey,RGB(255,0,0))
				LineXY(700,500,700-obenz,500-obeny,RGB(0,255,0))
				DrawText(700,400,"Rechte Seite")
				
				DrawText(750,0,Str(GetFPS()))
			StopDrawing()
			If KeyboardPushed(#PB_Key_Escape) :Abbruch=1 : EndIf
		Until (Event=#PB_Event_CloseWindow) Or (Abbruch=1) ; Nur Abbruch=1! ; Close Window per Sicher?-Abfrage absichern!
	Else
		MessageRequester("Fehler","Windowed Screen konnte nicht initialisiert werden!",0)
		End
	EndIf
Else
	MessageRequester("Fehler","Window konnte nicht initialisiert werden!",0)
	End
EndIf
End
Danke nochmal! Manchmal braucht man einfach einen 2ten der es liest, weil man genau "weiß" 'ich habe in Zeile X habe ich das letzte mal was verändert; jetzt taucht ein Fehler auf, also muß er in Zeile x sein....' und nicht an näherliegendes Sachen denkt! :)
If OSVersion() <> #PB_OS_Windows_ME : End : EndIf !!!
Kaeru Gaman
Beiträge: 17389
Registriert: 10.11.2004 03:22

Beitrag von Kaeru Gaman »

:D das war also der Bug?

na dann, hab gerne geholfen.


noch "tips" zu deinen Nsin&NCos

1) du benötigst keine while-schleife, um den wert auf 0-359 zu kürzen, ein modulo

Code: Alles auswählen

Sinustabelle(Grad%360)
tut es auch.

2) ich würde den cosinus auch direkt berechnen

Code: Alles auswählen

Sinustabelle((Grad+90)%360)
und nicht einen weiteren proc-aufruf dazumachen (performance)

3) als Macro wärs noch schneller

Code: Alles auswählen

Macro Nsin(Grad)
  Sinustabelle(Grad%360)
EndMacro
und
4) wenn du nicht mit 360° arbeitest, sondern mit einer 2er-potenz (z.b. 512° oder 1024°)
dann kannst du auch auf den Modulo verzichten, und benutzt einfach ein AND:

Code: Alles auswählen

f.f=(3.141593/256) 
For a=0 To 511 
  Sinustabelle(a)=Round(Sin(a*f.f)*cSinCosFaktor,0) 
Next 
Macro Nsin(Grad)
  Sinustabelle(Grad&511)
EndMacro
damit hättest du dann 512° für den Vollkreis. ;)
Der Narr denkt er sei ein weiser Mann.
Der Weise weiß, dass er ein Narr ist.
Benutzeravatar
Nalfein
Beiträge: 29
Registriert: 22.08.2005 13:14

Tips

Beitrag von Nalfein »

Die Idee mit dem Modulo hatte ich (nachdem es implementiert wurde) tatsächlich auch mal, aber Purebasic bekommt für negative Winkel modulo 360 leider auch negative Zahlen raus!
Ohne Sicherheitsabfrage wird das trotzdem nichts glaube ich...
Entweder muß dann ne Abfrage mit "if ergebnis<0 : ergebnis+360 : endiff" oder ich Addiere zum Ergebnis nochmal 360 und mache nochmal modulo 360 (also (Grad%360+360)%360, sollte doch auf jeden Fall Positiv sein, das... ).
Nur modulo produziert "Array out of bounds" (wenn man es mit negativen Winkeln aufruft)
If OSVersion() <> #PB_OS_Windows_ME : End : EndIf !!!
Kaeru Gaman
Beiträge: 17389
Registriert: 10.11.2004 03:22

Beitrag von Kaeru Gaman »

mit der &-lösung kannst du die ganze problematik umschiffen...

...musst dich halt dran gewöhnen, dass ein vollkreis 512° hat.
Der Narr denkt er sei ein weiser Mann.
Der Weise weiß, dass er ein Narr ist.
Benutzeravatar
Nalfein
Beiträge: 29
Registriert: 22.08.2005 13:14

Tips

Beitrag von Nalfein »

Mein "Sinustabelle((Winkel%360+360)%360)" sieht zwar etwas seltsam aus, funktioniert aber (erstmal) und sogar als Makro. Scheint wirklich schnell zu sein, vorher ging die FPS manchmal von 61 auf 45, nur bei dem Anzeigen hier.... Jetzt bleibt er bei 61. :D Im optimieren für Geschwindigkeit bin ich leider nich so gut... Und wenn Modulo oder was neues das ich ausprobiere nicht auf Anhieb will, nehm ich was, was garantiert funktioniert (hier: While-Schleifen...) Aber so wie oben führt die Formel zu keinen Array out of bounds, also...
Das mit dem AND merke ich mir mal, wenn ich das nächste mal das Programm bei 0 anfange, weil wieder ein systematischer Fehler drin ist, den ich nicht rauskriege :) (kommt öfter vor...)

Nochmal Danke!
If OSVersion() <> #PB_OS_Windows_ME : End : EndIf !!!
Gesperrt