Skip to content

Instantly share code, notes, and snippets.

@ssadler
Created April 3, 2024 13:33
Show Gist options
  • Save ssadler/1628a01e66f848c81b922a7bd582bfc5 to your computer and use it in GitHub Desktop.
Save ssadler/1628a01e66f848c81b922a7bd582bfc5 to your computer and use it in GitHub Desktop.
Lossy decimal pack uint96 to uint40
// 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