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
Do you want to request a feature or report a bug?
Feature
What is the current behavior?
Big Ints can't return fractions, so in some cases we lose precision that we need
If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem.
Reproduction of the problem can be seen on uniswap subgraph, where we try to divide ETH price by MKR price and it returns 0 instead of ~0.270
What is the expected behavior?
We get the fraction returned
I agree that if you divide two BigInts you should get an exact result. Using f64 internally is a bad option since it will lose precison in intermediary operations, since this usually represents monetary values precision must be controled. So we should represent the number as a fraction.
An open question is how do we represent this in graphql? Some options:
Return a fraction string and let frontend get as much precision as it wants.
Approximate to an f64 so we can use the native graphql type. This gets us around 15 digits of decimal precision.
Aproximate to a decimal string so we can get more precision than an f64, but how much is enough? 100 decimal places? Make it user configurable?
Oh wow we support floats in the store (I was assuming we didn't). In light of this whole discussion, I suppose we should only have one type for non-integer numbers, and we should drop support for f32 and support only a new fixed-point BigDecimal.
But we never write any functions to handle setting or getting Float. Since no one is dependant on f32 right now, I agree with what you are saying leo, that we should stick with just BigDecimal
In graph-node, add a BigDecimal scalar graphql type, with all the filters that BigInt has. Coerce it from input strings, ints or floats. Add a store::Value::BigDecimal variant, with a bigdecimal as the backing implementation. Store it in the entity json as a string, casting to ::numeric for filtering, same as we do for BigInt.
In graph-ts, add a BigDecimal class with a BigInt field for the digits and an i64 for the scale (decimal expoent). Have a from_big_int constructor and the math operators (exported from the node). Change BigInt division from returning a BigInt to returning a BigDecimal.
In graph-cli, add BigDecimal to validation and to schema codegen.
Store it as a string in the entity json, casting to postgres numeric when necessary.
In graph-ts, represent it as a class with a BigInt field for the digits and an i64 for the scale (decimal expoent), with a from_big_int constructor, a from_float constructor and a parse function. Have same operations as BigInt. Change BigInt division from returning a BigInt to returning a BigDecimal.
Tasks
Graph Node: Add BigDecimal scalar graphql type, with appropriate filters and coercions.
Graph Node: Add BigDecimal store value type, and implement its operations in the postgres store.
Graph CLI: Add BigDecimal to validation and schema codegen.
Graph TS: Implement the BigDecimal class and its operations.
Graph Node: Implement runtime support for the BigDecimal AS class.
I only have two comments about the plan (which looks great):
In graph-ts, the methods would be fromBigInt(n: BigInt) and fromFloat(n: f64) instead of from_big_int and from_float.
Changing the behavior of div// for BigInt might break existing subgraphs. I also think there should be a BigInt division method that returns a BigInt. Think 5 / 4 = 1 vs. 5 / 4.0 = 1.25. Can we check if we can overload the / operator so that we can do e.g.
Thanks for the comments! Different languages take different approaches with division of integers. Since JS/TS will return a float, it seems reasonable to follow the convention. We should offer a asBigInt() to truncate the BigDecimal.
Activity
leoyvens commentedon Feb 13, 2019
I agree that if you divide two BigInts you should get an exact result. Using f64 internally is a bad option since it will lose precison in intermediary operations, since this usually represents monetary values precision must be controled. So we should represent the number as a fraction.
An open question is how do we represent this in graphql? Some options:
15 remaining items
leoyvens commentedon Feb 27, 2019
Oh wow we support floats in the store (I was assuming we didn't). In light of this whole discussion, I suppose we should only have one type for non-integer numbers, and we should drop support for
f32and support only a new fixed-pointBigDecimal.davekay100 commentedon Feb 27, 2019
We only refer to float here in
graph-ts index.ts:But we never write any functions to handle setting or getting Float. Since no one is dependant on f32 right now, I agree with what you are saying leo, that we should stick with just BigDecimal
[-]Add big decimal support[/-][+]Big Decimal Support[/+]leoyvens commentedon Mar 11, 2019
Implementation plan:
In graph-node, add a
BigDecimalscalar graphql type, with all the filters thatBigInthas. Coerce it from input strings, ints or floats. Add astore::Value::BigDecimalvariant, with a bigdecimal as the backing implementation. Store it in the entity json as a string, casting to::numericfor filtering, same as we do forBigInt.In graph-ts, add a
BigDecimalclass with aBigIntfield for the digits and ani64for the scale (decimal expoent). Have afrom_big_intconstructor and the math operators (exported from the node). ChangeBigIntdivision from returning aBigIntto returning aBigDecimal.In graph-cli, add
BigDecimalto validation and to schema codegen.Jannis commentedon Mar 11, 2019
@leodasvacas The plan looks good to me! 👍
leoyvens commentedon Mar 15, 2019
A better organized plan:
Requirements
Proposed solution
BigDecimal.BigIntin graphql, with the added ability of coercing from floats.numericwhen necessary.BigIntfield for the digits and ani64for the scale (decimal expoent), with afrom_big_intconstructor, afrom_floatconstructor and aparsefunction. Have same operations asBigInt. ChangeBigIntdivision from returning aBigIntto returning aBigDecimal.Tasks
BigDecimalscalar graphql type, with appropriate filters and coercions.BigDecimalstore value type, and implement its operations in the postgres store.BigDecimalto validation and schema codegen.BigDecimalclass and its operations.BigDecimalAS class.Jannis commentedon Mar 18, 2019
I only have two comments about the plan (which looks great):
In graph-ts, the methods would be
fromBigInt(n: BigInt)andfromFloat(n: f64)instead offrom_big_intandfrom_float.Changing the behavior of
div//forBigIntmight break existing subgraphs. I also think there should be aBigIntdivision method that returns aBigInt. Think5 / 4 = 1vs.5 / 4.0 = 1.25. Can we check if we can overload the/operator so that we can do e.g.That would be ideal. If we can't do that, it's maybe better to keep
divas it is and add a new method to not break subgraphs.Jannis commentedon Mar 18, 2019
I touched base with the AS team regarding operator overloading. It's not currently possible.
leoyvens commentedon Mar 18, 2019
Thanks for the comments! Different languages take different approaches with division of integers. Since JS/TS will return a float, it seems reasonable to follow the convention. We should offer a
asBigInt()to truncate theBigDecimal.