question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Floating point errors when decoding Cadence numbers larger than 32 bits

See original GitHub issue

The JSON-Cadence Data Interchange Format spec states:

Although JSON supports integer literals up to 64 bits, all integer types are encoded as strings for consistency.

FCL decodes all numeric types as to a number:

const decodeNumber = async (num, _, stack) => {
  try {
    return Number(num)
  } catch (e) {
    throw new Error(`Decode Number Error : ${stack.join(".")}`)
  }
}

This causes floating point errors for numbers larger than 32 bits (the @onflow/decode library is used in the example below since it exposes the underlying decode method, but the same issue exists in the @onflow/fcl implementation):

import { decode } from '@onflow/decode'

const encoded = {
  "type": "UInt64",
  "value": "18446744073709551615"
}

const decoded = await decode(encoded)
console.log(decoded)
// Output is 18446744073709552000 instead of the expected value

I’m happy to submit a PR, but hoping for discussion on the preferred solution since I can’t think of a way to do this without breaking backward compatibility. My initial thought is to leave numeric values as a string when decoded and let the user handle this however they’d like in their userspace code, but this does put the responsibility on the user to know about this issue and implement a solution. Another option would be to wrap all numeric types as an object that properly handles large values.

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:10 (5 by maintainers)

github_iconTop GitHub Comments

2reactions
JeffreyDoylecommented, Jan 13, 2022

I believe a safe solution to this issue would be to do the following:

The default decoders for [U]Int* and Word* types should, if the value being decoded represents a number that exceeds Number.MAX_SAFE_INTEGER, emit a console error explaining how a Custom Decoder should be specified to safely handle these values:

The error might look like:

Flow JS-SDK Decode Error: The ${type} being decoded represents a number that exceeds Number.MAX_SAFE_INTEGER. It is recommended you use a Custom Decoder to address such values:

Example:
fcl.config()
 .put("decoder.UInt64", num => BigInt(num))

This solution allows users to make use of whichever tool they desire to handle large numbers. By emitting a console error, we also inform the user that there is an issue, and of how to remedy the issue. Importantly, it allows us to avoid changing the return types of the existing decoders, or worse, have multiple return types.

When declaring an argument of type [U]Int* or Word*, we should open them up to allow strings as arguments, along with numbers.

eg:

fcl.send([
  fcl.script`pub fun main(a: Int8): Int8 { return a }`,
  fcl.args([ fcl.arg("8", t.Int8) ])
])

or

fcl.send([
  fcl.script`pub fun main(a: Int8): Int8 { return a }`,
  fcl.args([ fcl.arg(8, t.Int8) ])
])

JSON-CDC specifies the values of [U]Int* or Word* types as strings https://docs.onflow.org/cadence/json-cadence-spec/#integers. The types declared in @onflow/types should be updated to directly use the argument value if it is a string, or if it is a number, convert it to a string.

Libraries like BigNumber and BigInt have functionality to convert their values to strings. For example, if a user wants to specify their BigInt value as an argument to a script, they can easily convert it to string:

fcl.send([
  fcl.script`pub fun main(a: Int): Int { return a }`,
  fcl.args([ fcl.arg(12n.toString(), t.Int) ])
])

Do you believe these proposals would adequately solve this issue? @chriswayoub @bluesign @chasefleming

0reactions
chriswayoubcommented, Jan 12, 2022

@bluesign I agree, maybe I will create custom encoders as a separate enhancement request and submit a PR there if the team is open to that.

Read more comments on GitHub >

github_iconTop Results From Across the Web

What Every Computer Scientist Should Know About Floating ...
Although there are infinitely many integers, in most programs the result of integer computations can be stored in 32 bits.
Read more >
What are floating point errors? [Answered]
Floating point numbers have limitations on how accurately a number can be represented. ... For example, a 32-bit integer type can represent:.
Read more >
Lightweight Floating-Point Arithmetic: Case Study of Inverse ...
FP representation with less than half of the IEEE standard. FP bit-width can produce almost ... Figure 2), a 32-bit fixed-point number with...
Read more >
Fixed-Posit: A Floating-Point Representation for Error ... - arXiv
We do not consider posits with es values greater than 6, like (32,7) since these posits can have maximum 22 fraction bits thereby...
Read more >
Design of a Fused Multiply-Add Floating-Point and Integer ...
Their most noteworthy being the System/360 floating-point format. In this format a single precision binary floating-point number is stored in a 32-bit word....
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found