Skip to content

Should lossy packing round to the closest Float instead of truncating? #349

Closed as not planned

Description

@thedavidmeister

Lossy packing truncates: when a value needs more digits than an int224 coefficient holds, it keeps the representable Float toward zero. #333 sharpens this into "the closest representable Float without exceeding the true magnitude".

The #340 discussion raised the question of whether truncation is the right default at all. The absolute error can be up to one unit in the last place, where rounding to the closest Float would be at most half a unit. Example from #340: for 1e100 − 1, truncation gives (10^67 − 1)e33 (off by about 1e33), and the closest Float is 1e100 (off by 1).

The library can't know the caller's safe direction. DeFi rounds in the protocol's favour, which is down for some call sites and up for others. So "always toward zero" isn't a safety property on its own, and closest-value rounding may be the better default.

Decide:

  • Default rounding for lossy packing and for every operation built on it (mul, div, inv, add/sub, conversions): toward zero, or closest Float (and which tie-break).
  • Whether callers need explicit directional variants (down/up) for the cases where direction matters.

Consequences to weigh:

Refs #333, #340, #339.

Activity

  1. thedavidmeister commented on Oct 9, 2026

    @thedavidmeister
    ContributorAuthor

    Related finding from #386 / PR #411: log10, pow10 and pow are not correctly rounded. They round an internal approximation, within the proven bound, to nearest at 41 digits. A true value that lies within that bound of a rounding tie can therefore land on either neighbour.

    Example: x = 62876337819492131669053461917963730523385842721800e-50. The true 10^x (bc) is just above a tie (…3215|500000001784…), but pow10 rounds down. That is within the documented bound.

    #411 rewords the NatSpec and README to say this, instead of the literal "rounded to nearest, half up". The tie-rounding test stays as a white-box check of the internal rounding step; the public contract is the bound.

    For this issue: whatever default rounding direction is chosen, it can only be exact for operations that are exact before packing (add, sub, mul, div, inv, sqrt, the conversions). For the transcendentals the contract is a bound around the true value, so "round once in direction X" can only be guaranteed up to that bound, unless they get the precision to decide ties (the table-maker's dilemma; see #350).

  2. thedavidmeister commented on Oct 11, 2026

    @thedavidmeister
    ContributorAuthor

    Closing per the ruling on #350: toward zero stays the default for lossy packing and every operation built on it. add/sub move to round once toward zero (#350), so mul, div, inv, add, sub and the lossy conversions all truncate the exact result once. Closest-value rounding is not adopted as the default; sqrt, pow, pow10 and log10 stay documented as rounded approximations within their proven bounds.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions