_Docs/
Get StartedModulesPlatformDeployCookbookChangelogReference
_Stack
_Modules
  • Ledger
  • Numscript
    • Program Structure
    • Selecting an Interpreter
    • Unambiguous Monetary Notation
    • CLI
    • Numscript specs format
    • Reference
      • Send
      • Sources
      • Destinations
      • Rounding
      • Save
      • Overdraft
      • Variables
      • Metadata
      • oneofexp
      • Account Interpolationexp
      • get_assetexp
      • get_amountexp
      • Mid-script Function Callsexp
      • Asset Colorsexp
  • Connectivity
  • WalletsEE
  • FlowsEE
  • ReconciliationEE
  1. Modules
  2. Numscript
  3. Reference
  4. Rounding
Numscript

Rounding

There is no support for floating point or decimal numbers in Numscript, which will always make sure non integer values resulting from monetary computations are balanced.

Practically, this means appropriately distributing the non integer allocation remainder to accounts. Numscript works by flooring any computed amount and subsequently spreading the remaining amount as fairly as possible starting from top to bottom.

In the example below:

Numscript
send [COIN 99] (
  source = @world
  destination = {
    50% to @rider
    50% to @taxes
  }
)

The @rider account will receive COIN 50 and the @taxes account COIN 49. The opposite can be achieved by reversing the order of destinations:

Numscript
send [COIN 99] (
  source = @world
  destination = {
    50% to @taxes
    50% to @rider
  }
)

In a more complex example below, we are splitting 99 into 5 which would result in 19.8 allocated to each account. Numscript will first allocate 19 to every account, then attempt to distribute the remaining 4 evenly starting from @a:

Numscript
send [COIN 99] (
  source = @world
  destination = {
    1/5 to @a
    1/5 to @b
    1/5 to @c
    1/5 to @d
    1/5 to @e
  }
)

Which will resolve into the following postings:

JSON
[
  {
    "source": "world",
    "destination": "a",
    "amount": 20,
    "asset": "COIN"
  },
  {
    "source": "world",
    "destination": "b",
    "amount": 20,
    "asset": "COIN"
  },
  {
    "source": "world",
    "destination": "c",
    "amount": 20,
    "asset": "COIN"
  },
  {
    "source": "world",
    "destination": "d",
    "amount": 20,
    "asset": "COIN"
  },
  {
    "source": "world",
    "destination": "e",
    "amount": 19,
    "asset": "COIN"
  }
]

Fixed fees and allocation order#

When combining fixed amounts with percentage-based allocations, the order of destinations matters due to the multi-pass resolution mechanism.

The problem#

Consider a transaction splitting funds between a payment provider (fixed fee + percentage), a franchise fee (percentage), and a store (remaining):

Numscript
send [AUD/2 1999] (
  source = @world
  destination = {
    7/1999 to @payment_provider
    0.6% to @payment_provider
    0.5% to @franchise_fee
    remaining to @store
  }
)

You might expect @payment_provider to receive exactly 7 cents as a fixed fee, but it actually receives 8 cents. This happens because of the two-pass allocation mechanism.

How multi-pass allocation works#

First pass - Numscript allocates whole amounts:

  • 7/1999 * 1999 = 7 → @payment_provider
  • 0.6% * 1999 = 11.994 → floors to 11 for @payment_provider (keeps 0.994 aside)
  • 0.5% * 1999 = 9.995 → floors to 9 for @franchise_fee (keeps 0.995 aside)
  • remaining = 1999 - 7 - 11 - 9 - ceil(0.994 + 0.995) = 1970 → @store

Second pass - Numscript distributes the remaining fragments:

  • Total distributed: 1999 - 2 = 1997
  • Remaining to distribute: 2 cents
  • Distribution is top to bottom: first position (@payment_provider) gets +1, second position (@payment_provider again) gets +1

Result: @payment_provider receives 7 + 1 = 8 cents instead of the expected 7.

The solution#

Move the fixed fee after the percentage allocations. Since percentage computations generate at most 1 cent fragment each, placing them first ensures they absorb the remainder:

Numscript
send [AUD/2 1999] (
  source = @world
  destination = {
    0.6% to @payment_provider
    0.5% to @franchise_fee
    7/1999 to @payment_provider
    remaining to @store
  }
)

Now the percentages receive any extra cents, and the fixed fee remains exactly 7 cents.

When mixing fixed amounts and percentages, place percentage-based allocations before fixed amounts to ensure fixed fees remain exact.

DestinationsSave
On This Page
  • Fixed fees and allocation order
  • The problem
  • How multi-pass allocation works
  • The solution