You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Deciding how many significant figures to show for BigDecimal #868
Do you want to request a feature or report a bug?
Feature
What is the current behavior?
A BigDecimals can have up to 128 significant figures, such as this: "0.000000000000000000000000004056857188988099386043441332328976217179316194185743029850020662656864699416968852342972096539937360"
What behavior do we want?
I think we made some good arguments from the start about why we want to keep a lot of precision #720
However this level of precision isn't easy on the eyes. It would be good if we could allow developers to choose how precise BigDecimal goes. For example, many solidity developers might set up their code so a token only has 8 decimal places. In this case, we don't need 128 bits of precision. The simplest solution I can think of is something like this:
let largeNumber: BigDecimal = "0.000000000000000000000000004056857188988099386043441332328976217179316194185743029850020662656864699416968852342972096539937360"
largeNumber.decimals(35)
console.log(largeNumber) --> "0.00000000000000000000000000405685718"
Basically a method that lets the developer choose . Open to other suggestions! This is only a first pass.
Talking with @leodasvacas he suggested a way to do this, and I implemented it like so in a mapping:
// For each division by 10, add one to exponent to truncate one significant figure
function truncateBigDecimal(bd: BigDecimal, truncateAmount: i32):BigDecimal {
// This will shave off the actual digits, so our BigInt is getting smaller
for(let i = 0; i < truncateAmount; i++){
bd.digits = bd.digits.div(BigInt.fromI32(10))
}
// This adds to the exponent, which is negative for numbers below zero
// and moves the decimal point to be in line with the fact that the digits BigInt got 1 length shorter
bd.exp = bd.exp.plus(BigInt.fromI32(truncateAmount))
return bd
}
This works, but it is untested. It works in the case where I knew all my BigDecimals would be smaller than 1.0. I'm not sure if it would work with 234325.1241353144342342342.
However this level of precision isn't easy on the eyes
Looking at a query is something we do when developing, but primarily it is meant to be consumed by machines which can then round and format a number in a bunch of different ways for presentation. IMO that's mostly not our concern, it's easy for the consumer to do and we can't cover all cases anyways.
We should however have some limit, because right now the number of zeroes we print after or before the dot can easily get very large. IMO we should then switch to exponential notation. Maybe we should always use exponential notation?
Can we just have a helper function, like the one above, in the graph-ts library - and let the consumer use it at their own discretion? This way we don't have to change anything, but if consumers find it too long they can shorten it themselves.
Also for reference, Right now I believe the digits max out at 128 bits, and so that is 0 and . with 126 numbers after the decimal. I guess there can only be 127 digits combined, left or right of the period
@davekaj By consumer I meant querier, not the developer of the subgraph. But yes having that helper wouldn't hurt. The limit shouldn't be 128, could you confirm that if you divide by 10 a bunch of times, you never get more than 128 digits? That would be a bug. Exponential notation should be easy to implement.
This new function I created works very well, you just pass it how many decimals you desire.
I think if we add this helper to graph-ts, it might solve this issue completely.
// Must keep all values left of the decimal, and then allow user to decide how many decimals they want
export function truncateBigDecimal(bd: BigDecimal, decimalLength: i32): BigDecimal {
// Figuring out how many digits to truncate
// exp is negative if there are decimals, so we add it, unintuitively
let largerThanZeroLength = bd.digits.toString().length + bd.exp.toI32()
// if largerThanZeroLength is negative, then digit length will be less than 18, which is okay
let newDigitLength = decimalLength + largerThanZeroLength
let lengthToTruncate = bd.digits.toString().length - newDigitLength
// This means it was originally smaller than desired decimalLength, so do nothing
if (lengthToTruncate < 0) {
return bd
} else {
// This will shave off the length of the full digits to what is desired
for (let i = 0; i < lengthToTruncate; i++) {
bd.digits = bd.digits.div(BigInt.fromI32(10))
}
// simply set the exp to what was desired (* -1 because it must be negative, but as parameter it is passed in postive
bd.exp = BigInt.fromI32(decimalLength* -1)
return bd
}
}
Activity
davekay100 commentedon Apr 10, 2019
Talking with @leodasvacas he suggested a way to do this, and I implemented it like so in a mapping:
This works, but it is untested. It works in the case where I knew all my BigDecimals would be smaller than
1.0. I'm not sure if it would work with234325.1241353144342342342.But it is a start!
leoyvens commentedon Apr 10, 2019
Looking at a query is something we do when developing, but primarily it is meant to be consumed by machines which can then round and format a number in a bunch of different ways for presentation. IMO that's mostly not our concern, it's easy for the consumer to do and we can't cover all cases anyways.
We should however have some limit, because right now the number of zeroes we print after or before the dot can easily get very large. IMO we should then switch to exponential notation. Maybe we should always use exponential notation?
davekay100 commentedon Apr 10, 2019
Yeah I agree with everything you are saying.
Can we just have a helper function, like the one above, in the
graph-tslibrary - and let the consumer use it at their own discretion? This way we don't have to change anything, but if consumers find it too long they can shorten it themselves.Also for reference, Right now I believe the digits max out at 128 bits, and so that is
0and.with126numbers after the decimal. I guess there can only be 127 digits combined, left or right of the perioddavekay100 commentedon Apr 10, 2019
would exponential notation take a lot of time to implement?
leoyvens commentedon Apr 11, 2019
@davekaj By consumer I meant querier, not the developer of the subgraph. But yes having that helper wouldn't hurt. The limit shouldn't be 128, could you confirm that if you divide by 10 a bunch of times, you never get more than 128 digits? That would be a bug. Exponential notation should be easy to implement.
davekay100 commentedon Apr 11, 2019
ahhh my hypothesis was wrong apparently. you can get them really long.
but for some reason all the data I was having returned from normal calculations never was longer than 128 digits
davekay100 commentedon Apr 15, 2019
This new function I created works very well, you just pass it how many decimals you desire.
I think if we add this helper to graph-ts, it might solve this issue completely.
Jannis commentedon Apr 15, 2019
I'd like us to not drop more stuff into the helpers file. We need to be deliberate about the APIs that we expose and support going forward.
But I could see us adding a function like this to
BigDecimal. @leodasvacas: What do you think?leoyvens commentedon Apr 15, 2019
I don't see a problem in adding that helper as a method on BigDecimal, if @davekaj is willing to make a PR to graph-ts.
davekay100 commentedon Apr 15, 2019
leoyvens commentedon Apr 15, 2019
I want to set up a testing infrastructure in graph-ts that compiles test files to wasm and runs them, but right now no, there isn't.
davekay100 commentedon May 17, 2019
I think we can close this now that
truncate()is ingraph-ts