Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

Tris

Treue Seele

  • »Tris« ist der Autor dieses Themas

Beiträge: 102

Wohnort: ~Stuttgart

  • Private Nachricht senden

1

14.05.2021, 08:27

C# Floats durch 0 teilen

Ich habe gerade festgestellt, dass C# keine Exception wirft, wenn ich eine float durch 0 teile.

Auf Stackoverflow habe ich das gefunden:

Zitat

According to Microsoft, "Floating-point arithmetic overflow or division by zero never throws an exception, because floating-point types are based on IEEE 754 and so have provisions for representing infinity and NaN (Not a Number)."


Aber was genau bedeutet das nun? Teilt er dann einfach nicht, weil er die 0 dann nicht als Zahl erkennt und gut ist?
Sollte ich sicherheitshalber 0 Werte abfangen und z.B. 0.0001 daraus machen? ?(

Betrifft die Skalierung von UVs in Unity:

Quellcode

1
uvsArray[i] = new Vector2(vertices[i].x / uvScale, vertices[i].y / uvScale);

David Scherfgen

Administrator

Beiträge: 10 382

Wohnort: Hildesheim

Beruf: Wissenschaftlicher Mitarbeiter

  • Private Nachricht senden

2

14.05.2021, 09:13

Du kriegst als Ergebnis "unendlich" (float.PositiveInfinity oder float.NegativeInfinity - je nach Vorzeichen) raus.

Tris

Treue Seele

  • »Tris« ist der Autor dieses Themas

Beiträge: 102

Wohnort: ~Stuttgart

  • Private Nachricht senden

3

14.05.2021, 09:22

Danke! Ich vermute mal "unendlich" ist in diesem Fall auch nicht gut und sollte verhindert werden?
Wobei ich beim Testen keine Fehlerhafte Darstellung feststellen konnte. Das irrtiert mich doch sehr. 8|

David Scherfgen

Administrator

Beiträge: 10 382

Wohnort: Hildesheim

Beruf: Wissenschaftlicher Mitarbeiter

  • Private Nachricht senden

4

14.05.2021, 11:51

Was ist denn dein konkreter Anwendungsfall?
Wenn so ein Unendlich-Wert in die GPU gelangt, ist das eher nicht gut. GPUs implementieren IEEE 754 nicht gezwungenermaßen vollständig, so dass man nicht genau weiß, was dann passiert.

Jonathan

Community-Fossil

  • Private Nachricht senden

5

14.05.2021, 12:01

Naja, sieht ja eher nach CPU Code aus.

Die Frage ist, wieso sollte UvScale jemals 0 sein? Wo kommen die Werte her? Überprüfst du, ob deine Eingabedaten sinnvoll sind?

Ich würde davon ausgehen, dass die floats danach einfach den Wert 'unendlich' haben, und du, wenn du das Ergebnis irgendwo anders zum Weiterrechnen benutzt, nie wieder auf vernünftige Zahlen kommst. Bei UVs dürfte sich dass dann darin zeigen, dass deine Texturen falsch aussehen.

Ich meine, probier es halt einfach mal aus. Im Zweifelsfalle mal im Debugger die Werte angucken. Dann solltest du recht schnell sehen, was passiert.
Lieber dumm fragen, als dumm bleiben!

David Scherfgen

Administrator

Beiträge: 10 382

Wohnort: Hildesheim

Beruf: Wissenschaftlicher Mitarbeiter

  • Private Nachricht senden

6

14.05.2021, 14:47

CPU-Code, ja, aber dem Namen der Variable nach zu urteilen (uvsArray) wird das Ergebnis genutzt, um irgendwelche Geometrie zu rendern (auf der GPU).

Tris

Treue Seele

  • »Tris« ist der Autor dieses Themas

Beiträge: 102

Wohnort: ~Stuttgart

  • Private Nachricht senden

7

15.05.2021, 01:54

Ich beschäftige mich gerade mit der Erstellung von vers. Meshformen und in dem Tutorial wird ein Slider im Inspector (Unity) eingefügt um die Texturen zu skalieren.
Im Tutorial werden die möglichen Werte nicht eingegrenzt und ich hatte eigentlich damit gerechnet, dass 0 Werte zu Problemen führen, aber er schluckt alle Werte problemlos und skaliert die Texture entsprechend.
Das hat mich einfach irritiert.
Bei negativen Werten wird die Texture wohl gespiegelt wenn ich das richtig erkennen kann.

David Scherfgen

Administrator

Beiträge: 10 382

Wohnort: Hildesheim

Beruf: Wissenschaftlicher Mitarbeiter

  • Private Nachricht senden

8

15.05.2021, 07:03

Wie sieht es denn bei einer Skalierung von 0 aus, verglichen mit einer Skalierung von z. B. 0.001? Eigentlich dürftest du dann gar keine Textur mehr sehen, weil ja alles Vertices dieselben UV-Koordinaten von "unendlich" kriegen.

9

15.05.2021, 12:47

c# hat einige Features um ganz automatisch bestimmte Stolpersteine bei Spezialsituationen, an die man normalerweise garnicht Denkt, selber zu lösen. z.B. kann man Math.Abs(float.NaN) aufrufen, und der Rückgabewert wird nicht zu noch mehr Blödsinn, sondern bleibt float.NaN.
Und auch Bit-Operatoren, berücksichtigen automatisch die CPU-Architektur, so das man z.B. auf einen BigEndian System und einen LittleEndian System, den exakt gleichen Programmcode eintippen kann, und es kommt sozusagen das gleiche Ergebnis raus. So das man da also garnichts beachten muss. Selbst diese Daten dann per Netzwerk an einen Rechner mit anderer CPU-Architektur zu senden, funktioniert problemlos. Lediglich wenn man bewust sehr tief runter geht um z.B. eine eigene Binäre Serialisierung zu programmieren, müsste man da einwenig darauf achten.

Was nun float und 0 angeht... muss man sich meist auch keine Gedanken machen. Erst bei sowas wie Modulo 0 (z.B. float x = 7f % 0f) weiß auch c# nichtmehr wie man das Sinnvoll handhaben kann und wirft eine Exception.
Grundsätzlich kann man bei c# eigentlich immer davon ausgehen, wenn es irgendeine Sinvolle Möglichkeit gibt mit einer besonderen Situation bei einer Operation umzugehen, dann wurde das auch implementiert. Und nur wenn es keine Sinnvolle Möglichkeit gibt, oder mehrere unterschiedliche aber "gleich gute Lösungsergebnisse" bei denen c# nicht Wissen kann was der Programmierer beforzugt, erst dann wird eine Exception geworfen.

Oben drauf kommt da dann nun Unity, was auch nochmal einige kleine versteckte aber schöne Verbesserungen mit sich bringt. z.B. haben alle primitive Datentypen, ganz besonders float, in sich eingebaut eine performance optimierung auf ihre Operatoren bekommen. Zusätzlich dann noch die Mathf welche man anstelle von Math nutzen kann/sollte (Math.Abs() -> Mathf.Abs()) da dort dann auch diese weiteren Funktionen Performanceoptimiert sind.


Übrigens, schon mal Gedanken gemacht über sowas hier?
if(anyFloat == 7.0f)

Normalerweise wäre das gefährlicher Code, da aufgrund der unumgänglichen minimalen Ungenauigkeiten bei Fließkommazahlen, eine exaktes == sehr problematisch ist. Doch in c#/Unity ist der == Operator implementiert mit sozusagen der Logik: "A ist auf +/- 1.401298E-45F ungefähr gleich zu B"
Solche Dinge meine ich mit Stolpersteine bei Spezialsituationen an die man normalerweise garnicht Denkt, die aber automatisch für einen gelöst werden ;)


Was nun deine 0 und Texturskalierung angeht.... da kommt nun auch noch der Shader dazu. Die in Unity mitgelieferten Shader, und ebenso auch bei den Shadern deren Code man in Unity selber schreibt, läuft eine wirklich extreme optimierung drüber wenn sie compiliert werden. Und ja, an den Code der Unity Shader kommt man ran und die nutzen die gleiche Magie/Mittel/Funktione/etc. wie die Shader die man selber schreibt. Und eben bei diesen extremen optimierungen beim compilieren, werden auch wieder einige (leider nicht alle) Stolpersteine berücksichtigt. Hier kommt es sehr darauf an, welche Funktionen man selber in seinem Shader Code aufruft. Am ehesten noch problematisch sind hier Bereichsüberläufe bei Farben/Licht/Schatten ... wenn also z.B. aus einem "gaaaanzzz schwarz und dunkel", plötzlich ein "strahlend weißer Pixel" wird. Was z.B. dann passiert wenn man einen Shader für HDR schreibt, aber im Shader z.B. eine NICHT-Color-Funktionen nutzt um einen Color Wert zu ändern (im Shader kann man z.B. ein Float3-Lerp() auch auf ein Color anwenden, anstelle eines Color-Lerp()).

Worauf ich hinaus will ist, auch bei den Shadern wird vieles automatisch berücksichtigt. Daher vermute ich das bei dem Shader den du da gerade nutzt, die 0 bei Texturskalierungen berücksichtigt wird und es deswegen zu keinen Problemen kommt. Wundere dich also nicht zuuu viel ;)

Trotzdem würde ich dir empfehlen, bei allem immer trotzdem zu versuchen plausible Zahlen einzugeben, auch wenn du keinen Unterschied siehst. Irgendwo ganz anders oder erst im Zusammenhang mit dem aktivieren eines ganz anderen Features könnte es dann eine Rolle spielen. ...z.B. wenn du jetzt alles im Gamma Farbraum machst ... und in einem halben Jahr auf die Idee kommst auf Linear Farbraum zu wechseln (oder Umgekehrt, von Linar auf Gamma -> z.B. weil Gamma für deinen Geschmack/Spiel schöner aussieht, oder das Spiel für Mobiltelefon oder Tablet sein soll).

Tris

Treue Seele

  • »Tris« ist der Autor dieses Themas

Beiträge: 102

Wohnort: ~Stuttgart

  • Private Nachricht senden

10

15.05.2021, 23:58

Wie sieht es denn bei einer Skalierung von 0 aus, verglichen mit einer Skalierung von z. B. 0.001? Eigentlich dürftest du dann gar keine Textur mehr sehen, weil ja alles Vertices dieselben UV-Koordinaten von "unendlich" kriegen.


Ich konnte keinen Unterschied feststellen zwischen 0 und 0.001. Die Textur wird immer angezeigt. Ich bin schon davon ausgegangen, dass der Wert niemals genau 0 wird, auch wenn ich ihn explizit auf 0 setze.
Ich habe es mit nun mit einem integer Wert von 0 getestet. Auch das teilt er anstandslos und zeigt die Textur.



c# hat einige Features um ganz automatisch bestimmte Stolpersteine bei Spezialsituationen, an die man normalerweise garnicht Denkt, selber zu lösen. z.B. kann man Math.Abs(float.NaN) aufrufen, und der Rückgabewert wird nicht zu noch mehr Blödsinn, sondern bleibt float.NaN.
Und auch Bit-Operatoren, berücksichtigen automatisch die CPU-Architektur, so das man z.B. auf einen BigEndian System und einen LittleEndian System, den exakt gleichen Programmcode eintippen kann, und es kommt sozusagen das gleiche Ergebnis raus. So das man da also garnichts beachten muss. Selbst diese Daten dann per Netzwerk an einen Rechner mit anderer CPU-Architektur zu senden, funktioniert problemlos. Lediglich wenn man bewust sehr tief runter geht um z.B. eine eigene Binäre Serialisierung zu programmieren, müsste man da einwenig darauf achten.

Was nun float und 0 angeht... muss man sich meist auch keine Gedanken machen. Erst bei sowas wie Modulo 0 (z.B. float x = 7f % 0f) weiß auch c# nichtmehr wie man das Sinnvoll handhaben kann und wirft eine Exception.
Grundsätzlich kann man bei c# eigentlich immer davon ausgehen, wenn es irgendeine Sinvolle Möglichkeit gibt mit einer besonderen Situation bei einer Operation umzugehen, dann wurde das auch implementiert. Und nur wenn es keine Sinnvolle Möglichkeit gibt, oder mehrere unterschiedliche aber "gleich gute Lösungsergebnisse" bei denen c# nicht Wissen kann was der Programmierer beforzugt, erst dann wird eine Exception geworfen.

Oben drauf kommt da dann nun Unity, was auch nochmal einige kleine versteckte aber schöne Verbesserungen mit sich bringt. z.B. haben alle primitive Datentypen, ganz besonders float, in sich eingebaut eine performance optimierung auf ihre Operatoren bekommen. Zusätzlich dann noch die Mathf welche man anstelle von Math nutzen kann/sollte (Math.Abs() -> Mathf.Abs()) da dort dann auch diese weiteren Funktionen Performanceoptimiert sind.


Übrigens, schon mal Gedanken gemacht über sowas hier?
if(anyFloat == 7.0f)

Normalerweise wäre das gefährlicher Code, da aufgrund der unumgänglichen minimalen Ungenauigkeiten bei Fließkommazahlen, eine exaktes == sehr problematisch ist. Doch in c#/Unity ist der == Operator implementiert mit sozusagen der Logik: "A ist auf +/- 1.401298E-45F ungefähr gleich zu B"
Solche Dinge meine ich mit Stolpersteine bei Spezialsituationen an die man normalerweise garnicht Denkt, die aber automatisch für einen gelöst werden ;)


Was nun deine 0 und Texturskalierung angeht.... da kommt nun auch noch der Shader dazu. Die in Unity mitgelieferten Shader, und ebenso auch bei den Shadern deren Code man in Unity selber schreibt, läuft eine wirklich extreme optimierung drüber wenn sie compiliert werden. Und ja, an den Code der Unity Shader kommt man ran und die nutzen die gleiche Magie/Mittel/Funktione/etc. wie die Shader die man selber schreibt. Und eben bei diesen extremen optimierungen beim compilieren, werden auch wieder einige (leider nicht alle) Stolpersteine berücksichtigt. Hier kommt es sehr darauf an, welche Funktionen man selber in seinem Shader Code aufruft. Am ehesten noch problematisch sind hier Bereichsüberläufe bei Farben/Licht/Schatten ... wenn also z.B. aus einem "gaaaanzzz schwarz und dunkel", plötzlich ein "strahlend weißer Pixel" wird. Was z.B. dann passiert wenn man einen Shader für HDR schreibt, aber im Shader z.B. eine NICHT-Color-Funktionen nutzt um einen Color Wert zu ändern (im Shader kann man z.B. ein Float3-Lerp() auch auf ein Color anwenden, anstelle eines Color-Lerp()).

Worauf ich hinaus will ist, auch bei den Shadern wird vieles automatisch berücksichtigt. Daher vermute ich das bei dem Shader den du da gerade nutzt, die 0 bei Texturskalierungen berücksichtigt wird und es deswegen zu keinen Problemen kommt. Wundere dich also nicht zuuu viel ;)

Trotzdem würde ich dir empfehlen, bei allem immer trotzdem zu versuchen plausible Zahlen einzugeben, auch wenn du keinen Unterschied siehst. Irgendwo ganz anders oder erst im Zusammenhang mit dem aktivieren eines ganz anderen Features könnte es dann eine Rolle spielen. ...z.B. wenn du jetzt alles im Gamma Farbraum machst ... und in einem halben Jahr auf die Idee kommst auf Linear Farbraum zu wechseln (oder Umgekehrt, von Linar auf Gamma -> z.B. weil Gamma für deinen Geschmack/Spiel schöner aussieht, oder das Spiel für Mobiltelefon oder Tablet sein soll).


Das ist ja interessant. Ich habe mich auch schon gewundert, warum Unity seine eigene Math Lib mitbringt. :D

Werbeanzeige