0.2 + 0.1 = 

0.30000000000000004

Roland Brand

PHP / Symfony User Group Basel

8. März 2018

Über den Umgang mit Zahlen

(speziell: Fliesskommazahlen)

Inhalt

  • Einführung in die Problematik
     
  • konkrete Beispiele
     
  • Lösungsstrategien

...

Wundervolle Zeiten für Entwickler...

High Level

Abstraktionen

High Level

String Manipulation

High Level

Mathe

Bsp 1: Quadratwurzel

Was ist die Hauptaufgabe eines Computers?

0.2 + 0.1 ≠ 0.3

???

 

Können Computer nicht richtig rechnen?

  • doch, natürlich
  • Computer rechnen mit
    • Prozessorarithmetik (z.B. 64bit)
    • Langzahlarithmetik (arbitrary precision)
    • symbolischer Mathematik

Die Elementaroperationen (+, -, *, /)

verwenden normalerweise Prozessorarithmetik

Prozessorarithmetik

  • Fixe Länge von Ein- und Ausgabe
  • Wird das (Zwischen-) Resultat länger, wird abgeschnitten
    • Integer Overflow
    • Float Precision loss
  • Schnell, üblicherweise konstante Laufzeit
    • deterministisches Laufzeitverhalten

Langzahlarithmetik

  • Rechnung wird bei Bedarf aus mehreren elementare Schritten kombiniert
  • Addition und Subtraktion mit Übertragsbit
  • komliziertere Algorithmen für Multiplikation, Division
  • Limitierend ist der Speicher

Symbolische Mathematik

  • "Algebra"
  • Operationen sind statische Elemente in Datenstrukturen (typischerweise abstract syntax tree)
  • Vereinfachungen, Term-Umformungen
  • Evaluation ganz am Schluss, idealerweise nur noch einfache Prozessorrechnung

Gleitpunktarithmetik

Die selbe Rechnung in PHP

precision

sqlite

Grundproblem

  • Mathematik entspricht nicht immer 1:1 dem, was der Code macht.
    • aber eben doch fast immer ->  falsches Vertrauen
  • Mathematische Invarianten (z.B. Schritte von 0,10 auf einer Skala) verhalten sich nicht immer gemäss ihrer mathematischen Definition
  • Führt zu schwer auffindbaren Bugs

Konkretes Problem

  • winzige Ungenauigkeiten können sich nach Berechnungen, Imports, etc. einschleichen und sind nicht sichtbar, i.d.R auch nicht beim Debuggen
  • werden diese Werte später in Vergleichen (<, >, =) verwendet, kommt es zu den genannten Bugs.

Lösungs- / Präventionsstrategien

Runden

  • konsequent vor Vergleichen (<, >, =, ...)
  • oder konsequent nach Operationen (Achtung bei Daten von externer Herkunft!!!)

Tests

  • assertEquals mit delta

Runden mit PHP

Ganzzahlige Representation

  • Felder sind in Models und Entitäten immer ganzzahlig und entsprechen einer Zehnerpotenz (z.B. 100stel / Rappen / Cent)
  • Für Ein- und Ausgabe wird immer konvertiert (implizite Rundung)
  • rechnerisch effizienteste Lösung (Performance)
  • Probleme mit Prozenten (z.B. MwSt) 
    • Rechenreihenfolge wichtig
  • eher aufwändig

bcmath

  • PHP Extension, Wrapper für bc
  • Langzahlarithmetik
  • Wenig Funktionen (Grundoperationen)
  • String Argumente
  • langsamer

bcmath

bcmath performance

Checks an Systemgrenzen

  • Typisierte View Models im Frontend
  • Datenbanktypen nutzen (z.B. DECIMAL)

 

 

  • Import-Checks / Rundung bei Import

Zusammenfassung

  • Keine "One Size Fits all"- Lösung
  • Wenn Performance keine wichtige Rolle spielt, ist bcmath zu Empfehlen
  • Rundungsstrategie ist als "Patch" mit dem kleinsten Aufwand verbunden im Fall von float-Bugs
  • Bewusstsein für die Problematik ist schon die halbe Miete
    • anders debuggen (==)
    • Checks an Systemgrenzen 
  • Precision hat keinen Einfluss auf Berechnungen, aber Darstellung
Made with Slides.com