[C++] Snippets
Re: [C++] Snippets
Sieht nicht so aus, dass es mit VS 2010 geht. Für C# ja, für C++ nein. Es wird absolut gar nichts an Kommentaren von IntelliSense angezeigt.
Ohne Input kein Output.
- Krishty
- Establishment
- Beiträge: 8316
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: [C++] Snippets
Ich ergänze ums entfernt verwandteCodingCat hat geschrieben:Zwei verallgemeinernde Schleifenmuster, die mir elegant erscheinen: […]
#define until(...) while(false == (__VA_ARGS__))
für sowas wie
do {
++i;
} until(i == 100);
… damit man auch dort nicht so viele Negationen unterbringen muss.
Re: [C++] Snippets
Netter kleiner Trick mit Kommentaren, an den ich mich spontan erinnert habe und dann ein paar Minuten brauchte um herauszufinden, wie genau das nochmal ging. Die Idee ist einfach, dass entweder Block A oder Block B ausgeführt wird:
Ersetzt man die obere Zeile durch /* wird statt BlockA BlockB auskommentiert. Man braucht also nur 1 Zeichen ändern, um zwischen beiden hin und her zu wechseln. Natürlich haben moderne IDE's für sowas allerhand Komfort-Features, aber das hier funktioniert halt immer. Möchte man das ganze zur für einen Block benutzen, lässt man den Mittelteil einfach weg:
Die Idee dahinter ist natürlich jeweils, dass // in einem Block Kommentar ignoriert wird, und /* sowie */ in Zeilenkommentaren ignoriert werden.
Code: Alles auswählen
//*
BlockA;
/*/
BlockB;
//*/
Code: Alles auswählen
//*
Block;
//*/
Lieber dumm fragen, als dumm bleiben!
https://jonathank.de/games/
https://jonathank.de/games/
- Schrompf
- Moderator
- Beiträge: 5045
- Registriert: 25.02.2009, 23:44
- Benutzertext: Lernt nur selten dazu
- Echter Name: Thomas
- Wohnort: Dresden
- Kontaktdaten:
Re: [C++] Snippets
Hmm... erscheint mir sehr mühsam konstruiert für den gewünschten Zweck. warum kein #ifdef oder ein if( true oder false )?
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
- Krishty
- Establishment
- Beiträge: 8316
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: [C++] Snippets
Auch nur ein Zeichen zu ändern:
#if 0
BlockA;
#else
BlockB;
#endif
Hier bei Microsoft kann man das sogar schachteln; ich weiß aber gerade nicht, ob der Standard das garantiert.
#if 0
BlockA;
#else
BlockB;
#endif
Hier bei Microsoft kann man das sogar schachteln; ich weiß aber gerade nicht, ob der Standard das garantiert.
-
- Establishment
- Beiträge: 426
- Registriert: 23.01.2013, 15:55
Re: [C++] Snippets
Natürlich garantiert das der Standard.
Sonst könnte man ja zum Beispiel keine Präprozessor-Ifs nutzen, wenn man Include-Guards nutzt. Ist aber auch generell sehr üblich.
Sonst könnte man ja zum Beispiel keine Präprozessor-Ifs nutzen, wenn man Include-Guards nutzt. Ist aber auch generell sehr üblich.
Zuletzt geändert von Spiele Programmierer am 19.11.2014, 15:49, insgesamt 1-mal geändert.
- Krishty
- Establishment
- Beiträge: 8316
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: [C++] Snippets
Das stimmt natürlich – jetzt bin ich beruhigt :)
Re: [C++] Snippets
Wenn man mal schnell eingebetteten GLSL Code in seiner Anwendung nutzen möchte und keine Lust hat ständig lästige Anführungszeichen zu pflegen, gibt es eine einfache Lösung:
Vorher:
Nachher:
Wichtig ist jedoch auch hier nach jedem GLSL-Präprozessor Statement einen Umbruch einzuführen, weil auch hier sonst alles in einer Zeile landet und es sonst zu einem Fehler führen würde.
EDIT:
Wichtig hierbei ist zu beachten, Anfang und Ende zu kennzeichnen. Sprich R"Trennzeichensymbol(Zeichenkette)Trennzeichensymbol". Ich vermute das ist nötig um den Compiler nicht zu verwirren, weitere Anführungszeichen innerhalb der Zeichenkette könnten als Ende angesehen werden, und nicht als Teil der Zeichenkette.
Vorher:
Code: Alles auswählen
const char* glslCode =
"#version 420 core\n"
"void main() {"
" gl_Position = vec4(0.0, 0.0, 0.0, 1.0);"
"}";
Code: Alles auswählen
#define TO_STRING(x) #x
const char* glslCode = TO_STRING(
#version 420 core\n
void main() {
gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
}
);
EDIT:
Dank Spiele Programmierer gibt es für VS2013 und C++11 Nutzer eine noch einfachere Lösung mithilfe von Raw String Literals:Spiele Programmierer hat geschrieben:Es gibt eine einfachere Lösung.
C++11 Raw String Literal
Den Umbruch kannst du dir dann auch sparen und die Zeilenangaben stimmen noch.
Code: Alles auswählen
const char* glslCode = R"glsl(
#version 420 core
void main() {
gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
}
)glsl";
Zuletzt geändert von mnemonix am 12.12.2014, 15:18, insgesamt 3-mal geändert.
-
- Establishment
- Beiträge: 426
- Registriert: 23.01.2013, 15:55
Re: [C++] Snippets
Es gibt eine einfachere Lösung.
C++11 Raw String Literal
Den Umbruch kannst du dir dann auch sparen und die Zeilenangaben stimmen noch.
C++11 Raw String Literal
Den Umbruch kannst du dir dann auch sparen und die Zeilenangaben stimmen noch.
Re: [C++] Snippets
Cool, danke. Das ist natürlich eine noch viel bessere Lösung. Ich habe es oben zur Vollständigkeit hinzugefügt. Man lernt immer dazu. ;)
Re: [C++] Snippets
Ja, wirklich sehr ausgezeichnet. Ich hatte bisher immer so ein Webtool benutzt, dass aus mehrzeiligen Text einen C-String bastelt. War dann halt immer sehr nervig, es reinkopieren zu müssen. Ich bin froh, dass das nun ein Ende hat.
Lieber dumm fragen, als dumm bleiben!
https://jonathank.de/games/
https://jonathank.de/games/
- xq
- Establishment
- Beiträge: 1589
- Registriert: 07.10.2012, 14:56
- Alter Benutzername: MasterQ32
- Echter Name: Felix Queißner
- Wohnort: Stuttgart & Region
- Kontaktdaten:
Re: [C++] Snippets
Was imho noch fehlt (vorallem für die ganzen Buffergeschichten):
liefert den Offset eines Members innerhalb eines Structs in "void*"
Code: Alles auswählen
#define OFFSET(type, member) (&(*(type*)nullptr).member)
War mal MasterQ32, findet den Namen aber mittlerweile ziemlich albern…
Programmiert viel in Zig und nervt Leute damit.
Programmiert viel in Zig und nervt Leute damit.
-
- Establishment
- Beiträge: 426
- Registriert: 23.01.2013, 15:55
Re: [C++] Snippets
Auch das gibt es schon: http://en.cppreference.com/w/cpp/types/offsetof
Das selbst zu implementieren basiert wohl streng genommen auf Undefined Behavior.
Das selbst zu implementieren basiert wohl streng genommen auf Undefined Behavior.
- xq
- Establishment
- Beiträge: 1589
- Registriert: 07.10.2012, 14:56
- Alter Benutzername: MasterQ32
- Echter Name: Felix Queißner
- Wohnort: Stuttgart & Region
- Kontaktdaten:
Re: [C++] Snippets
Naja, wenn man die beiden mal vergleicht, wird man feststellen: ist genau das selbe. Aber gut zu wissen, dass es das schon fertig definiert gibt
Und nein, es basiert nicht auf undefined behaviour, sondern auf pointerarithmetik ;)
Und nein, es basiert nicht auf undefined behaviour, sondern auf pointerarithmetik ;)
War mal MasterQ32, findet den Namen aber mittlerweile ziemlich albern…
Programmiert viel in Zig und nervt Leute damit.
Programmiert viel in Zig und nervt Leute damit.
- dot
- Establishment
- Beiträge: 1745
- Registriert: 06.03.2004, 18:10
- Echter Name: Michael Kenzel
- Kontaktdaten:
Re: [C++] Snippets
Das Dereferenzieren eines Nullpointers ist definitiv UB und es ist nicht genau das selbe, denn offsetof() muss auch funktionieren, wenn der operator & irgendwie überladen wurde. Abgesehen davon funktioniert selbst offsetof() nur mit Standard Layout Structs... ;)
Zuletzt geändert von dot am 14.12.2014, 19:55, insgesamt 1-mal geändert.
- Krishty
- Establishment
- Beiträge: 8316
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: [C++] Snippets
Ich erinnere mich auch, dass in Valves Master-Header eine Erweiterung dieses Makros war, um ein Element in der übergeordneten struct zu finden.
struct Game {
struct World {
void draw() {
OUTER_MEMBER(world, Game, device)->BeginScene(); // this plus Differenz aus offsetof(Game::world), offsetof(Game::device) als decltype(Game::device)
…
}
} world;
D3DDevice * device;
};
Tausende Parameterübergaben haben sie erfolgreich gespart. Aber ich weiß bis heute nicht, wie ich dazu stehen soll :P
struct Game {
struct World {
void draw() {
OUTER_MEMBER(world, Game, device)->BeginScene(); // this plus Differenz aus offsetof(Game::world), offsetof(Game::device) als decltype(Game::device)
…
}
} world;
D3DDevice * device;
};
Tausende Parameterübergaben haben sie erfolgreich gespart. Aber ich weiß bis heute nicht, wie ich dazu stehen soll :P
-
- Establishment
- Beiträge: 426
- Registriert: 23.01.2013, 15:55
Re: [C++] Snippets
Eher nicht. Bei einem großzügigen Compiler ist es eine schnelle und hässliche Möglichkeit das zu Implementieren.Naja, wenn man die beiden mal vergleicht, wird man feststellen: ist genau das selbe
Aber das ist nicht vom Standard so definiert. Da steht nämlich "Example Implemenation".
In Clangs Headern ist es zum Beispiel auf "__builtin_offsetof" definiert.
- Krishty
- Establishment
- Beiträge: 8316
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: [C++] Snippets
Ich habe jetzt hier aufgegeben:Krishty hat geschrieben:Wo du es gerade erwähnst: Ich saß letztens an der Clang-Kompatibilität, bin aber nicht fertig geworden :( Für GCC und Clang sähe das Gegenstück so ähnlich wie das aus:
#define TODO_INTERNAL_DEFER(OPERAND, ...) OPERAND(__VA_ARGS__)
#define TODO(TODOTEXT) _Pragma(TODO_INTERNAL_DEFER(warning(TODOTEXT)))
Vielleicht kann, wer auf diesen Systemen entwickelt, was damit anfangen.
#define STRINGIZE(X) #X
#define TODO(TODOTEXT) _Pragma(STRINGIZE(GCC warning "TODO: " #TODOTEXT))
Scheiß String-Gefrickel. Visual C++ macht es richtig.
Re: [C++] Snippets
Hier mal ein Code-Snippet zum Thema Code-Benchmarking nach Intel. Habe ich gerade dazu verwendet um SSE Codespielereien zu benchmarken und es scheint ganz gut und stabil zu funktionieren. ^^ Die RDTSC* Befehle geben hierbei einen Timestamp nach Taktzyklen zurück. Der CPUID Befehl dient zum einen als Pipeline-Flush als auch als Befehlsbarriere bzgl. Reordering (so habe ich es zumindest verstanden). RDTSCP garantiert, dass alle vorherigen Befehle komplett abgearbeitet wurden, bevor der Timestamp gelesen wird. Die pushad und popad Befehle dienen zum Sichern und Wiederherstellen der Register (scheinen auf 64-Bit nicht mehr unterstützt zu werden, weil eine Sicherung hier wohl nicht mehr nötig ist, wegen der Fülle an Registern). Ich habe dazu auch mal versucht Intrinsics zu verwenden, nur fängt dann der Compiler an Instruktionen zu reordern, was für diesen Zweck wohl nicht erwünscht ist. Das Ganze lässt sich vielleicht auch noch erweitern, indem die Thread-Affinitäten und -Prioriäten noch angepasst werden (Stichwort: Context Switch).
Code: Alles auswählen
namespace benchmark
{
union Timestamp
{
std::uint64_t ui64;
struct
{
std::uint32_t ui64l;
std::uint32_t ui64h;
};
};
__forceinline std::uint64_t begin()
{
Timestamp timestamp;
__asm
{
pushad
xor eax, eax
cpuid
rdtsc
mov timestamp.ui64l, eax
mov timestamp.ui64h, edx
popad
}
return timestamp.ui64;
}
__forceinline std::uint64_t end()
{
Timestamp timestamp;
__asm
{
pushad
rdtscp
mov timestamp.ui64l, eax
mov timestamp.ui64h, edx
xor eax, eax
cpuid
popad
}
return timestamp.ui64;
}
}
// Anwendung
...
auto t0 = benchmark::begin();
// Code, der gebenchmarkt werden soll
auto t1 = benchmark::end();
auto dt = t1 - t0;
...
Re: [C++] Snippets
Ist streng genommen kaum ein C++ Snippet, sondern eine Kombination aus verschiedenen Dingen, könnte aber für einige hier ganz nützlich sein: Automatische Generierte Versionsinformationen
Angefangen hat alles damit, dass ich um kleine Dinge auszuprobieren vermehrt Branches in Git erstellt habe. Irgendwann wurde das aber unübersichtlich, wenn man z.B. noch eine fertig kompilierte Datei rumfliegen hatte, aber nicht mehr weiß, aus welchem Branch die jetzt kommt. Also wollte ich eine Dialogbox, die mir Infos über Git und den Kompilierzeitpunkt anzeigt. Das war mir dann sogar so viel wert, dass ich mich freiwillig wieder mit CMake beschäftigt habe. Ich habe mich dafür entschieden, möglichst wenig in CMake tun zu müssen, also wird die Hauptarbeit von einem kleinen Python-Skript erledigt, das alle Informationen sammelt und eine cpp Datei daraus generiert. Es hat ein bisschen gedauert, bis ich die Abhängigkeiten richtig konfiguriert habe (die VersionInfo.cpp soll immer dann neu generiert werden, wenn das Programm neu kompiliert wird, aber bloß nicht immer wenn man auf Ausführen klickt, weil man sonst nie starten kann, ohne neu zu linken), aber jetzt bin ich ganz zufrieden mit meiner Lösung.
Die CMake Datei:
Die VersionInfo.hpp (einfach dort im Programm inkludieren, wo man die Infos verarbeiten möchte)
Das Python Script um die VersionInfo.cpp zu generieren:
Das ganze kann man dann ganz nach belieben anpassen und bei Bedarf weitere Informationen hinzufügen (z.B. auf welchem Gerät das Programm kompiliert wurde oder so).
Angefangen hat alles damit, dass ich um kleine Dinge auszuprobieren vermehrt Branches in Git erstellt habe. Irgendwann wurde das aber unübersichtlich, wenn man z.B. noch eine fertig kompilierte Datei rumfliegen hatte, aber nicht mehr weiß, aus welchem Branch die jetzt kommt. Also wollte ich eine Dialogbox, die mir Infos über Git und den Kompilierzeitpunkt anzeigt. Das war mir dann sogar so viel wert, dass ich mich freiwillig wieder mit CMake beschäftigt habe. Ich habe mich dafür entschieden, möglichst wenig in CMake tun zu müssen, also wird die Hauptarbeit von einem kleinen Python-Skript erledigt, das alle Informationen sammelt und eine cpp Datei daraus generiert. Es hat ein bisschen gedauert, bis ich die Abhängigkeiten richtig konfiguriert habe (die VersionInfo.cpp soll immer dann neu generiert werden, wenn das Programm neu kompiliert wird, aber bloß nicht immer wenn man auf Ausführen klickt, weil man sonst nie starten kann, ohne neu zu linken), aber jetzt bin ich ganz zufrieden mit meiner Lösung.
Die CMake Datei:
Code: Alles auswählen
# hier einfach alles sammeln, was man bei ADD_EXECUTABLE angeben würde:
SET(SOURCE_FILES
${MAIN_FILES}
${MAIN_FILES}
${UI_FILES}
${MISC_FILES}
...
)
ADD_CUSTOM_COMMAND(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/VersionInfo.cpp
COMMAND python ${CMAKE_CURRENT_SOURCE_DIR}/GenerateVersionInfo.py ${CMAKE_CURRENT_BINARY_DIR}/VersionInfo.cpp
DEPENDS ${SOURCE_FILES}
)
ADD_EXECUTABLE(MeinProject ${SOURCE_FILES} ${CMAKE_CURRENT_BINARY_DIR}/VersionInfo.cpp
)
Code: Alles auswählen
#pragma once
#include <string>
namespace VersionInfo
{
extern const std::string CompileTime;
extern const std::string CompileDate;
extern const std::string GitBranch;
extern const std::string GitCommit;
extern const std::string GitCommitDate;
extern const std::string GitCommitAuthor;
}
Code: Alles auswählen
import sys
import datetime
import subprocess
import re
print("Generating ", sys.argv[1])
# Fetch information:
now = datetime.datetime.now()
time_string = now.strftime("%H:%M:%S")
date_string = now.strftime("%A, %d. %m. %Y")
git_branch = subprocess.check_output(["git", "branch", "--all"]).decode("utf-8")
git_commit = subprocess.check_output(["git", "log", "-n 1"]).decode("utf-8")
branch_string = re.search(r"\* *(.*)\n", git_branch).group(1)
commit_string = re.search(r"commit *(.*)\n", git_commit).group(1)
commitDate_string = re.search(r"Date: *(.*)\n", git_commit).group(1)
commitAuthor_string = re.search(r"Author: *(.*)\n", git_commit).group(1)
# Generate an info file:
with open(sys.argv[1], "w") as file:
file.write(
"""#include "VersionInfo.hpp"
namespace VersionInfo
{
""")
file.write(' const std::string CompileTime = "'+time_string+'";\n')
file.write(' const std::string CompileDate = "'+date_string+'";\n')
file.write(' const std::string GitBranch = "'+branch_string+'";\n')
file.write(' const std::string GitCommit = "'+commit_string+'";\n')
file.write(' const std::string GitCommitDate = "'+commitDate_string+'";\n')
file.write(' const std::string GitCommitAuthor = "'+commitAuthor_string+'";\n')
file.write("}\n")
Das ganze kann man dann ganz nach belieben anpassen und bei Bedarf weitere Informationen hinzufügen (z.B. auf welchem Gerät das Programm kompiliert wurde oder so).
Lieber dumm fragen, als dumm bleiben!
https://jonathank.de/games/
https://jonathank.de/games/
- Sternmull
- Establishment
- Beiträge: 264
- Registriert: 27.04.2007, 00:30
- Echter Name: Til
- Wohnort: Dresden
Re: [C++] Snippets
So etwas in der Art nutze ich auch. Die Informationen zum aktuellen Zustand der Working-Copy lassen sich mit "git describe --dirty --always --abbrev=40" etwas einfacher ermitteln. Auf Branch-Namen lege ich dabei keinen großen Wert da die ja in der nächsten Minute schon wieder ganz anders aussehen können. Viel wichtiger ist das man weiß aus welchem Commit etwas gebaut wurde (egal ob der sich zukünftig in einem Branch befindet von dem man noch nichts wissen kann), und ob es lokale Änderungen gab die nicht in dem Commit enthalten sind (deshalb --dirty).
Für das Zusammenbauen des Strings ist übrigens String-Formatierung äußerst komfortabel. Um Strings mit vielen Ersetzungen zu erzeugen mache ich oft so was:
Das **locals() übergibt alle lokalen Variablen als Schlüsselwort-Argumente an die Formatierung. So stehen einem im Format-String alle Variablen zur Verfügung. Innerhalb einer sicherheitskritischen Web-Anwendung sollte man damit zwar vorsichtig sein, aber für so was wie deine Anwendung finde ich es sehr nützlich.
Für das Zusammenbauen des Strings ist übrigens String-Formatierung äußerst komfortabel. Um Strings mit vielen Ersetzungen zu erzeugen mache ich oft so was:
Code: Alles auswählen
x = 123
y = 'hallo'
s = '''
int foo()
{{
x = {x}; y = "{y}";
}}
'''.format(**locals())
Re: [C++] Snippets
Ah, danke für die Tipps. Hat sich doch schon wieder gelohnt, das hier zu posten :)
Lieber dumm fragen, als dumm bleiben!
https://jonathank.de/games/
https://jonathank.de/games/
- Krishty
- Establishment
- Beiträge: 8316
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: [C++] Snippets
Gleichverteilte zufällige floats:
float rand_snorm_float(uint32 const r) {
return float(int(r) / 256) / 8388608.0f;
}
Hintergrund:
float rand_snorm_float(uint32 const r) {
return float(int(r) / 256) / 8388608.0f;
}
Hintergrund:
- erzeugt Zahlen im Bereich [-1, +0.999999881]
- float hat 23-Bit-Mantissen und kann damit alle Ganzzahlen im Bereich [-8.388.608, +8.388.608] ohne Verlust darstellen
- solche Ganzzahlen erzeugen wir durch arithmetischen Shift einer 32-Bit-Zufallszahl (acht statt neun Bits weil wir das höchstwertige Bit als Vorzeichen verwenden wollen)
- ganz nebenbei ist int -> float auf x86 schneller als unsigned int -> float
- der Shift ist einem Modulo vorzuziehen, weil die meisten Zufallszahlengeneratoren geringe Entropie in den niederwertigen Bits haben und wir lieber die höchstwertigen behalten wollen
- die Division durch 8.388.608 wird zur Multiplikation mit dem Reziprokwert optimiert, weil es eine glatte Zweierpotenz ist
- lässt sich perfekt via SIMD parallelisieren
- bloß nicht diese Methode verwenden – bei ihr sind durch die finale Subtraktion die unteren Bits immer Null!
- die xoroshiro-Methode erzeugt leider keine vorzeichenbehafteten Zahlen, das ist für 3D-Grafik schwach
- bloß nicht das Vorzeichen nachträglich draufkleistern – weil IEEE754 zwischen +0 und -0 unterscheidet, kommen Nullen bei euch am Ende doppelt so häufig vor wie andere Werte; dann lieber die -1 in meinem Code explizit rausschmeißen (damit der Bereich [-0.999999881, +0.999999881] wird)
- Krishty
- Establishment
- Beiträge: 8316
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: [C++] Snippets
NaN-korrektes Clamping mit SSE:
NaN Poisoning Attacks gegen anderer Leute Code sind immer wieder lustig. Die meisten Leute machen ihr clamp() falsch – und sind überrascht, dass clamp(0.0f, x, 1.0f) nicht nur Zahlen zwischen 0 und 1 liefert.
Dabei gibt es auf x86 eine Besonderheit, die das ganze enorm vereinfacht: MINPS und MAXPS mit einem NaN-Parameter geben nämlich immer den zweiten Parameter zurück. Zum Verständnis:
_mm_min_ps(1.0f, 2.0f) → 1.0f
_mm_min_ps(1.0f, NaN) → NaN
_mm_min_ps( NaN, 1.0f) → 1.0f
_mm_min_ps( NaN, NaN) → NaN
MAXPS funktioniert genauso. Durch die Reihenfolge der Operanden können wir das Ganze also so formulieren, dass clamp() niemals NaN durchreicht:
__m128 clampNum(__m128 min, __m128 x, __m128 max) {
return _mm_min_ps(_mm_max_ps(x, max), min);
}
… und schon sind keine NaNs mehr möglich. Ein Angriffsvektor weniger, bzw. ein Dutzend Befehle weniger, falls ihr bisher explizit prüfen musstet.
Der Name ist übrigens nicht zufällig gewählt: IEEE754-2008 schreibt vor, dass NaN-sichere min()- und max()-Versionen minNum() und maxNum() heißen. Leider ist x86 nicht kompatibel.
NaN Poisoning Attacks gegen anderer Leute Code sind immer wieder lustig. Die meisten Leute machen ihr clamp() falsch – und sind überrascht, dass clamp(0.0f, x, 1.0f) nicht nur Zahlen zwischen 0 und 1 liefert.
Dabei gibt es auf x86 eine Besonderheit, die das ganze enorm vereinfacht: MINPS und MAXPS mit einem NaN-Parameter geben nämlich immer den zweiten Parameter zurück. Zum Verständnis:
_mm_min_ps(1.0f, 2.0f) → 1.0f
_mm_min_ps(1.0f, NaN) → NaN
_mm_min_ps( NaN, 1.0f) → 1.0f
_mm_min_ps( NaN, NaN) → NaN
MAXPS funktioniert genauso. Durch die Reihenfolge der Operanden können wir das Ganze also so formulieren, dass clamp() niemals NaN durchreicht:
__m128 clampNum(__m128 min, __m128 x, __m128 max) {
return _mm_min_ps(_mm_max_ps(x, max), min);
}
… und schon sind keine NaNs mehr möglich. Ein Angriffsvektor weniger, bzw. ein Dutzend Befehle weniger, falls ihr bisher explizit prüfen musstet.
Der Name ist übrigens nicht zufällig gewählt: IEEE754-2008 schreibt vor, dass NaN-sichere min()- und max()-Versionen minNum() und maxNum() heißen. Leider ist x86 nicht kompatibel.