Created
April 3, 2024 13:33
-
-
Save ssadler/1628a01e66f848c81b922a7bd582bfc5 to your computer and use it in GitHub Desktop.
Lossy decimal pack uint96 to uint40
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: UNLICENSED | |
pragma solidity ^0.8.16; | |
library Balance { | |
uint constant internal BALBITS = 35; | |
uint constant internal EXPBITS = 5; | |
uint constant internal BALMAX = 2**35-1; | |
uint constant internal BALMAXS = 2**34-1; | |
uint constant internal SIGNED = 2**39; | |
uint constant internal B40 = 2**40-1; | |
uint constant internal B96 = 2**96-1; | |
// { number bits }{ exp bits } | |
// { signed bit }{ number bits }{ exp bits } | |
function pack(uint96 bal) internal pure returns (uint40 out) { | |
assembly { | |
bal := and(bal, B96) | |
if gt(bal, mul(BALMAX, 1000)) { | |
out := add(3, out) | |
bal := div(bal, 1000) | |
if gt(bal, mul(BALMAX, 1000)) { | |
out := add(3, out) | |
bal := div(bal, 1000) | |
if gt(bal, mul(BALMAX, 1000)) { | |
out := add(3, out) | |
bal := div(bal, 1000) | |
if gt(bal, mul(BALMAX, 1000)) { | |
out := add(3, out) | |
bal := div(bal, 1000) | |
for {} gt(bal, mul(BALMAX, 1000)) {} { | |
out := add(3, out) | |
bal := div(bal, 1000) | |
} | |
} | |
} | |
} | |
} | |
if gt(bal, BALMAX) { | |
out := add(1, out) | |
bal := div(bal, 10) | |
if gt(bal, BALMAX) { | |
out := add(1, out) | |
bal := div(bal, 10) | |
if gt(bal, BALMAX) { | |
out := add(1, out) | |
bal := div(bal, 10) | |
} | |
} | |
} | |
out := add(out, shl(EXPBITS, bal)) | |
} | |
} | |
function unpack(uint40 encoded) internal pure returns (uint96 bal) { | |
//unchecked { | |
// bal = uint96((encoded >> EXPBITS) * 10 ** (encoded & 31)); | |
//} | |
assembly { | |
encoded := and(encoded, B40) | |
bal := mul(shr(EXPBITS, encoded), exp(10, and(encoded, 31))) | |
} | |
} | |
function packS(int96 bal) internal pure returns (uint40 out) { | |
assembly { | |
if slt(bal, 0) { | |
out := SIGNED | |
bal := sub(0, bal) | |
} | |
// this needs to come after flip to positive because doesnt work on negative numbers | |
bal := and(bal, B96) | |
if gt(bal, mul(BALMAXS, 1000)) { | |
out := add(3, out) | |
bal := div(bal, 1000) | |
if gt(bal, mul(BALMAXS, 1000)) { | |
out := add(3, out) | |
bal := div(bal, 1000) | |
if gt(bal, mul(BALMAXS, 1000)) { | |
out := add(3, out) | |
bal := div(bal, 1000) | |
if gt(bal, mul(BALMAXS, 1000)) { | |
out := add(3, out) | |
bal := div(bal, 1000) | |
for {} gt(bal, mul(BALMAXS, 1000)) {} { | |
out := add(3, out) | |
bal := div(bal, 1000) | |
} | |
} | |
} | |
} | |
} | |
if gt(bal, BALMAXS) { | |
out := add(1, out) | |
bal := div(bal, 10) | |
if gt(bal, BALMAXS) { | |
out := add(1, out) | |
bal := div(bal, 10) | |
if gt(bal, BALMAXS) { | |
out := add(1, out) | |
bal := div(bal, 10) | |
} | |
} | |
} | |
out := add(out, shl(EXPBITS, bal)) | |
} | |
} | |
function unpackS(uint40 packed) internal pure returns (int96) { | |
unchecked { | |
int96 bal = int96(unpack(packed & ~uint40(SIGNED))); | |
return packed & SIGNED > 0 ? -bal : bal; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment