Forum: PC-Programmierung Platformunabhängiges iterieren über alle floats


von Vlad T. (vlad_tepesch)


Lesenswert?

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.

von Peter II (Gast)


Lesenswert?

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.

von Sebastian V. (sebi_s)


Lesenswert?

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.

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

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

von Walter T. (nicolas)


Lesenswert?

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.

von Peter II (Gast)


Lesenswert?

Walter T. schrieb:
> Das sollte kein Problem sein:
> bool isnan(float a)
> {
>     return (a != a);
> }

das ist gewagt.

ist nun eine -0 != 0 ?

von Walter T. (nicolas)


Lesenswert?

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
von Peter II (Gast)


Lesenswert?

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.

von Walter T. (nicolas)


Lesenswert?

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.

von Oliver S. (oliverso)


Lesenswert?

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

von Eric B. (beric)


Lesenswert?

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
von Noch einer (Gast)


Lesenswert?

>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
}

von Daniel A. (daniel-a)


Lesenswert?

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

von Rolf M. (rmagnus)


Lesenswert?

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

von Noch einer (Gast)


Lesenswert?

> 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.

von Ano (Gast)


Lesenswert?

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/

von Peter II (Gast)


Lesenswert?

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".

von Vlad T. (vlad_tepesch)


Lesenswert?

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 ;(.

von ... (Gast)


Lesenswert?

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.

von Peter II (Gast)


Lesenswert?

... 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.

von Peter II (Gast)


Lesenswert?

... 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

von Mikro 7. (mikro77)


Lesenswert?

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

von Peter II (Gast)


Lesenswert?

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.

von Mark B. (markbrandis)


Lesenswert?

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.

von Mikro 7. (mikro77)


Lesenswert?

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
von Peter II (Gast)


Lesenswert?

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

von Daniel A. (daniel-a)


Lesenswert?

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 ;-)

von Rolf M. (rmagnus)


Lesenswert?

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.

von Dussel (Gast)


Lesenswert?

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

von Oliver S. (oliverso)


Lesenswert?

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
Noch kein Account? Hier anmelden.