Division/Prozente in Solidität (eine Problemumgehung)

Division/Prozentsätze sind ein erforderlicher Bestandteil vieler Anwendungen, sind jedoch schwierig zu implementieren, da fixedsie ufixedin Solidity noch nicht unterstützt werden. Ich habe mir diesen Workaround ausgedacht:

  pragma solidity ^0.4.9;

  contract Math {

    function Math() public {}

    function percent(uint a, uint b) public constant returns(uint, uint) {
      uint c = a*b;
      uint integer = c/100;
      uint fractional = c%100;
      return (integer, fractional);
    }

  }

Wenn ich 12% von 27 bekommen will und calle percent(27, 12), bekomme ich korrekt zurück 3und 24repräsentiere 3.24. Die Einschränkung darauf ist natürlich, dass dies percent(12.5, 100)nicht möglich ist.

Wenn ich jedoch anrufe percent(17, 359), bekomme ich zurück 61und 3. Das eigentliche Ergebnis ist 61.03jedoch , denn fractionalist a uint, wird die 0 vor der 3 weggelassen. Gibt es eine Möglichkeit herauszufinden, ob ein Bruch eine führende Null hat?

Hätte nach Ihrer Logik nicht jede Zahl < 10 eine führende Null für den Bruch? Andernfalls würde es eine zweistellige Zahl zwischen 10 und 99 zurückgeben.
Hier gibt es keine allgemeine Lösung, da die Anzahl der führenden Nullen im Dezimalteil beliebig sein kann. Sie sollten wahrscheinlich selbst darüber nachdenken, warum Sie diese Anforderung überhaupt aufgestellt haben. Um X % von Y mit voller Genauigkeit darzustellen , müssen Sie ein Zahlenpaar pflegen – Zähler und Nenner. Genauer gesagt - X * Yund 100. Teilen Sie nicht X * Ydurch 100, bis Sie an dem Punkt angelangt sind, an dem Sie es wirklich brauchen. Angenommen, Sie müssen diesen Wert später in Ihrem Code mit multiplizieren Z, dann tun Sie dies an dieser Stelle X * Y * Z / 100.
Und natürlich, wenn Sie auch Bruchprozentsätze unterstützen möchten, können Sie 100 auf die gewünschte Auflösung erhöhen (dh 1000, 10000 usw.).
Ich denke du hast die Lösung mit Angabe der Auflösung wie bei @goodvibration beschrieben. Auch Ihr Programm gibt bereits die richtige Antwort; als 100/3 = 0.03und100/24 = 0.24

Antworten (2)

Meiner Meinung nach ist dies wahrscheinlich eine unangemessene Verwendung der On-Chain-Logik, nicht nur wegen der damit verbundenen Umständlichkeit des Codes, sondern auch wegen der Gaskosten, die damit verbunden sind, etwas zu tun, das der Anrufer leicht selbst herausfinden kann.

Allerdings könnte man mit Dezimalstellen etwa so umgehen: Can't do any integer division

Diese Interpretation gibt einen ganzzahligen Prozentsatz korrekt aufgerundet zurück. Zum Beispiel gibt 23,5 % 24 zurück. Sie könnten die Dezimalstellen der Genauigkeit erhöhen, indem Sie an der Art und Weise herumbasteln, wie es mit den ganzzahligen Konvertierungen spielt.

pragma solidity 0.4.24;

contract Percent {

    function getPercent(uint part, uint whole) public pure returns(uint percent) {
        uint numerator = part * 1000;
        require(numerator > part); // overflow. Should use SafeMath throughout if this was a real implementation. 
        uint temp = numerator / whole + 5; // proper rounding up
        return temp / 10;
    }
}

Ich hoffe es hilft.

AKTUALISIEREN

Gehen Sie wie folgt umgekehrt vor. Erhöhen Sie außerdem die Größenordnung, um ein Ergebnis mit höherer Genauigkeit zurückzugeben.

Hier ergibt 12 über 27 (12,27) 324, was der Kunde als 32,4 % interpretieren kann.

function getFraction(uint percent, uint base) public pure returns(uint portion) {
    uint temp = percent * base * 10 + 5;
    return temp / 10;
}

Das Wichtigste ist, mit ganzen Zahlen zu arbeiten. Der Trick mit +5und /10besteht darin, für eine korrekte Rundung zu sorgen.

Danke Rob, das Testen Ihrer Funktion mit (17, 359)beispielsweise würde zurückgeben 5. Ich beziehe mich jedoch darauf, eine Möglichkeit zu schaffen, die genaue Ganzzahl und den Bruch zu kennen.
Der Link, auf den Sie verwiesen haben, funktioniert ähnlich wie mein Code und hat das gleiche Problem. Die führende Null geht verloren, sobald sie in einer gespeichert wurde uint.
Ich habe ein weiteres Beispiel hinzugefügt.

Wie rob erwähnt, arbeiten Sie mit Integer. Ich komme andersherum auf die Idee. In meinem Szenario muss ich den Zinssatz mit dem Basisbetrag hinterlegen. Um den Prozentsatz zu finden und mit dem Basisbetrag zu addieren, lautet die Formel wie folgt

y = amount + (amount * percentValue / 100)
y = (100 * amount) + (amount * percentValue) /100
y*100 = amount * (100 + percentValue)

hier ist y im Grunde die erwartete Ausgabe. Ich teile den Wert nicht durch 100, also bleibt der Wert ganzzahlig und im Frontend-Teil teile ich den Wert und speichere nur die Ausgabe von

amount(100 + percentValue)

in Solidität.