Float-Zahlen



  • Hallo,

    kann mir einer folgendes Verhalten erklären:

    #include <stdio.h>
    
    int main()
    {
      float even = 0;
      for( int i=0 ; i<2048 ; i+=2 )
      {
        even += i * i;
      }
    
      float odd = 0;
      for( int i=1 ; i<2048 ; i+=2 )
      {
        odd += i * i;
      }
    
      float gold = 0;
      for( int i=1 ; i<2048 ; i+=1 )
      {
        gold += i * i;
      }
    
      printf("sum=%f, gold=%f\n", even+odd, gold);
    
      return 0;
    }
    
    // Ausgabe: sum=2861214720.000000, gold=2861216768.000000
    

    Entgegen der Intuition ist sum!=gold.
    Erhöht man jetzt jedoch die Genauigkeit für "gold" (float -> double), dann erhält man wie erwartet sum==gold:

    #include <stdio.h>
    
    int main()
    {
      float even = 0;
      for( int i=0 ; i<2048 ; i+=2 )
      {
        even += i * i;
      }
    
      float odd = 0;
      for( int i=1 ; i<2048 ; i+=2 )
      {
        odd += i * i;
      }
    
      double gold = 0;
      for( int i=1 ; i<2048 ; i+=1 )
      {
        gold += i * i;
      }
    
      printf("sum=%f, gold=%f\n", even+odd, gold);
    
      return 0;
    }
    
    // Ausgabe: sum=2861214720.000000, gold=2861214720.000000
    

    Das scheint mit der nicht-kommutativität von Float-Zahlen zusammenzuhängen. Was mich jedoch wundert: warum ist eine Berechnung genauer, wenn man sie in Teilsummen aufteilt und erst final zusammenfügt?

    Gruß



  • Ein IEEE 754 float hat nur 32 Bit, ein Bit ist Vorzeichen, ein paar weitere sind für das Bias, und der größte Teil sind für die Matrize. 2.861.216.768 ist schon zu viel, als du mit einem 32-Bit int darstellen könntest (signed, weil floats halt auch ein Vorzeichen-Bit haben). Darauf dann noch eine Menge Additionsoperationen anzuwenden ist ... doof. 🙂

    In der gold-Schleife hättest du auch:

    if(i > 2042)
            printf("%f | %d | %f\n",gold,i * i,gold + i * i);
    

    Einfügen können, um zu sehen, wo die Rundungsfehler kommen:

    2840306688.000000 | 4173849 | 2844480512.000000
    2844480512.000000 | 4177936 | 2848658432.000000
    2848658432.000000 | 4182025 | 2852840448.000000
    2852840448.000000 | 4186116 | 2857026560.000000
    2857026560.000000 | 4190209 | 2861216768.000000
    

    Jetzt nimm im Vergleich 1.429.557.760 und 1.431.656.960 - die passen bequem in ein 32-Bit int. Und die Addition ist hier noch nicht so kritisch. Ergibt daher schon Sinn.

    EDIT: Vermaledeite Einrückungen ...



  • Mit Anzahl bits hat das überhaupt nix zu tun, hier passt alles sicherlich in 32 bits:

    #include <stdio.h>
    
    int main()
    {
       float sum = 0;
       float summand = 0.1;
    
       int i;
    
       for(i = 0; i < 10; ++i)
          sum += summand;
    
       if(sum == 1)
          printf("1 und %f sind gleich", sum);
       else
          printf("1 und %f sind ungleich", sum);
    
    }
    

  • Mod

    Belli schrieb:

    Mit Anzahl bits hat das überhaupt nix zu tun,

    Doch!

    hier passt alles sicherlich in 32 bits:

    Was hat dein Beispiel mit dem Problem des TE zu tun? Du demonstrierst eine völlig andere Eigenschaft von Fließkommazahlen.



  • dachschaden schrieb:

    der größte Teil sind für die Matrize

    Mantisse heißt das.



  • SeppJ schrieb:

    Was hat dein Beispiel mit dem Problem des TE zu tun? Du demonstrierst eine völlig andere Eigenschaft von Fließkommazahlen.

    Siehe:

    Gast43321 schrieb:

    ...
    Entgegen der Intuition ist sum!=gold.

    So ist es auch in meinem Beispiel:
    Entgegen der Intuition ist 1 != sum


  • Mod

    Belli schrieb:

    So ist es auch in meinem Beispiel:
    Entgegen der Intuition ist 1 != sum

    Ja. Aber aus völlig unterschiedlichen Gründen!


Anmelden zum Antworten