Loss of Precision

Published by Mario Oettler on

Die Ethereum Virtual Machine kann nur mit ganzen Zahlen (Integer), z. B. 1, 2 oder 547853, umgehen. Das Rechnen mit Gleitkommazahlen, wie z. B. 1,45763, ist nicht möglich.

Trotzdem gibt es einen Divisions-Operator „/“. Wenn das Ergebnis eine Gleitkommazahl ist, werden die Stellen nach dem Komma einfach abgeschnitten. Damit geht ein Genauigkeitsverlust (Loss of precision) einher.

Beispiel:

Mit Gleitkommazahlen: 2721/10 = 272,1

Ohne Gleitkommazahlen: 2721/10 = 272

Das zweite Ergebnis wird in Solidity ausgegeben.

Obwohl der Unterschied auf den ersten Blick eher klein aussieht, kann er eine deutliche Wirkung entfalten. Das folgende Beispiel verdeutlicht dies.

Tokenanzahl: n = 255

Zins: r = 5

Reward-Multiplikator: m = 4

Formel: x = n/100*r*m

Korrekte RechnungUngenaue RechnungUmstellung der Formel Variante 1Umstellung der Formel Variante 2
255/100*5*4 = 51n/100: 255/100 = 2
2*r*m: 2*5*4 = 40
n*r/100*m
n*5/100*4
255*5 = 1275

1275/100 = 12 (die Nachkommastellen werden abgeschnitten)
12*4 = 48
n*r*m/100
255*5*4/100 = 51

Das Kommutativgesetz (Multiplikatoren können vertauscht werden) kann hier also nicht ohne Weiteres angewendet werden, da sich die Ergebnisse ändern.

Lösungen

Folgende Möglichkeiten gibt es, um mit dem Genauigkeitsverlust bei Gleitkommaoperationen umzugehen.

Skalierung: Man multipliziert die Ausgangswerte mit einem Skalierungsfaktor (z. B. 1000), um die Genauigkeit zu erhöhen.

Rundungshelfer: Statt der Formel a/b kann man den Rundungshelfer (a+(b/2))/b nutzen. So wird korrekt auf- und abgerundet.

Speichern des Rests: man akkumuliert den Rest in einer Variable und schüttet ihn aus, sobald er die Grenze zu einer ganzen Zahl übersteigt.

Nutzung von Bibliotheken: Es gibt Solidity-Bibliotheken, die mit gebrochenen Zahlen umgehen können.

Eindämmung der Abweichungen: Prüfen, ob die Abweichung in einem akzeptablen Rahmen bleibt.

Erst multiplizieren, dann dividieren: Die Reihenfolge der Operationen beeinflusst das Ergebnis. Wenn man zuerst multipliziert und dann dividiert, wird der Fehler kleiner.

Aufgabe

Überzeugen Sie sich selbst von den unterschiedlichen Ergebnissen. Schreiben Sie dazu einen Smart Contract, der die oben gezeigten Formeln umsetzt.

Lösung

Den Code finden Sie auch in der Datei 09_Rounding_Errors.sol

pragma solidity 0.8.26;

contract RoundingErrors{


    function order1() public pure returns(uint256){
        uint n = 255;
        uint r = 5;
        uint m = 4;
        uint res;
        res = n/100*r*m;
        return res;
    }


    function order2() public pure returns(uint256){
        uint n = 255;
        uint r = 5;
        uint m = 4;
        uint res;

        res = n*r/100*m;
        return res;
    }

    function order3() public pure returns(uint256){
        uint n = 255;
        uint r = 5;
        uint m = 4;
        uint res;

        res = n*r*m/100;
        return res;
    }
}

Quelle: https://www.immunebytes.com/blog/precision-loss-vulnerability-in-solidity-a-deep-technical-dive/

Categories: