Hi, hat von euch schon mal für testcodes eine funktion geschrieben die über alle normalen (also nicht denormalen und auch keine spezialwerte) float-werte iteriert? das ganze soll platformunabhängig sein, da es unter verschiedenen Platformen und compilern zum Einsatz kommen soll, um math-funktionen zu validieren.
Float sind doch auch nur 4byte. Also nimmt man sich ein uint32_t und geht von 0 bis 2^32-1 durch. Dann einfach ein harten cast auf float (memcpy) und schon hat man alle float werte.
Kannst ja bei 0 anfangen und dann mit nextafter (http://www.cplusplus.com/reference/cmath/nextafter/) in beide Richtungen gehen bis es nicht mehr weiter geht. Hab ich noch nicht gemacht, sollte aber funktionieren. Eventuell muss man die Subnormals noch rauswerfen, kann man aber mit isnormal (http://www.cplusplus.com/reference/cmath/isnormal/) testen.
Hallo Vlad, Wenn Du eine Möglichkeit findest, heraus zu bekommen, ob ein Bitmuster einen gültigen Wert repräsentiert, dann könntest Du über alle Bitmuster iterieren und die auslassen, die keinen gültigen Wert repräsentieren. Aber ich kann mir nicht so richtig vorstellen, dass das was Du da vorhast sinnvoll ist. Selbst wenn ein Float "nur" 2^^32 mögliche Werte annehmen kann, dann kannst Du das Verfahren spätestens bei einer Funktion mit 2 Parametern nicht mehr anwenden (es sei den, Du hast sehr viel Zeit). Und wie möchtest Du den vom Eingangsvektor darauf schließen, dass der Ausgangsvektor korrekt ist? mfg Torsten
Torsten R. schrieb: > Wenn Du eine Möglichkeit findest, heraus zu bekommen, ob ein Bitmuster > einen gültigen Wert repräsentiert Das sollte kein Problem sein:
1 | bool isnan(float a) |
2 | {
|
3 | return (a != a); |
4 | }
|
Und eigentlich sollte eine Funktion auch bei ungültigen Floats funktionieren.
Walter T. schrieb: > Das sollte kein Problem sein: > bool isnan(float a) > { > return (a != a); > } das ist gewagt. ist nun eine -0 != 0 ?
Peter II schrieb: > ist nun eine -0 != 0 ? Gute Frage.... zwei unterschiedliche floats würde ich eh nie vergleichen. Dann schon eher: abs(-0 - 0)< epsilon und das ist auf jeden Fall wahr. Aber hier träfe ja auch eher zu: return (-0 != -0); und das Resultat wäre false.
:
Bearbeitet durch User
Walter T. schrieb: > Gute Frage.... zwei unterschiedliche würde ich eh nie vergleichen. ok, aber der Compiler könnte auch auf die Idee kommen das es immer false sein muss.
Peter II schrieb: > ok, aber der Compiler könnte auch auf die Idee kommen das es immer false > sein muss. Nein, denn im IEEE 754 ist definiert, daß der Vergleich zweier NaNs , also auch der Vergleich mit sich selbst, false liefern muß. Deswegen eignet sich der Vergleich auch wunderbar dazu, zu prüfen, ob eine Variable NaN ist - vorausgesetzt, die Funktion "isnan" ist nicht sowieso schom implementiert.
Vlad T. schrieb: > das ganze soll platformunabhängig sein, Na ja, du solltest schon vorher sicherstellen, daß alle deine Plattformen 32 bit floats verwenden. Selbst, wenn deine Implementierung mit 8- oder 10-byte-Implementierungen (oder noch breiteren) klar käme, werden die Tests dann wohl praktisch unbrauchbar. Ein Test muß ja auch mal fertig werden ;) Oliver
Vlad T. schrieb: > hat von euch schon mal für testcodes eine funktion geschrieben die über > alle normalen (also nicht denormalen und auch keine spezialwerte) > float-werte iteriert? Das maht normalerweise überhaupt kein Sinn. Man testet ein oder ein paar "normale" Fälle und die Randbedingungen: minimum, maximum, 0, 1, 42 und evt noch Werte gerade ausserhalb den gültigen Bereich.
:
Bearbeitet durch User
>Das maht normalerweise überhaupt kein Sinn.
Trifft sich gut. Ich schreibe nur sinnlosen Code.
1 | #include <math.h> |
2 | #include <stdio.h> |
3 | |
4 | long isfloat = 0; |
5 | long isnotfloat = 0; |
6 | |
7 | union { |
8 | float f; |
9 | unsigned char c [sizeof(float)]; |
10 | } t; |
11 | |
12 | void doit() { |
13 | isfloat++; |
14 | } |
15 | |
16 | void loop(int pos) { |
17 | if (pos < 0) { |
18 | if (isnan(t.f)) |
19 | isnotfloat++; |
20 | else |
21 | doit(); |
22 | } else { |
23 | t.c[pos] = 0; |
24 | while(1) { |
25 | loop(pos -1); |
26 | if(++t.c[pos] == 0) |
27 | return; |
28 | } |
29 | } |
30 | } |
31 | |
32 | void main() { |
33 | loop(sizeof(float) - 1); |
34 | printf("float %ld nan %ld\n", isfloat, isnotfloat); |
35 | } |
Walter T. schrieb: >> Wenn Du eine Möglichkeit findest, heraus zu bekommen, ob ein Bitmuster >> einen gültigen Wert repräsentiert > > Das sollte kein Problem sein: > bool isnan(float a) > { > return (a != a); > } > Verlangt war aber: Vlad T. schrieb: > die über > alle normalen (also nicht denormalen und auch keine spezialwerte) > float-werte iteriert? NaN ist nicht der einzige Spezialwert. Es gibt noch Dinge wie Infinity und andere nettigkeiten. http://en.cppreference.com/w/cpp/numeric/math/INFINITY
Daniel A. schrieb: > NaN ist nicht der einzige Spezialwert. Es gibt auch nicht nur ein NaN, und INFINITY kann positiv oder negativ sein. https://www.doc.ic.ac.uk/~eedwards/compsys/float/nan.html
> NaN ist nicht der einzige Spezialwert.
Alle Bitmuster durchprobiert kommt der Gnu-C unter Linux auf folgende
Summen
1 | if(isnan()) 16777214 |
2 | else 4278190082 |
3 | if(a != a) 16777214 |
fpclassify() teilt die Bitmuster in 5 Gruppen auf.
1 | FP_NAN 16777214 |
2 | FP_INFINITE 2 |
3 | FP_ZERO 2 |
4 | FP_SUBNORMAL 16777214 |
5 | FP_NORMAL 4261412864 |
(a != a) lässt also 16777218 Bitmuster durch, die fpclassify() nicht unter FP_NORMAL einsortiert.
Eric B. schrieb: > Vlad T. schrieb: >> hat von euch schon mal für testcodes eine funktion geschrieben die über >> alle normalen (also nicht denormalen und auch keine spezialwerte) >> float-werte iteriert? > > Das maht normalerweise überhaupt kein Sinn. Man testet ein oder ein paar > "normale" Fälle und die Randbedingungen: minimum, maximum, 0, 1, 42 und > evt noch Werte gerade ausserhalb den gültigen Bereich. Kommt drauf an was man macht. Dauert vielleicht 5 Minuten, aber wenn du gegen Werte einer bewaehrten Implementierung testen kannst, kannst du dir sicher sein richtige Werte auszugeben. Dann sollte man allerdings nicht Spezialfaelle aussen vor lassen. Funktioniert aber nur fuer Single-Precision floats. Von Schnippseln die versuchen int-pointer zu float-pointern zu casten um die Daten zu beschreiben sollte man auch moeglichst Abstand halten. Das verstoesst gegen die Aliasing-Regeln. Zum Erzeugen sollte man den Umweg ueber eine Union gehen, das Funktioniert dann auch. Hier ne Leseempfehlung, die genau das behandelt: https://randomascii.wordpress.com/2014/01/27/theres-only-four-billion-floatsso-test-them-all/
Ano schrieb: > Von Schnippseln die versuchen int-pointer zu float-pointern zu casten um > die Daten zu beschreiben sollte man auch moeglichst Abstand halten. Das > verstoesst gegen die Aliasing-Regeln. Zum Erzeugen sollte man den Umweg > ueber eine Union gehen, das Funktioniert dann auch. nein Union ist der fasche weg. memcpy nimmt man für den "cast".
Peter II schrieb: > nein Union ist der fasche weg. memcpy nimmt man für den "cast". Prinzipiell hast du recht, aber die meisten Compiler garantieren wohl aber die Korrektheit dieses Verstoßes. Ano schrieb: > Kommt drauf an was man macht. Dauert vielleicht 5 Minuten, aber wenn du > gegen Werte einer bewaehrten Implementierung testen kannst, kannst du > dir sicher sein richtige Werte auszugeben. genau Ano schrieb: > Hier ne Leseempfehlung, die genau das behandelt: > https://randomascii.wordpress.com/2014/01/27/theres-only-four-billion-floatsso-test-them-all/ Danke für den Link. Auch ein esehr nette Artikelreihe: https://randomascii.wordpress.com/2012/05/20/thats-not-normalthe-performance-of-odd-floats/ Noch einer schrieb: > Alle Bitmuster durchprobiert > [...] > fpclassify() Glaub das ist der einfachste Weg. braucht es nur noch eine brauchbare implementierung von fpclassify für nicht-C99 kompatible Compiler ;(.
Was labert ihr eigentlich von memcpy für den Cast?? man muss Speicher doch nicht umherkopieren, damit man ihn anders interpetiert. float f = Lalalal; unsigned int ui = *((unsigned int*)&f). Kannst ja mal den Geschwindigkeitsunterschied testen. "memcpy der richtige weg". Google mal nach Mount Stupid. Dunning Kruger lässt grüßen. Die nichtwissenden schreien am lautesten.
... schrieb: > Was labert ihr eigentlich von memcpy für den Cast?? > man muss Speicher doch nicht umherkopieren, damit man ihn anders > interpetiert. > > float f = Lalalal; > unsigned int ui = *((unsigned int*)&f). dumm nur wenn der float auf einigen Plattformen anders im Speicher ausgerichtet sein muss.
... schrieb: > Kannst ja mal den Geschwindigkeitsunterschied testen. > "memcpy der richtige weg". Google mal nach Mount Stupid. Dunning Kruger > lässt grüßen. Die nichtwissenden schreien am lautesten. dann sei mal schön leise http://zaynar.co.uk/docs/float-aliasing.html
Peter II schrieb: > nein Union ist der fasche weg. memcpy nimmt man für den "cast". Naja, das ist ein bisschen sehr allgemein formuliert. Man kann durchaus unions benutzen. Es kommt auf den konkreten Fall an (C/++ Version, verwendet Typen). Für den o.g. Fall sollte die union imho immer funktionieren. >> float f = Lalalal; >> unsigned int ui = *((unsigned int*)&f). > > dumm nur wenn der float auf einigen Plattformen anders im Speicher > ausgerichtet sein muss. Anders ausgerichtet? Was meinst du damit? Hier gibt es möglicherweise ein Problem bei der Optimierung, wenn "Aliasing" aktiv ist. > dann sei mal schön leise > http://zaynar.co.uk/docs/float-aliasing.html Genau, da geht es um Aliasing (das Optimierung kann man auch abschalten). Und die union Lösung funktioniert sogar. BTW, hier ein Link mit einer guten Beschreibung zum Aliasing: http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html
S. J. schrieb: >> nein Union ist der fasche weg. memcpy nimmt man für den "cast". > > Naja, das ist ein bisschen sehr allgemein formuliert. Man kann durchaus > unions benutzen. Es kommt auf den konkreten Fall an (C/++ Version, > verwendet Typen). Für den o.g. Fall sollte die union imho immer > funktionieren. Bis jetzt steht nichts von verwendeten Compiler und System da, wie kannst da jetzt entscheiden das es damit geht? > Anders ausgerichtet? Was meinst du damit? es gibt System, da muss ein float ein gewissen Speicheradresse beginnen sonst kann es die FPU nicht verarbeiten.
Vlad T. schrieb: > Hi, > hat von euch schon mal für testcodes eine funktion geschrieben die über > alle normalen (also nicht denormalen und auch keine spezialwerte) > float-werte iteriert? > > das ganze soll platformunabhängig sein, da es unter verschiedenen > Platformen und compilern zum Einsatz kommen soll, um math-funktionen zu > validieren. Naja. Ob man unbedingt eine hundertprozentige Testabdeckung braucht, darüber kann man streiten. Spätestens bei mehreren Variablen die man durchiterieren muss ist Schluss mit lustig.
Peter II schrieb: > S. J. schrieb: >>> nein Union ist der fasche weg. memcpy nimmt man für den "cast". >> >> Naja, das ist ein bisschen sehr allgemein formuliert. Man kann durchaus >> unions benutzen. Es kommt auf den konkreten Fall an (C/++ Version, >> verwendet Typen). Für den o.g. Fall sollte die union imho immer >> funktionieren. > > Bis jetzt steht nichts von verwendeten Compiler und System da, wie > kannst da jetzt entscheiden das es damit geht? Interessant. Da muss ich doch mal die Retourkutsche probieren: Wie kannst du da jetzt entscheiden" dass die union der falsche weg ist? ;-) Zu meiner "Entscheidung", einfach mal den verlinkten Artikel lesen. >> Anders ausgerichtet? Was meinst du damit? > es gibt System, da muss ein float ein gewissen Speicheradresse beginnen > sonst kann es die FPU nicht verarbeiten. Hilft mir immer noch nicht weiter. > float f = Lalalal; > unsigned int ui = *((unsigned int*)&f). Wie kommt der float hier gleich auf die "falsche" Speicheradresse? ;-)
:
Bearbeitet durch User
S. J. schrieb: > Wie > kannst du da jetzt entscheiden" dass die union der falsche weg ist? ;-) weil ich nicht garantieren kann, das es überall funktioniert: > das ganze soll platformunabhängig sein, da es unter verschiedenen > Platformen und compilern zum Einsatz kommen soll
S. J. schrieb: > Hilft mir immer noch nicht weiter. > >> float f = Lalalal; >> unsigned int ui = *((unsigned int*)&f). > > Wie kommt der float hier gleich auf die "falsche" Speicheradresse? ;-) Die Aliasing rule in C und C++ besagt, dass Pointer mit unterschiedlichen Datentypen nicht gleichzeitig auf den selben Speicher zeigen dürfen (ausnahmen sind das Generieren von char oder void pointern auf andere Datentypen, aber im fall von char nicht umgekehrt). Dies gillt auch für unions. Das heisst, da der Kompiler ja weiss, das (unsigned int*)&f nicht auf f zeigt, dass dies irgendwohin zeigt. Er darf und wird das Wegoptimieren, ins nirvana zeigen Lassen, oder sogar mit dem Pointer auf den Entwickler schiessen, das ist nunmal nicht definiert. Das du von Pointern getroffen wurdest merkst du daran, das du Kopfschmerzen bekommst und dann Stundenlang Phantome jagst, bis du den Cast findest ;-)
Daniel A. schrieb: > Die Aliasing rule in C und C++ besagt, dass Pointer mit > unterschiedlichen Datentypen nicht gleichzeitig auf den selben Speicher > zeigen dürfen (ausnahmen sind das Generieren von char oder void pointern > auf andere Datentypen, aber im fall von char nicht umgekehrt). > > Dies gillt auch für unions. Für unions ist es eigentlich eine andere Regel. Diese besagt, daß man aus einer union immer nur das Element lesen darf, das als letztes geschrieben wurde. Der Hintergrund mag aber ein ähnlicher wie bei der Aliasing-Regel sein.
Das Casten wurde im Quakequelltext offensichtlich funktionsfähig verwendet. Mein Lieblingsprogrammbeispiel, die fast inverse square root: https://en.wikipedia.org/wiki/Fast_inverse_square_root
Nun ja, geschätzte 99% aller Programme dieser Welt tun so ungefähr das, was von ihnen erwartet wird, obwohl jede Menge Fehler drin stecken... Das Thema cast per Union oder solche pointer cast sind halt im C-Standard als undefiniert definiert. "Undefiniert" schließt auch die Variante ein, daß es trotzdem funktioniert. Nur darf man sich halt nicht drauf verlassen. Mir der nächsten Compiler-Generation mag es anders aussehen. Das gilt auch und gerade für solche Bitfrickler-Algorithmen aus der Computersteinzeit. Oliver
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.