2016-10-04 4 views
6

Ist der Zusatz x + x austauschbar durch die Multiplikation 2 * x in 754 IEEE (IEC 559)-Gleitkommastandard, oder allgemeiner gesagt gibt es keine Garantie, dass case_add und case_mulimmer geben genau das gleiche Ergebnis?Austauschbarkeit der IEEE 754 Gleitkomma-Addition und Multiplikation

#include <limits> 

template <typename T> 
T case_add(T x, size_t n) 
{ 
    static_assert(std::numeric_limits<T>::is_iec559, "invalid type"); 

    T result(x); 

    for (size_t i = 1; i < n; ++i) 
    { 
     result += x; 
    } 

    return result; 
} 

template <typename T> 
T case_mul(T x, size_t n) 
{ 
    static_assert(std::numeric_limits<T>::is_iec559, "invalid type"); 

    return x * static_cast<T>(n); 
} 
+0

Hinweis ist, dass es scheinen viele Möglichkeiten, um es zusammenzufassen n * x, aber überraschend so viele sind gleichwertig! Das hängt irgendwie mit http://stackoverflow.com/questions/21690585/is-3xx-always-exact und http://stackoverflow.com/questions/21676955/floating-point-product-expansion-equivalence zusammen –

Antwort

9

ist der Zusatz x + x austauschbar durch die Multiplikation 2 * x in IEEE 754 (IEC 559)-Gleitkommastandard

Ja Da beide mathematisch identisch sind, ergeben sie das gleiche Ergebnis (da das Ergebnis im Fließkomma genau ist).

oder allgemeiner gesagt gibt es irgendeine Garantie, dass case_add und case_mul immer genau das gleiche Ergebnis liefern?

Nicht allgemein, nein. Von dem, was ich sagen kann, scheint es für n <= 5 zu halten:

  • n=3: als x+x genau ist (das heißt beinhaltet keine Rundung), so (x+x)+x nur eine im letzten Schritt Runden beinhaltet.
  • n=4 (und Sie verwenden den Standardrundungsmodus), dann

    • wenn das letzte Bit x 0 ist, dann x+x+x ist genau, und so sind die Ergebnisse gleich nach dem gleichen Argumente wie n=3.
    • Wenn die letzten 2 Bits 01 sind, dann hat der genaue Wert von x+x+x die letzten 2 Bits von 1|1 (wobei | das letzte Bit im Format angibt), das auf 0|0 aufgerundet wird. Die nächste Addition ergibt ein exaktes Ergebnis |01, so dass das Ergebnis abgerundet wird, wodurch der vorherige Fehler aufgehoben wird.
    • Wenn die letzten 2 Bits 11 sind, dann hat der genaue Wert von x+x+x die letzten 2 Bits von 0|1, die auf 0|0 abgerundet werden. Die nächste Addition ergibt ein exaktes Ergebnis |11, so dass das Ergebnis aufgerundet wird, wodurch der vorherige Fehler wieder aufgehoben wird.
  • n=5 (wieder unter der Annahme, Standardrundungs): seit x+x+x+x genau ist, ist es aus dem gleichen Grund wie n=3 hält.

Für n=6 schlägt es fehl, z.B. nehmen x1.0000000000000002 (die nächste double nach 1.0) zu sein, wobei in diesem Fall 6x ist 6.000000000000002 und x+x+x+x+x+x6.000000000000001

+0

Wow Ihr Beweis ist viel kürzer als meine Beweis-nach-Fall-Analyse, ob 3 * x in der gleichen binade wie 2 * x oder 4 * x ist. –

+1

Schöne detaillierte Antwort mit dem Nachweis der Richtigkeit. – plasmacel

+1

Von dem, was ich aufgrund erschöpfender Tests mit IEEE-754 'binary32' (=' float') sagen kann, ist die Multiplikation mit * n * identisch mit * (n-1) * wiederholten Additionen wenn * n * ≤ 5 für 'FE_TONEAREST ', * n * ≤ 3 für" FE_TOWARDZERO ", * n * ≤ 3 für" FE_DOWNWARD "und * n * ≤ 3 für" FE_UPWARD ". Ist der Nachweis auf gerichtete Rundungsmodi erweiterbar? – njuffa

3

Wenn n ist zum Beispiel pow(2, 54) dann wird die Multiplikation gut funktionieren, aber in dem Additionspfad, sobald das Ergebniswert ausreichend größer ist als der Eingang x wird result += xresult ergeben.

1

Ja, aber es gilt nicht allgemein. Eine Multiplikation mit einer Zahl größer als 2 führt möglicherweise nicht zu den gleichen Ergebnissen, da Sie den Exponenten geändert haben und ein wenig fallen können, wenn Sie durch Additionen ersetzen. Die Multiplikation mit Zwei kann jedoch kein Bit mehr löschen, wenn sie durch Additionsoperationen ersetzt wird.

1

Wenn der Akkumulator result in case_add zu groß wird, führt das Hinzufügen von x zu Rundungsfehlern. Zu einem bestimmten Zeitpunkt wird das Hinzufügen von x überhaupt keinen Effekt haben. Die Funktionen ergeben also nicht das gleiche Ergebnis.

Zum Beispiel, wenn double x = 0x1.0000000000001p0 (hexadezimal float-Notation):

n case_add    case_mul 

1 0x1.0000000000001p+0 0x1.0000000000001p+0 
2 0x1.0000000000001p+1 0x1.0000000000001p+1 
3 0x1.8000000000002p+1 0x1.8000000000002p+1 
4 0x1.0000000000001p+2 0x1.0000000000001p+2 
5 0x1.4000000000001p+2 0x1.4000000000001p+2 
6 0x1.8000000000001p+2 0x1.8000000000002p+2 
Verwandte Themen