Arbitrary-precision decimals for Go

apd

apd is an arbitrary-precision decimal package for Go.

apd implements much of the decimal specification from the General Decimal Arithmetic description. This is the same specification implemented by python’s decimal module and GCC’s decimal extension.

Features

  • Panic-free operation. The math/big types don’t return errors, and instead panic under some conditions that are documented. This requires users to validate the inputs before using them. Meanwhile, we’d like our decimal operations to have more failure modes and more input requirements than the math/big types, so using that API would be difficult. apd instead returns errors when needed.
  • Support for standard functions. sqrt, ln, pow, etc.
  • Accurate and configurable precision. Operations will use enough internal precision to produce a correct result at the requested precision. Precision is set by a "context" structure that accompanies the function arguments, as discussed in the next section.
  • Good performance. Operations will either be fast enough or will produce an error if they will be slow. This prevents edge-case operations from consuming lots of CPU or memory.
  • Condition flags and traps. All operations will report whether their result is exact, is rounded, is over- or under-flowed, is subnormal, or is some other condition. apd supports traps which will trigger an error on any of these conditions. This makes it possible to guarantee exactness in computations, if needed.

apd has two main types. The first is Decimal which holds the values of decimals. It is simple and uses a big.Int with an exponent to describe values. Most operations on Decimals can’t produce errors as they work directly on the underlying big.Int. Notably, however, there are no arithmetic operations on Decimals.

The second main type is Context, which is where all arithmetic operations are defined. A Context describes the precision, range, and some other restrictions during operations. These operations can all produce failures, and so return errors.

Context operations, in addition to errors, return a Condition, which is a bitfield of flags that occurred during an operation. These include overflow, underflow, inexact, rounded, and others. The Traps field of a Context can be set which will produce an error if the corresponding flag occurs. An example of this is given below.

See the examples for some operations that were previously difficult to perform in Go.

Documentation

https://pkg.go.dev/github.com/cockroachdb/apd/v2?tab=doc

Owner
Comments
  • Proper way to set a apd.NullDecimal?

    Proper way to set a apd.NullDecimal?

    The types on https://gopkg.in/nullbio/null.v6 implement a SetValid method, which maybe useful for NullDecimal. Otherwise, how should you set the value of NullDecimal?

  • ln: minor improvements

    ln: minor improvements

    Hi Matt,

    The Ln code looks good to me! I just have this suggested minor improvement to replace Mul by decimalTwo with an Add (should be a bit faster). I couldn't run the benchmark to verify if we see an improvement though.

    I think we could stop the loop when the delta (tmp2) becomes small enough (less than 10^-c.Precision or so). We would also optimize out the Sub in loop.done() since we already know the delta. I will look at that.

    By the way, I don't quite understand the logic in loop.done(); we "stall" when l.delta.Cmp(l.prevDelta) == 0? Why? I would expect in most iterative algorithms the delta to keep decreasing.. So we should stop when it becomes 0 or smaller than a certain value no?


    This change is Reviewable

  • Is this maintained?

    Is this maintained?

    I tried posting an issue a while ago (#98) and haven't heard anything. It seems like apd is still used in cockroachdb in production, which is a good sign but this repo itself doesn't look like it's had a ton of activity recently. So I'm wondering if this has a maintainer assigned, if we can expect anyone to look at issues or potential PRs, etc. We are considering using this library in a critical production code base and if there is no maintainer, we will likely fork or choose a different approach. Thanks 🙏 .

  • pow: correctly compute exactness

    pow: correctly compute exactness

    Power calculations of x**y where y is an integer will now correctly be flagged as exact when possible. This avoids lots of work and is thus much faster for these operations.

    Remove some unneeded precision changing in integerPower. It was leftover from the inf code which did bad things with 1/x calculations. This was causing some overflow problems that shouldn't have been present.

    This change reduces go test time from 2.5s to 0.9s.

    name          old time/op  new time/op  delta
    GDA/power-12   666ms ±16%   387ms ± 6%  -41.86%  (p=0.008 n=5+5)
    

    First commit is from another PR, but has a fix this needs.


    This change is Reviewable

  • precision, scale

    precision, scale

    Firstly, thank you for making this library available.

    We use CRDB as our data store and this library when we need to do decimal operations.

    Following taken from CRDB docs:

    DECIMAL(precision, scale), where precision is the maximum count of digits both to the left and right of the decimal point 
    and scale is the exact count of digits to the right of the decimal point.
    .
    .
    .
    When inserting a decimal value:
    
    If digits to the right of the decimal point exceed the column's scale, CockroachDB rounds to the scale.
    If digits to the right of the decimal point are fewer than the column's scale, CockroachDB pads to the scale with 0s.
    

    What is the best way to achieve CRDB's precision, scale with apd v2? An example would be handy.

    What is the best way to round digits to the right of the decimal point with apd v2 ? An example would be handy.

  • Implement json.Unmarshaler and json.Marshaler interfaces

    Implement json.Unmarshaler and json.Marshaler interfaces

    Implementing the encoding.TextUnmarshaler and encoding.TextMarshaler interfaces aren't sufficient for decoding/encoding JSON numbers since JSON numbers aren't strings.

    See the docs:

    • https://golang.org/pkg/encoding/json/#Unmarshal

      Otherwise, if the value implements encoding.TextUnmarshaler and the input is a JSON quoted string, Unmarshal calls that value's UnmarshalText method with the unquoted form of the string.

    • https://golang.org/pkg/encoding/json/#Marshal

      If no MarshalJSON method is present but the value implements encoding.TextMarshaler instead, Marshal calls its MarshalText method and encodes the result as a JSON string.

    In other words:

    • UnmarshalText() will support unmarshaling Decimals in JSON objects like {"decimal": "1.1"}
    • UnmarshalJSON() will support unmarshling Decimals in JSON ojbects like {"decimal": 1.1}

    Since most JSON objects use JSON numbers instead of strings, we need to implement the json.Unmarshaler interface.

  • improvements for Ln

    improvements for Ln

    Improving the Ln implementation by scaling to range [0.1, 1) and using Math.log for an initial estimate. Special casing for numbers close to 1 for which scaling would require too much precision.

    Also improving the precision calculation in Log10, and replacing the division by ln(10) to a multiplication (by adding 1/ln(10) constant).

    Benchmarks below (about the cases that are slower: these are high-precision cases where I believe we weren't using enough internal precision - we don't have tests with high precisions).

    Ln/P2/S-100/D2-24       631µs ± 1%   129µs ± 1%   -79.52%  (p=0.029 n=4+4)
    Ln/P2/S-10/D2-24        322µs ± 1%   114µs ± 1%   -64.71%  (p=0.029 n=4+4)
    Ln/P2/S-2/D2-24         315µs ± 1%   124µs ± 1%   -60.65%  (p=0.029 n=4+4)
    Ln/P2/S2/D2-24          313µs ± 1%   111µs ± 1%   -64.44%  (p=0.029 n=4+4)
    Ln/P2/S10/D2-24         372µs ± 3%   121µs ± 2%   -67.60%  (p=0.029 n=4+4)
    Ln/P2/S100/D2-24        691µs ± 2%   128µs ± 2%   -81.53%  (p=0.029 n=4+4)
    Ln/P10/S-100/D2-24      897µs ± 2%   284µs ± 1%   -68.29%  (p=0.029 n=4+4)
    Ln/P10/S-100/D10-24     886µs ± 2%   597µs ± 1%   -32.69%  (p=0.029 n=4+4)
    Ln/P10/S-10/D2-24       604µs ± 1%   284µs ± 2%   -53.00%  (p=0.029 n=4+4)
    Ln/P10/S-10/D10-24      609µs ± 2%   561µs ± 1%    -7.85%  (p=0.029 n=4+4)
    Ln/P10/S-2/D2-24        564µs ± 3%   313µs ± 0%   -44.58%  (p=0.029 n=4+4)
    Ln/P10/S-2/D10-24       640µs ± 6%   571µs ± 1%   -10.79%  (p=0.029 n=4+4)
    Ln/P10/S2/D2-24         538µs ± 1%   301µs ± 1%   -43.96%  (p=0.029 n=4+4)
    Ln/P10/S2/D10-24        569µs ± 3%   587µs ± 2%      ~     (p=0.114 n=4+4)
    Ln/P10/S10/D2-24        576µs ± 4%   300µs ± 3%   -47.94%  (p=0.029 n=4+4)
    Ln/P10/S10/D10-24       604µs ± 4%   623µs ± 2%      ~     (p=0.200 n=4+4)
    Ln/P10/S100/D2-24       918µs ± 2%   286µs ± 2%   -68.84%  (p=0.029 n=4+4)
    Ln/P10/S100/D10-24      927µs ± 3%   589µs ± 2%   -36.41%  (p=0.029 n=4+4)
    Ln/P100/S-100/D2-24    36.8ms ± 2%  19.0ms ± 1%   -48.18%  (p=0.029 n=4+4)
    Ln/P100/S-100/D10-24   35.0ms ± 1%  20.2ms ± 3%   -42.19%  (p=0.029 n=4+4)
    Ln/P100/S-100/D100-24  36.2ms ± 1%  78.0ms ± 1%  +115.44%  (p=0.029 n=4+4)
    Ln/P100/S-10/D2-24     33.5ms ± 2%  17.2ms ± 2%   -48.65%  (p=0.029 n=4+4)
    Ln/P100/S-10/D10-24    35.1ms ± 2%  21.1ms ± 1%   -39.96%  (p=0.029 n=4+4)
    Ln/P100/S-10/D100-24   34.9ms ± 3%  71.4ms ± 1%  +104.93%  (p=0.029 n=4+4)
    Ln/P100/S-2/D2-24      33.8ms ± 1%  19.3ms ± 7%   -42.98%  (p=0.029 n=4+4)
    Ln/P100/S-2/D10-24     34.4ms ± 1%  22.1ms ± 3%   -35.66%  (p=0.029 n=4+4)
    Ln/P100/S-2/D100-24    34.8ms ± 1%  74.0ms ± 0%  +112.48%  (p=0.029 n=4+4)
    Ln/P100/S2/D2-24       30.8ms ± 2%  18.4ms ± 2%   -40.25%  (p=0.029 n=4+4)
    Ln/P100/S2/D10-24      34.6ms ± 1%  22.2ms ± 2%   -35.91%  (p=0.029 n=4+4)
    Ln/P100/S2/D100-24     35.7ms ± 1%  78.7ms ± 2%  +120.44%  (p=0.029 n=4+4)
    Ln/P100/S10/D2-24      34.3ms ± 1%  18.7ms ± 1%   -45.57%  (p=0.029 n=4+4)
    Ln/P100/S10/D10-24     32.2ms ± 1%  21.9ms ± 4%   -32.03%  (p=0.029 n=4+4)
    Ln/P100/S10/D100-24    32.8ms ± 1%  82.5ms ± 2%  +151.47%  (p=0.029 n=4+4)
    Ln/P100/S100/D2-24     34.7ms ± 1%  18.9ms ± 1%   -45.52%  (p=0.029 n=4+4)
    Ln/P100/S100/D10-24    34.0ms ± 2%  22.3ms ± 2%   -34.31%  (p=0.029 n=4+4)
    Ln/P100/S100/D100-24   34.8ms ± 1%  98.0ms ± 1%  +181.17%  (p=0.029 n=4+4)
    

    This change is Reviewable

  • apd: remove some allocs from Decimal.setString

    apd: remove some allocs from Decimal.setString

    exps contains exactly 0, 1, or 2 elements. Provide space for 2 elements up front. This is an easy, broadly positive optimization. Some of the time/op benchmarks are noisy; the allocs benchmarks are clearer.

    name                           old time/op    new time/op    delta
    Exp/P5/S-4/D-2-8                  823ns ± 2%     917ns ±23%  +11.34%  (p=0.000 n=19+19)
    Exp/P5/S-4/D2-8                   894ns ±18%     876ns ±15%     ~     (p=0.141 n=18+18)
    Exp/P5/S-1/D-2-8                 1.60µs ± 1%    1.73µs ±26%   +8.07%  (p=0.000 n=20+19)
    Exp/P5/S-1/D2-8                  1.77µs ± 1%    1.96µs ±21%  +11.08%  (p=0.000 n=20+19)
    Exp/P5/S2/D-2-8                  3.92µs ± 1%    4.07µs ±10%   +3.93%  (p=0.049 n=20+17)
    Exp/P5/S2/D2-8                   4.38µs ± 0%    4.44µs ± 2%   +1.42%  (p=0.000 n=19+19)
    Exp/P10/S-4/D-10-8               1.61µs ± 0%    2.01µs ±57%  +24.95%  (p=0.000 n=17+19)
    Exp/P10/S-4/D-2-8                1.42µs ± 0%    1.49µs ± 8%   +4.24%  (p=0.000 n=19+17)
    Exp/P10/S-4/D2-8                 1.59µs ± 0%    1.62µs ± 5%     ~     (p=0.587 n=18+20)
    Exp/P10/S-4/D10-8                1.54µs ± 0%    1.54µs ± 0%   -0.28%  (p=0.000 n=16+18)
    Exp/P10/S-1/D-10-8               2.98µs ± 0%    2.97µs ± 0%   -0.17%  (p=0.002 n=20+19)
    Exp/P10/S-1/D-2-8                2.81µs ± 0%    2.81µs ± 0%     ~     (p=0.096 n=20+18)
    Exp/P10/S-1/D2-8                 2.99µs ± 0%    3.01µs ± 4%     ~     (p=0.231 n=19+19)
    Exp/P10/S-1/D10-8                2.90µs ± 0%    2.96µs ± 7%     ~     (p=0.061 n=20+17)
    Exp/P10/S2/D-10-8                6.25µs ± 0%    6.58µs ± 9%   +5.22%  (p=0.000 n=18+20)
    Exp/P10/S2/D-2-8                 6.55µs ± 1%    6.56µs ± 4%     ~     (p=0.231 n=19+19)
    Exp/P10/S2/D2-8                  6.82µs ± 1%    6.83µs ± 1%     ~     (p=0.702 n=18+18)
    Exp/P10/S2/D10-8                 6.50µs ± 0%    6.48µs ± 2%   -0.32%  (p=0.001 n=19+17)
    Exp/P100/S-4/D-100-8             50.6µs ± 5%    49.9µs ± 3%   -1.49%  (p=0.004 n=18+16)
    Exp/P100/S-4/D-10-8              50.1µs ± 3%    48.6µs ± 1%   -3.04%  (p=0.000 n=18+18)
    Exp/P100/S-4/D-2-8               50.2µs ± 3%    49.1µs ± 1%   -2.07%  (p=0.000 n=19+20)
    Exp/P100/S-4/D2-8                49.1µs ± 2%    47.5µs ± 1%   -3.19%  (p=0.000 n=18+19)
    Exp/P100/S-4/D10-8               49.7µs ± 1%    48.6µs ± 1%   -2.17%  (p=0.000 n=19+20)
    Exp/P100/S-4/D100-8              51.0µs ± 1%    50.1µs ± 2%   -1.78%  (p=0.000 n=20+20)
    Exp/P100/S-1/D-100-8             96.9µs ± 2%    95.5µs ± 1%   -1.47%  (p=0.000 n=19+17)
    Exp/P100/S-1/D-10-8              94.4µs ± 2%    92.4µs ± 1%   -2.12%  (p=0.000 n=20+19)
    Exp/P100/S-1/D-2-8               95.0µs ± 2%    93.0µs ± 1%   -2.13%  (p=0.000 n=20+16)
    Exp/P100/S-1/D2-8                97.7µs ± 1%    96.4µs ± 2%   -1.38%  (p=0.000 n=20+19)
    Exp/P100/S-1/D10-8               93.5µs ± 2%    92.3µs ± 1%   -1.32%  (p=0.000 n=20+19)
    Exp/P100/S-1/D100-8              94.9µs ± 1%    93.9µs ± 1%   -1.09%  (p=0.000 n=18+19)
    Exp/P100/S2/D-100-8               149µs ± 2%     146µs ± 1%   -2.11%  (p=0.000 n=18+18)
    Exp/P100/S2/D-10-8                147µs ± 1%     144µs ± 1%   -2.15%  (p=0.000 n=19+19)
    Exp/P100/S2/D-2-8                 153µs ± 2%     148µs ± 1%   -3.31%  (p=0.000 n=20+20)
    Exp/P100/S2/D2-8                  150µs ± 1%     145µs ± 1%   -3.15%  (p=0.000 n=19+20)
    Exp/P100/S2/D10-8                 149µs ± 1%     144µs ± 1%   -3.43%  (p=0.000 n=20+19)
    Exp/P100/S2/D100-8                146µs ± 2%     142µs ± 1%   -2.86%  (p=0.000 n=20+20)
    Ln/P2/S-100/D2-8                 6.77µs ± 1%    6.69µs ± 1%   -1.22%  (p=0.000 n=19+19)
    Ln/P2/S-10/D2-8                  6.30µs ± 0%    6.20µs ± 0%   -1.57%  (p=0.000 n=19+19)
    Ln/P2/S-2/D2-8                   5.83µs ± 1%    5.74µs ± 0%   -1.64%  (p=0.000 n=20+18)
    Ln/P2/S2/D2-8                    5.62µs ± 1%    5.52µs ± 0%   -1.76%  (p=0.000 n=18+16)
    Ln/P2/S10/D2-8                   6.49µs ± 1%    6.35µs ± 0%   -2.16%  (p=0.000 n=20+16)
    Ln/P2/S100/D2-8                  6.71µs ± 1%    6.61µs ± 0%   -1.49%  (p=0.000 n=20+18)
    Ln/P10/S-100/D2-8                11.3µs ± 1%    11.2µs ± 0%   -1.01%  (p=0.000 n=20+19)
    Ln/P10/S-100/D10-8               11.0µs ± 6%    10.8µs ± 0%   -1.51%  (p=0.000 n=17+18)
    Ln/P10/S-10/D2-8                 12.5µs ±15%    11.6µs ± 0%   -7.71%  (p=0.000 n=19+17)
    Ln/P10/S-10/D10-8                10.6µs ± 1%    10.5µs ± 0%   -1.00%  (p=0.000 n=19+17)
    Ln/P10/S-2/D2-8                  11.0µs ± 3%    10.8µs ± 0%   -1.69%  (p=0.000 n=17+20)
    Ln/P10/S-2/D10-8                 12.0µs ± 2%    11.6µs ± 0%   -3.50%  (p=0.000 n=17+18)
    Ln/P10/S2/D2-8                   10.6µs ± 2%    10.3µs ± 0%   -2.76%  (p=0.000 n=19+20)
    Ln/P10/S2/D10-8                  11.2µs ± 3%    10.7µs ± 0%   -4.22%  (p=0.000 n=18+19)
    Ln/P10/S10/D2-8                  11.8µs ± 4%    11.3µs ± 0%   -4.22%  (p=0.000 n=19+20)
    Ln/P10/S10/D10-8                 10.5µs ± 8%    10.3µs ± 1%   -2.21%  (p=0.000 n=19+18)
    Ln/P10/S100/D2-8                 12.2µs ± 1%    12.2µs ± 4%     ~     (p=0.072 n=18+18)
    Ln/P10/S100/D10-8                12.2µs ± 1%    12.5µs ± 9%   +2.71%  (p=0.030 n=20+20)
    Ln/P100/S-100/D2-8                415µs ± 1%     409µs ± 2%   -1.59%  (p=0.000 n=20+20)
    Ln/P100/S-100/D10-8               370µs ± 1%     360µs ± 1%   -2.63%  (p=0.000 n=19+19)
    Ln/P100/S-100/D100-8              394µs ± 2%     381µs ± 1%   -3.28%  (p=0.000 n=20+20)
    Ln/P100/S-10/D2-8                 399µs ± 1%     386µs ± 1%   -3.28%  (p=0.000 n=20+19)
    Ln/P100/S-10/D10-8                384µs ± 1%     372µs ± 1%   -3.02%  (p=0.000 n=18+20)
    Ln/P100/S-10/D100-8               399µs ± 1%     387µs ± 1%   -3.06%  (p=0.000 n=19+18)
    Ln/P100/S-2/D2-8                  340µs ± 2%     329µs ± 1%   -3.25%  (p=0.000 n=18+20)
    Ln/P100/S-2/D10-8                 365µs ± 1%     366µs ±10%   +0.24%  (p=0.042 n=19+18)
    Ln/P100/S-2/D100-8                401µs ± 3%     466µs ±23%  +16.32%  (p=0.000 n=19+20)
    Ln/P100/S2/D2-8                   347µs ± 2%     371µs ± 8%   +7.14%  (p=0.000 n=20+20)
    Ln/P100/S2/D10-8                  344µs ± 2%     362µs ±14%   +5.44%  (p=0.043 n=20+20)
    Ln/P100/S2/D100-8                 368µs ± 2%     361µs ± 2%   -1.93%  (p=0.000 n=19+17)
    Ln/P100/S10/D2-8                  355µs ± 1%     349µs ± 2%   -1.76%  (p=0.000 n=19+19)
    Ln/P100/S10/D10-8                 381µs ± 3%     373µs ± 4%   -1.96%  (p=0.000 n=17+20)
    Ln/P100/S10/D100-8                375µs ± 1%     363µs ± 0%   -3.15%  (p=0.000 n=20+15)
    Ln/P100/S100/D2-8                 401µs ± 1%     387µs ± 1%   -3.43%  (p=0.000 n=20+20)
    Ln/P100/S100/D10-8                354µs ± 1%     344µs ± 1%   -2.79%  (p=0.000 n=20+17)
    Ln/P100/S100/D100-8               390µs ± 2%     379µs ± 2%   -2.91%  (p=0.000 n=20+17)
    BigIntBinomial-8                  700ns ± 2%     687ns ± 1%   -1.81%  (p=0.000 n=19+18)
    BigIntQuoRem-8                    917ns ± 0%     912ns ± 1%   -0.56%  (p=0.000 n=17+20)
    BigIntExp-8                      2.72ms ± 0%    2.71ms ± 0%   -0.46%  (p=0.000 n=20+18)
    BigIntExp2-8                     2.72ms ± 0%    2.70ms ± 0%   -0.44%  (p=0.000 n=19+19)
    BigIntBitset-8                   10.0ns ± 0%     9.9ns ± 1%   -0.47%  (p=0.000 n=19+17)
    BigIntBitsetNeg-8                22.9ns ± 0%    22.7ns ± 0%   -0.57%  (p=0.000 n=18+19)
    BigIntBitsetOrig-8               50.5ns ± 1%    49.3ns ± 5%   -2.36%  (p=0.000 n=20+20)
    BigIntBitsetNegOrig-8            94.7ns ± 1%    92.2ns ± 1%   -2.60%  (p=0.000 n=18+20)
    BigIntModInverse-8                628ns ± 2%     600ns ± 2%   -4.50%  (p=0.000 n=20+20)
    BigIntSqrt-8                     12.6µs ± 1%    12.6µs ± 5%   +0.08%  (p=0.020 n=20+19)
    BigIntDiv/20/10-8                24.1ns ± 0%    24.1ns ± 1%     ~     (p=0.350 n=19+17)
    BigIntDiv/40/20-8                24.1ns ± 0%    24.0ns ± 0%   -0.38%  (p=0.000 n=20+18)
    BigIntDiv/100/50-8               33.3ns ± 0%    33.2ns ± 1%   -0.34%  (p=0.000 n=19+18)
    BigIntDiv/200/100-8               118ns ± 0%     117ns ± 0%   -0.45%  (p=0.000 n=19+18)
    BigIntDiv/400/200-8               136ns ± 0%     135ns ± 0%   -0.45%  (p=0.000 n=18+19)
    BigIntDiv/1000/500-8              254ns ± 0%     253ns ± 0%   -0.41%  (p=0.000 n=17+19)
    BigIntDiv/2000/1000-8             558ns ±20%     522ns ± 1%   -6.52%  (p=0.000 n=18+19)
    BigIntDiv/20000/10000-8          16.9µs ± 1%    16.7µs ± 0%   -1.14%  (p=0.000 n=18+18)
    BigIntDiv/200000/100000-8         621µs ± 0%     619µs ± 1%   -0.32%  (p=0.000 n=19+20)
    BigIntDiv/2000000/1000000-8      25.5ms ± 1%    25.3ms ± 0%   -0.65%  (p=0.000 n=17+18)
    BigIntDiv/20000000/10000000-8     963ms ± 1%     962ms ± 3%   -0.02%  (p=0.005 n=16+18)
    GDA/abs-8                        4.06µs ± 2%    4.02µs ± 1%   -0.83%  (p=0.000 n=19+19)
    GDA/add-8                         486µs ± 1%     480µs ± 2%   -1.30%  (p=0.000 n=19+20)
    GDA/base-8                       84.8µs ± 1%    84.3µs ± 1%   -0.54%  (p=0.000 n=20+18)
    GDA/compare-8                    23.7µs ± 1%    23.6µs ± 1%   -0.67%  (p=0.001 n=20+18)
    GDA/comparetotal-8               22.7µs ± 2%    22.8µs ± 1%     ~     (p=0.077 n=20+20)
    GDA/divide-8                     50.1µs ± 1%    49.4µs ± 1%   -1.33%  (p=0.000 n=20+20)
    GDA/divideint-8                  12.4µs ± 5%    12.2µs ± 2%   -2.10%  (p=0.000 n=18+20)
    GDA/exp-8                        12.4ms ± 6%    12.0ms ± 1%   -2.89%  (p=0.000 n=19+20)
    GDA/ln-8                         13.2ms ± 2%    13.0ms ± 1%   -1.27%  (p=0.000 n=18+19)
    GDA/log10-8                      17.8ms ± 3%    16.6ms ± 2%   -6.85%  (p=0.000 n=15+19)
    GDA/minus-8                      5.72µs ± 3%    5.56µs ± 1%   -2.89%  (p=0.000 n=18+18)
    GDA/multiply-8                   47.0µs ± 8%    44.4µs ± 1%   -5.55%  (p=0.000 n=18+19)
    GDA/plus-8                       31.7µs ± 2%    31.0µs ± 1%   -2.37%  (p=0.000 n=17+19)
    GDA/power-8                      37.1ms ± 2%    36.0ms ± 1%   -2.81%  (p=0.000 n=20+20)
    GDA/powersqrt-8                  53.6ms ± 1%    52.8ms ± 0%   -1.40%  (p=0.000 n=18+19)
    GDA/quantize-8                   62.6µs ± 1%    62.2µs ± 1%   -0.64%  (p=0.000 n=19+18)
    GDA/randoms-8                    1.03ms ± 0%    1.03ms ± 1%   -0.41%  (p=0.000 n=19+19)
    GDA/reduce-8                     9.08µs ± 1%    9.01µs ± 1%   -0.85%  (p=0.000 n=17+20)
    GDA/remainder-8                  24.5µs ± 2%    24.3µs ± 1%   -0.64%  (p=0.012 n=19+20)
    GDA/rounding-8                    135µs ± 1%     134µs ± 0%   -1.15%  (p=0.000 n=19+20)
    GDA/squareroot-8                 3.98ms ± 0%    3.94ms ± 0%   -0.95%  (p=0.000 n=19+20)
    GDA/subtract-8                   73.2µs ± 1%    72.4µs ± 1%   -1.11%  (p=0.000 n=17+17)
    GDA/tointegral-8                 14.7µs ± 2%    14.6µs ± 3%   -0.67%  (p=0.029 n=18+18)
    GDA/tointegralx-8                15.1µs ± 2%    15.4µs ± 5%   +1.49%  (p=0.044 n=18+20)
    GDA/cuberoot-apd-8                323µs ± 2%     327µs ± 1%   +1.23%  (p=0.000 n=19+20)
    NumDigitsLookup-8                7.84µs ± 1%    7.96µs ± 4%     ~     (p=0.081 n=16+20)
    NumDigitsFull-8                  2.15ms ± 1%    2.12ms ± 1%   -1.43%  (p=0.000 n=19+19)
    
    name                           old alloc/op   new alloc/op   delta
    Exp/P5/S-4/D-2-8                   152B ± 0%      152B ± 0%     ~     (all equal)
    Exp/P5/S-4/D2-8                    152B ± 0%      152B ± 0%     ~     (all equal)
    Exp/P5/S-1/D-2-8                   112B ± 0%      112B ± 0%     ~     (all equal)
    Exp/P5/S-1/D2-8                    112B ± 0%      112B ± 0%     ~     (all equal)
    Exp/P5/S2/D-2-8                    112B ± 0%      112B ± 0%     ~     (all equal)
    Exp/P5/S2/D2-8                     112B ± 0%      112B ± 0%     ~     (all equal)
    Exp/P10/S-4/D-10-8                 224B ± 0%      224B ± 0%     ~     (all equal)
    Exp/P10/S-4/D-2-8                  168B ± 0%      168B ± 0%     ~     (all equal)
    Exp/P10/S-4/D2-8                   168B ± 0%      168B ± 0%     ~     (all equal)
    Exp/P10/S-4/D10-8                  224B ± 0%      224B ± 0%     ~     (all equal)
    Exp/P10/S-1/D-10-8                 224B ± 0%      224B ± 0%     ~     (all equal)
    Exp/P10/S-1/D-2-8                  168B ± 0%      168B ± 0%     ~     (all equal)
    Exp/P10/S-1/D2-8                   168B ± 0%      168B ± 0%     ~     (all equal)
    Exp/P10/S-1/D10-8                  224B ± 0%      224B ± 0%     ~     (all equal)
    Exp/P10/S2/D-10-8                  224B ± 0%      224B ± 0%     ~     (all equal)
    Exp/P10/S2/D-2-8                   168B ± 0%      168B ± 0%     ~     (all equal)
    Exp/P10/S2/D2-8                    168B ± 0%      168B ± 0%     ~     (all equal)
    Exp/P10/S2/D10-8                   224B ± 0%      224B ± 0%     ~     (all equal)
    Exp/P100/S-4/D-100-8             51.7kB ± 0%    51.7kB ± 0%   -0.00%  (p=0.000 n=18+19)
    Exp/P100/S-4/D-10-8              51.0kB ± 0%    51.0kB ± 0%   -0.00%  (p=0.004 n=19+20)
    Exp/P100/S-4/D-2-8               52.0kB ± 0%    52.0kB ± 0%     ~     (p=0.759 n=20+19)
    Exp/P100/S-4/D2-8                47.2kB ± 0%    47.2kB ± 0%     ~     (p=0.115 n=19+20)
    Exp/P100/S-4/D10-8               48.5kB ± 0%    48.5kB ± 0%     ~     (p=0.383 n=20+20)
    Exp/P100/S-4/D100-8              49.6kB ± 0%    49.6kB ± 0%     ~     (p=1.000 n=20+20)
    Exp/P100/S-1/D-100-8              101kB ± 0%     101kB ± 0%     ~     (p=0.203 n=20+20)
    Exp/P100/S-1/D-10-8              98.0kB ± 0%    98.0kB ± 0%     ~     (p=0.115 n=20+20)
    Exp/P100/S-1/D-2-8               99.3kB ± 0%    99.3kB ± 0%     ~     (p=0.605 n=20+20)
    Exp/P100/S-1/D2-8                96.0kB ± 0%    96.0kB ± 0%     ~     (p=0.059 n=18+20)
    Exp/P100/S-1/D10-8               92.0kB ± 0%    92.0kB ± 0%     ~     (p=0.498 n=20+20)
    Exp/P100/S-1/D100-8              93.5kB ± 0%    93.5kB ± 0%   -0.01%  (p=0.048 n=20+20)
    Exp/P100/S2/D-100-8               147kB ± 0%     147kB ± 0%     ~     (p=0.490 n=20+20)
    Exp/P100/S2/D-10-8                145kB ± 0%     145kB ± 0%     ~     (p=0.606 n=20+20)
    Exp/P100/S2/D-2-8                 150kB ± 0%     150kB ± 0%     ~     (p=0.917 n=20+19)
    Exp/P100/S2/D2-8                  138kB ± 0%     138kB ± 0%     ~     (p=0.784 n=20+20)
    Exp/P100/S2/D10-8                 138kB ± 0%     138kB ± 0%   -0.01%  (p=0.047 n=20+20)
    Exp/P100/S2/D100-8                134kB ± 0%     134kB ± 0%     ~     (p=0.525 n=20+20)
    Ln/P2/S-100/D2-8                 1.21kB ± 0%    1.18kB ± 0%   -2.39%  (p=0.000 n=20+20)
    Ln/P2/S-10/D2-8                    644B ± 0%      614B ± 0%   -4.66%  (p=0.000 n=20+20)
    Ln/P2/S-2/D2-8                     606B ± 0%      578B ± 0%   -4.69%  (p=0.000 n=20+20)
    Ln/P2/S2/D2-8                      581B ± 0%      554B ± 0%   -4.65%  (p=0.000 n=20+19)
    Ln/P2/S10/D2-8                     645B ± 0%      615B ± 0%   -4.65%  (p=0.000 n=20+20)
    Ln/P2/S100/D2-8                  1.21kB ± 0%    1.18kB ± 0%   -2.40%  (p=0.000 n=20+20)
    Ln/P10/S-100/D2-8                1.35kB ± 0%    1.32kB ± 0%   -2.00%  (p=0.000 n=20+20)
    Ln/P10/S-100/D10-8               1.39kB ± 0%    1.37kB ± 0%   -1.37%  (p=0.000 n=20+20)
    Ln/P10/S-10/D2-8                   825B ± 0%      794B ± 0%   -3.76%  (p=0.000 n=20+20)
    Ln/P10/S-10/D10-8                  863B ± 0%      844B ± 0%   -2.20%  (p=0.000 n=20+20)
    Ln/P10/S-2/D2-8                    777B ± 0%      748B ± 0%   -3.73%  (p=0.000 n=20+20)
    Ln/P10/S-2/D10-8                   837B ± 0%      816B ± 0%   -2.51%  (p=0.000 n=20+20)
    Ln/P10/S2/D2-8                     780B ± 0%      753B ± 0%   -3.46%  (p=0.000 n=20+20)
    Ln/P10/S2/D10-8                    839B ± 0%      818B ± 0%   -2.50%  (p=0.000 n=20+20)
    Ln/P10/S10/D2-8                    818B ± 0%      788B ± 0%   -3.67%  (p=0.000 n=20+20)
    Ln/P10/S10/D10-8                   789B ± 0%      770B ± 0%   -2.41%  (p=0.000 n=20+20)
    Ln/P10/S100/D2-8                 1.43kB ± 0%    1.40kB ± 0%   -2.16%  (p=0.000 n=20+20)
    Ln/P10/S100/D10-8                1.48kB ± 0%    1.45kB ± 0%   -1.49%  (p=0.000 n=19+20)
    Ln/P100/S-100/D2-8                407kB ± 0%     407kB ± 0%     ~     (p=0.125 n=19+20)
    Ln/P100/S-100/D10-8               363kB ± 0%     363kB ± 0%   -0.03%  (p=0.015 n=20+19)
    Ln/P100/S-100/D100-8              384kB ± 0%     383kB ± 0%   -0.03%  (p=0.035 n=20+16)
    Ln/P100/S-10/D2-8                 391kB ± 0%     391kB ± 0%   +0.06%  (p=0.000 n=16+20)
    Ln/P100/S-10/D10-8                374kB ± 0%     374kB ± 0%   +0.07%  (p=0.000 n=17+18)
    Ln/P100/S-10/D100-8               387kB ± 0%     386kB ± 0%   -0.09%  (p=0.000 n=20+20)
    Ln/P100/S-2/D2-8                  330kB ± 0%     330kB ± 0%   -0.08%  (p=0.012 n=20+20)
    Ln/P100/S-2/D10-8                 357kB ± 0%     357kB ± 0%     ~     (p=0.941 n=20+20)
    Ln/P100/S-2/D100-8                390kB ± 0%     390kB ± 0%     ~     (p=0.457 n=20+20)
    Ln/P100/S2/D2-8                   343kB ± 0%     343kB ± 0%     ~     (p=0.920 n=20+20)
    Ln/P100/S2/D10-8                  336kB ± 0%     336kB ± 0%     ~     (p=0.774 n=20+20)
    Ln/P100/S2/D100-8                 358kB ± 0%     359kB ± 0%     ~     (p=0.324 n=20+20)
    Ln/P100/S10/D2-8                  348kB ± 0%     348kB ± 0%     ~     (p=0.650 n=15+19)
    Ln/P100/S10/D10-8                 372kB ± 0%     372kB ± 0%     ~     (p=0.229 n=20+20)
    Ln/P100/S10/D100-8                365kB ± 0%     365kB ± 0%   +0.16%  (p=0.000 n=20+20)
    Ln/P100/S100/D2-8                 390kB ± 0%     389kB ± 0%   -0.04%  (p=0.012 n=19+20)
    Ln/P100/S100/D10-8                351kB ± 0%     350kB ± 0%   -0.20%  (p=0.001 n=20+20)
    Ln/P100/S100/D100-8               384kB ± 0%     384kB ± 0%     ~     (p=0.654 n=20+20)
    BigIntBinomial-8                 1.02kB ± 0%    1.02kB ± 0%     ~     (all equal)
    BigIntQuoRem-8                    0.00B          0.00B          ~     (all equal)
    BigIntExp-8                      11.1kB ± 0%    11.1kB ± 0%     ~     (p=0.979 n=20+20)
    BigIntExp2-8                     11.3kB ± 0%    11.3kB ± 0%     ~     (p=0.083 n=20+17)
    BigIntBitset-8                    0.00B          0.00B          ~     (all equal)
    BigIntBitsetNeg-8                 0.00B          0.00B          ~     (all equal)
    BigIntBitsetOrig-8                71.0B ± 0%     71.0B ± 0%     ~     (all equal)
    BigIntBitsetNegOrig-8              183B ± 0%      183B ± 0%     ~     (all equal)
    BigIntModInverse-8               1.28kB ± 0%    1.28kB ± 0%     ~     (all equal)
    BigIntSqrt-8                     5.54kB ± 0%    5.54kB ± 0%     ~     (all equal)
    BigIntDiv/20/10-8                 0.00B          0.00B          ~     (all equal)
    BigIntDiv/40/20-8                 0.00B          0.00B          ~     (all equal)
    BigIntDiv/100/50-8                0.00B          0.00B          ~     (all equal)
    BigIntDiv/200/100-8               0.00B          0.00B          ~     (all equal)
    BigIntDiv/400/200-8               0.00B          0.00B          ~     (all equal)
    BigIntDiv/1000/500-8              0.00B          0.00B          ~     (all equal)
    BigIntDiv/2000/1000-8             0.00B          0.00B          ~     (all equal)
    BigIntDiv/20000/10000-8            129B ± 0%      129B ± 0%     ~     (all equal)
    BigIntDiv/200000/100000-8          804B ± 0%      713B ±48%  -11.34%  (p=0.000 n=16+20)
    BigIntDiv/2000000/1000000-8       228kB ±58%     301kB ±91%     ~     (p=0.106 n=16+20)
    BigIntDiv/20000000/10000000-8    18.0MB ± 0%     9.3MB ±40%  -48.45%  (p=0.000 n=13+19)
    GDA/abs-8                         0.00B          0.00B          ~     (all equal)
    GDA/add-8                         459kB ± 0%     459kB ± 0%     ~     (p=0.147 n=20+20)
    GDA/base-8                       26.2kB ± 0%    26.2kB ± 0%     ~     (all equal)
    GDA/compare-8                     0.00B          0.00B          ~     (all equal)
    GDA/comparetotal-8                0.00B          0.00B          ~     (all equal)
    GDA/divide-8                     10.6kB ± 0%    10.6kB ± 0%     ~     (all equal)
    GDA/divideint-8                   96.0B ± 0%     96.0B ± 0%     ~     (all equal)
    GDA/exp-8                        11.2MB ± 0%    11.2MB ± 0%     ~     (p=0.947 n=20+20)
    GDA/ln-8                         8.73MB ± 0%    8.73MB ± 0%   -0.07%  (p=0.000 n=19+20)
    GDA/log10-8                      11.4MB ± 0%    11.4MB ± 0%   -0.06%  (p=0.000 n=19+20)
    GDA/minus-8                       0.00B          0.00B          ~     (all equal)
    GDA/multiply-8                   15.7kB ± 0%    15.7kB ± 0%     ~     (all equal)
    GDA/plus-8                       41.0kB ± 0%    41.0kB ± 0%     ~     (all equal)
    GDA/power-8                      24.1MB ± 0%    24.1MB ± 0%   -0.04%  (p=0.000 n=20+20)
    GDA/powersqrt-8                  6.65MB ± 0%    6.58MB ± 0%   -1.17%  (p=0.000 n=20+19)
    GDA/quantize-8                   9.20kB ± 0%    9.20kB ± 0%     ~     (p=1.000 n=20+20)
    GDA/randoms-8                    48.5kB ± 0%    48.5kB ± 0%     ~     (all equal)
    GDA/reduce-8                      0.00B          0.00B          ~     (all equal)
    GDA/remainder-8                   96.0B ± 0%     96.0B ± 0%     ~     (all equal)
    GDA/rounding-8                   3.07kB ± 0%    3.07kB ± 0%     ~     (all equal)
    GDA/squareroot-8                  170kB ± 0%     170kB ± 0%     ~     (p=0.607 n=20+20)
    GDA/subtract-8                   35.2kB ± 0%    35.2kB ± 0%     ~     (p=0.063 n=20+20)
    GDA/tointegral-8                 6.58kB ± 0%    6.58kB ± 0%     ~     (all equal)
    GDA/tointegralx-8                6.58kB ± 0%    6.58kB ± 0%     ~     (all equal)
    GDA/cuberoot-apd-8                124kB ± 0%     124kB ± 0%   -0.00%  (p=0.000 n=20+20)
    NumDigitsLookup-8                 0.00B          0.00B          ~     (all equal)
    NumDigitsFull-8                  3.13MB ± 0%    3.13MB ± 0%     ~     (p=0.564 n=20+19)
    
    name                           old allocs/op  new allocs/op  delta
    Exp/P5/S-4/D-2-8                   12.0 ± 0%      12.0 ± 0%     ~     (all equal)
    Exp/P5/S-4/D2-8                    12.0 ± 0%      12.0 ± 0%     ~     (all equal)
    Exp/P5/S-1/D-2-8                   11.0 ± 0%      11.0 ± 0%     ~     (all equal)
    Exp/P5/S-1/D2-8                    11.0 ± 0%      11.0 ± 0%     ~     (all equal)
    Exp/P5/S2/D-2-8                    11.0 ± 0%      11.0 ± 0%     ~     (all equal)
    Exp/P5/S2/D2-8                     11.0 ± 0%      11.0 ± 0%     ~     (all equal)
    Exp/P10/S-4/D-10-8                 13.0 ± 0%      13.0 ± 0%     ~     (all equal)
    Exp/P10/S-4/D-2-8                  12.0 ± 0%      12.0 ± 0%     ~     (all equal)
    Exp/P10/S-4/D2-8                   12.0 ± 0%      12.0 ± 0%     ~     (all equal)
    Exp/P10/S-4/D10-8                  13.0 ± 0%      13.0 ± 0%     ~     (all equal)
    Exp/P10/S-1/D-10-8                 13.0 ± 0%      13.0 ± 0%     ~     (all equal)
    Exp/P10/S-1/D-2-8                  12.0 ± 0%      12.0 ± 0%     ~     (all equal)
    Exp/P10/S-1/D2-8                   12.0 ± 0%      12.0 ± 0%     ~     (all equal)
    Exp/P10/S-1/D10-8                  13.0 ± 0%      13.0 ± 0%     ~     (all equal)
    Exp/P10/S2/D-10-8                  13.0 ± 0%      13.0 ± 0%     ~     (all equal)
    Exp/P10/S2/D-2-8                   12.0 ± 0%      12.0 ± 0%     ~     (all equal)
    Exp/P10/S2/D2-8                    12.0 ± 0%      12.0 ± 0%     ~     (all equal)
    Exp/P10/S2/D10-8                   13.0 ± 0%      13.0 ± 0%     ~     (all equal)
    Exp/P100/S-4/D-100-8                692 ± 0%       692 ± 0%     ~     (all equal)
    Exp/P100/S-4/D-10-8                 690 ± 0%       690 ± 0%     ~     (all equal)
    Exp/P100/S-4/D-2-8                  701 ± 0%       701 ± 0%     ~     (all equal)
    Exp/P100/S-4/D2-8                   652 ± 0%       652 ± 0%     ~     (all equal)
    Exp/P100/S-4/D10-8                  675 ± 0%       675 ± 0%     ~     (all equal)
    Exp/P100/S-4/D100-8                 684 ± 0%       684 ± 0%     ~     (all equal)
    Exp/P100/S-1/D-100-8              1.35k ± 0%     1.35k ± 0%     ~     (all equal)
    Exp/P100/S-1/D-10-8               1.32k ± 0%     1.32k ± 0%     ~     (all equal)
    Exp/P100/S-1/D-2-8                1.33k ± 0%     1.33k ± 0%     ~     (all equal)
    Exp/P100/S-1/D2-8                 1.33k ± 0%     1.33k ± 0%     ~     (all equal)
    Exp/P100/S-1/D10-8                1.28k ± 0%     1.28k ± 0%     ~     (all equal)
    Exp/P100/S-1/D100-8               1.29k ± 0%     1.29k ± 0%     ~     (all equal)
    Exp/P100/S2/D-100-8               2.00k ± 0%     2.00k ± 0%     ~     (p=0.741 n=20+20)
    Exp/P100/S2/D-10-8                1.98k ± 0%     1.98k ± 0%   -0.01%  (p=0.023 n=16+20)
    Exp/P100/S2/D-2-8                 2.05k ± 0%     2.05k ± 0%     ~     (all equal)
    Exp/P100/S2/D2-8                  1.94k ± 0%     1.94k ± 0%     ~     (p=0.731 n=20+20)
    Exp/P100/S2/D10-8                 1.95k ± 0%     1.95k ± 0%     ~     (all equal)
    Exp/P100/S2/D100-8                1.89k ± 0%     1.89k ± 0%     ~     (p=0.320 n=20+20)
    Ln/P2/S-100/D2-8                   44.0 ± 0%      42.0 ± 0%   -4.55%  (p=0.000 n=20+20)
    Ln/P2/S-10/D2-8                    36.0 ± 0%      34.0 ± 0%   -5.56%  (p=0.000 n=20+20)
    Ln/P2/S-2/D2-8                     34.0 ± 0%      32.0 ± 0%   -5.88%  (p=0.000 n=20+20)
    Ln/P2/S2/D2-8                      32.0 ± 0%      30.0 ± 0%   -6.25%  (p=0.000 n=20+20)
    Ln/P2/S10/D2-8                     36.0 ± 0%      34.0 ± 0%   -5.56%  (p=0.000 n=20+20)
    Ln/P2/S100/D2-8                    44.0 ± 0%      42.0 ± 0%   -4.55%  (p=0.000 n=20+20)
    Ln/P10/S-100/D2-8                  45.0 ± 0%      44.0 ± 0%   -2.22%  (p=0.000 n=20+20)
    Ln/P10/S-100/D10-8                 44.0 ± 0%      43.0 ± 0%   -2.27%  (p=0.000 n=20+20)
    Ln/P10/S-10/D2-8                   39.0 ± 0%      37.0 ± 0%   -5.13%  (p=0.000 n=20+20)
    Ln/P10/S-10/D10-8                  36.0 ± 0%      35.0 ± 0%   -2.78%  (p=0.000 n=20+20)
    Ln/P10/S-2/D2-8                    37.0 ± 0%      35.0 ± 0%   -5.41%  (p=0.000 n=20+20)
    Ln/P10/S-2/D10-8                   38.0 ± 0%      36.0 ± 0%   -5.26%  (p=0.000 n=20+20)
    Ln/P10/S2/D2-8                     36.0 ± 0%      34.0 ± 0%   -5.56%  (p=0.000 n=20+20)
    Ln/P10/S2/D10-8                    38.0 ± 0%      36.0 ± 0%   -5.26%  (p=0.000 n=20+20)
    Ln/P10/S10/D2-8                    39.0 ± 0%      37.0 ± 0%   -5.13%  (p=0.000 n=20+20)
    Ln/P10/S10/D10-8                   35.0 ± 0%      33.0 ± 0%   -5.71%  (p=0.000 n=20+20)
    Ln/P10/S100/D2-8                   49.0 ± 0%      47.0 ± 0%   -4.08%  (p=0.000 n=20+20)
    Ln/P10/S100/D10-8                  50.0 ± 0%      48.0 ± 0%   -4.00%  (p=0.000 n=20+20)
    Ln/P100/S-100/D2-8                5.55k ± 0%     5.55k ± 0%     ~     (p=0.291 n=19+20)
    Ln/P100/S-100/D10-8               4.94k ± 0%     4.94k ± 0%   -0.07%  (p=0.002 n=20+20)
    Ln/P100/S-100/D100-8              5.21k ± 0%     5.21k ± 0%   -0.06%  (p=0.000 n=20+16)
    Ln/P100/S-10/D2-8                 5.30k ± 0%     5.31k ± 0%     ~     (p=0.063 n=18+20)
    Ln/P100/S-10/D10-8                5.09k ± 0%     5.09k ± 0%   +0.04%  (p=0.000 n=17+17)
    Ln/P100/S-10/D100-8               5.27k ± 0%     5.27k ± 0%   -0.12%  (p=0.000 n=20+20)
    Ln/P100/S-2/D2-8                  4.51k ± 0%     4.51k ± 0%   -0.11%  (p=0.003 n=20+20)
    Ln/P100/S-2/D10-8                 4.86k ± 0%     4.86k ± 0%     ~     (p=0.671 n=20+20)
    Ln/P100/S-2/D100-8                5.30k ± 0%     5.29k ± 0%     ~     (p=0.165 n=20+20)
    Ln/P100/S2/D2-8                   4.64k ± 0%     4.63k ± 0%     ~     (p=0.096 n=20+20)
    Ln/P100/S2/D10-8                  4.59k ± 0%     4.58k ± 0%     ~     (p=0.196 n=20+20)
    Ln/P100/S2/D100-8                 4.89k ± 0%     4.89k ± 0%     ~     (p=0.962 n=20+20)
    Ln/P100/S10/D2-8                  4.73k ± 0%     4.73k ± 0%   -0.05%  (p=0.000 n=15+15)
    Ln/P100/S10/D10-8                 5.07k ± 0%     5.06k ± 0%   -0.07%  (p=0.049 n=20+20)
    Ln/P100/S10/D100-8                4.98k ± 0%     4.98k ± 0%   +0.13%  (p=0.000 n=20+20)
    Ln/P100/S100/D2-8                 5.31k ± 0%     5.31k ± 0%   -0.08%  (p=0.000 n=19+20)
    Ln/P100/S100/D10-8                4.78k ± 0%     4.76k ± 0%   -0.21%  (p=0.000 n=20+19)
    Ln/P100/S100/D100-8               5.21k ± 0%     5.21k ± 0%     ~     (p=0.163 n=20+20)
    BigIntBinomial-8                   38.0 ± 0%      38.0 ± 0%     ~     (all equal)
    BigIntQuoRem-8                     0.00           0.00          ~     (all equal)
    BigIntExp-8                        21.0 ± 0%      21.0 ± 0%     ~     (all equal)
    BigIntExp2-8                       22.0 ± 0%      22.0 ± 0%     ~     (all equal)
    BigIntBitset-8                     0.00           0.00          ~     (all equal)
    BigIntBitsetNeg-8                  0.00           0.00          ~     (all equal)
    BigIntBitsetOrig-8                 0.00           0.00          ~     (all equal)
    BigIntBitsetNegOrig-8              1.00 ± 0%      1.00 ± 0%     ~     (all equal)
    BigIntModInverse-8                 11.0 ± 0%      11.0 ± 0%     ~     (all equal)
    BigIntSqrt-8                       12.0 ± 0%      12.0 ± 0%     ~     (all equal)
    BigIntDiv/20/10-8                  0.00           0.00          ~     (all equal)
    BigIntDiv/40/20-8                  0.00           0.00          ~     (all equal)
    BigIntDiv/100/50-8                 0.00           0.00          ~     (all equal)
    BigIntDiv/200/100-8                0.00           0.00          ~     (all equal)
    BigIntDiv/400/200-8                0.00           0.00          ~     (all equal)
    BigIntDiv/1000/500-8               0.00           0.00          ~     (all equal)
    BigIntDiv/2000/1000-8              0.00           0.00          ~     (all equal)
    BigIntDiv/20000/10000-8            1.00 ± 0%      1.00 ± 0%     ~     (all equal)
    BigIntDiv/200000/100000-8          1.00 ± 0%      1.00 ± 0%     ~     (all equal)
    BigIntDiv/2000000/1000000-8        5.25 ±90%      5.25 ±52%     ~     (p=0.828 n=20+20)
    BigIntDiv/20000000/10000000-8      78.0 ± 0%      24.2 ±17%  -68.93%  (p=0.000 n=14+17)
    GDA/abs-8                          0.00           0.00          ~     (all equal)
    GDA/add-8                         4.21k ± 0%     4.21k ± 0%     ~     (all equal)
    GDA/base-8                        2.52k ± 0%     2.52k ± 0%     ~     (all equal)
    GDA/compare-8                      0.00           0.00          ~     (all equal)
    GDA/comparetotal-8                 0.00           0.00          ~     (all equal)
    GDA/divide-8                        213 ± 0%       213 ± 0%     ~     (all equal)
    GDA/divideint-8                    2.00 ± 0%      2.00 ± 0%     ~     (all equal)
    GDA/exp-8                          144k ± 0%      144k ± 0%     ~     (p=0.909 n=20+20)
    GDA/ln-8                           169k ± 0%      169k ± 0%   -0.31%  (p=0.000 n=18+20)
    GDA/log10-8                        220k ± 0%      220k ± 0%   -0.28%  (p=0.000 n=19+20)
    GDA/minus-8                        0.00           0.00          ~     (all equal)
    GDA/multiply-8                      312 ± 0%       312 ± 0%     ~     (all equal)
    GDA/plus-8                          260 ± 0%       260 ± 0%     ~     (all equal)
    GDA/power-8                        473k ± 0%      472k ± 0%   -0.16%  (p=0.000 n=20+20)
    GDA/powersqrt-8                    217k ± 0%      212k ± 0%   -2.27%  (p=0.000 n=20+19)
    GDA/quantize-8                     53.0 ± 0%      53.0 ± 0%     ~     (all equal)
    GDA/randoms-8                       704 ± 0%       704 ± 0%     ~     (all equal)
    GDA/reduce-8                       0.00           0.00          ~     (all equal)
    GDA/remainder-8                    2.00 ± 0%      2.00 ± 0%     ~     (all equal)
    GDA/rounding-8                     96.0 ± 0%      96.0 ± 0%     ~     (all equal)
    GDA/squareroot-8                  4.18k ± 0%     4.18k ± 0%     ~     (all equal)
    GDA/subtract-8                      210 ± 0%       210 ± 0%     ~     (all equal)
    GDA/tointegral-8                   48.0 ± 0%      48.0 ± 0%     ~     (all equal)
    GDA/tointegralx-8                  48.0 ± 0%      48.0 ± 0%     ~     (all equal)
    GDA/cuberoot-apd-8                2.48k ± 0%     2.48k ± 0%     ~     (all equal)
    NumDigitsLookup-8                  0.00           0.00          ~     (all equal)
    NumDigitsFull-8                   23.4k ± 0%     23.4k ± 0%     ~     (p=1.000 n=20+20)
    
  • Thank you!

    Thank you!

    Just wanted to say thank you for the v3 release. The performance improvements are noticeable.

    Benchmarks from my package (bojanz/currency) where the average coefficient is small:

    Results for apd v2:

    BenchmarkAdd-12       	 2718600	       435.0 ns/op	     128 B/op	       3 allocs/op
    BenchmarkSub-12       	 3016879	       405.6 ns/op	      88 B/op	       3 allocs/op
    BenchmarkMul-12       	 1447600	       820.8 ns/op	     168 B/op	       5 allocs/op
    BenchmarkMulDec-12       1000000	      1055 ns/op	     184 B/op	       7 allocs/op
    BenchmarkDiv-12       	  465312	      2928 ns/op	     336 B/op	      11 allocs/op
    BenchmarkDivDec-12    	  355858	      3116 ns/op	     352 B/op	      13 allocs/op
    BenchmarkRound-12     	  870912	      1228 ns/op	     296 B/op	      11 allocs/op
    BenchmarkCmp-12       	85437470	        15.05 ns/op	       0 B/op	       0 allocs/op
    

    Results for apd v3:

    BenchmarkAdd-12       	 4333795	       286.2 ns/op	      64 B/op	       2 allocs/op
    BenchmarkSub-12       	 4126153	       298.8 ns/op	      64 B/op	       2 allocs/op
    BenchmarkMul-12       	 1990543	       625.5 ns/op	      96 B/op	       3 allocs/op
    BenchmarkMulDec-12      1366653	       863.4 ns/op	     112 B/op	       5 allocs/op
    BenchmarkDiv-12       	  627897	      1614 ns/op	      96 B/op	       3 allocs/op
    BenchmarkDivDec-12    	  889832	      1317 ns/op	     112 B/op	       5 allocs/op
    BenchmarkRound-12     	 2020036	       574.0 ns/op	      64 B/op	       2 allocs/op
    BenchmarkCmp-12       	84142538	        14.26 ns/op	       0 B/op	       0 allocs/op
    
  • [DNM] apd: embed small coefficient values in Decimal struct

    [DNM] apd: embed small coefficient values in Decimal struct

    Replaces cockroachdb/cockroach#74369. Replaces https://github.com/cockroachdb/apd/pull/101.

    This commit introduces a performance optimization that embeds small coefficient values directly in their Decimal struct, instead of storing these values in a separate heap allocation. It does so by replacing math/big.Int with a new wrapper type called BigInt that provides an "inline" compact representation optimization.

    Each BigInt maintains (through big.Int) an internal reference to a variable-length integer value, which is represented by a []big.Word. The _inline field and lazyInit method combine to allow BigInt to inline this variable-length integer array within the BigInt struct when its value is sufficiently small. In lazyInit, we point the _inner field's slice at the _inline array. big.Int will avoid re-allocating this array until it is provided with a value that exceeds the initial capacity.

    We set the capacity of the inline array to accommodate any value that would fit in a 128-bit integer (i.e. values up to 2^128 - 1).

    This is an alternative to an optimization that many other arbitrary precision decimal libraries have where small coefficient values are stored as numeric fields in their data type's struct. Only when this coefficient value gets large enough do these libraries fall back to a variable-length coefficient with internal indirection. We can see the optimization in practice in the ericlagergren/decimal library, where each struct contains a compact uint64 and an unscaled big.Int. Prior concern from the authors of cockroachdb/apd regarding this form of compact representation optimization was that it traded performance for complexity. The optimization fractured control flow, leaking out across the library and leading to more complex, error-prone code.

    The approach taken in this commit does not have the same issue. All arithmetic on the decimal's coefficient is still deferred to big.Int. In fact, the entire optimization is best-effort, and bugs that lead to missed calls to lazyInit are merely missed opportunities to avoid a heap allocation, and nothing more serious.

    However, one major complication with this approach is that Go's escape analysis struggles with understanding self-referential pointers. A naive implementation of this optimization would force all BigInt structs to escape to the heap. To work around this, we employ a similar trick to sync.Cond and strings.Builder. We trick escape analysis to allow the self-referential pointer without causing the struct to escape.

    This works but it introduces complexity if BigInt structs are copied by value. So to avoid nasty bugs, we disallow copying of BigInt structs. The self-referencing pointer from _inner to _inline makes this unsafe, as it could allow aliasing between two BigInt structs which would be hidden from escape analysis. If the first BigInt then fell out of scope and was GCed, this could corrupt the state of the second BigInt. sync.Cond and strings.Builder also prevent copying to avoid this kind of issue. In fact, big.Int itself says that "shallow copies are not supported and may lead to errors", but it doesn't enforce this.

    Impact on benchmarks:

    name                 old time/op    new time/op    delta
    GDA/comparetotal-10    46.3µs ± 0%    24.4µs ± 1%   -47.33%  (p=0.000 n=10+9)
    GDA/remainder-10       68.4µs ± 0%    40.2µs ± 0%   -41.31%  (p=0.000 n=10+9)
    GDA/abs-10             11.5µs ± 1%     7.0µs ± 0%   -39.46%  (p=0.000 n=10+10)
    GDA/compare-10         55.7µs ± 0%    33.8µs ± 1%   -39.25%  (p=0.000 n=10+10)
    GDA/tointegralx-10     36.0µs ± 1%    22.1µs ± 0%   -38.55%  (p=0.000 n=10+9)
    GDA/minus-10           14.1µs ± 0%     8.8µs ± 0%   -38.10%  (p=0.000 n=10+10)
    GDA/tointegral-10      35.1µs ± 1%    21.8µs ± 0%   -37.83%  (p=0.000 n=10+10)
    GDA/quantize-10         134µs ± 1%      84µs ± 0%   -37.57%  (p=0.000 n=9+10)
    GDA/subtract-10         171µs ± 0%     109µs ± 0%   -36.37%  (p=0.000 n=10+10)
    GDA/reduce-10          21.7µs ± 1%    14.0µs ± 0%   -35.18%  (p=0.000 n=10+8)
    GDA/divideint-10       34.2µs ± 0%    22.8µs ± 0%   -33.40%  (p=0.000 n=9+9)
    GDA/multiply-10        80.5µs ± 0%    54.9µs ± 0%   -31.83%  (p=0.000 n=9+10)
    GDA/randoms-10         3.20ms ± 0%    2.21ms ± 0%   -30.73%  (p=0.000 n=9+9)
    GDA/add-10              917µs ± 0%     641µs ± 0%   -30.03%  (p=0.000 n=10+10)
    GDA/rounding-10         623µs ± 0%     472µs ± 0%   -24.16%  (p=0.000 n=10+8)
    GDA/plus-10            45.0µs ± 0%    37.5µs ± 0%   -16.63%  (p=0.000 n=10+9)
    GDA/base-10             131µs ± 0%     114µs ± 0%   -13.40%  (p=0.000 n=10+10)
    GDA/squareroot-10      31.6ms ± 0%    27.4ms ± 0%   -13.16%  (p=0.000 n=9+8)
    GDA/powersqrt-10        431ms ± 0%     417ms ± 0%    -3.16%  (p=0.000 n=9+9)
    GDA/divide-10           366µs ± 0%     360µs ± 0%    -1.72%  (p=0.000 n=9+8)
    GDA/cuberoot-apd-10    1.97ms ± 0%    2.07ms ± 0%    +5.21%  (p=0.000 n=9+10)
    GDA/exp-10              119ms ± 0%     126ms ± 0%    +6.19%  (p=0.000 n=10+8)
    GDA/power-10            208ms ± 0%     225ms ± 0%    +8.55%  (p=0.000 n=10+9)
    GDA/log10-10            101ms ± 0%     110ms ± 0%    +9.49%  (p=0.000 n=10+9)
    GDA/ln-10              79.6ms ± 0%    87.3ms ± 0%    +9.61%  (p=0.000 n=9+10)
    
    name                 old alloc/op   new alloc/op   delta
    GDA/abs-10             6.50kB ± 0%    0.00kB       -100.00%  (p=0.000 n=10+10)
    GDA/compare-10         39.1kB ± 0%     0.0kB       -100.00%  (p=0.000 n=10+10)
    GDA/comparetotal-10    37.2kB ± 0%     0.0kB       -100.00%  (p=0.000 n=10+10)
    GDA/minus-10           7.71kB ± 0%    0.00kB       -100.00%  (p=0.000 n=10+10)
    GDA/reduce-10          10.1kB ± 0%     0.0kB       -100.00%  (p=0.000 n=10+10)
    GDA/remainder-10       45.5kB ± 0%     0.1kB ± 0%   -99.86%  (p=0.000 n=10+10)
    GDA/rounding-10         292kB ± 0%       5kB ± 0%   -98.33%  (p=0.000 n=10+10)
    GDA/squareroot-10      8.46MB ± 0%    0.29MB ± 0%   -96.55%  (p=0.000 n=10+10)
    GDA/randoms-10         1.25MB ± 0%    0.05MB ± 0%   -95.98%  (p=0.000 n=10+10)
    GDA/divideint-10       23.2kB ± 0%     1.2kB ± 0%   -94.85%  (p=0.000 n=10+10)
    GDA/divide-10           102kB ± 0%       6kB ± 0%   -93.64%  (p=0.000 n=10+10)
    GDA/powersqrt-10       77.8MB ± 0%     5.1MB ± 0%   -93.44%  (p=0.000 n=10+10)
    GDA/quantize-10        76.4kB ± 0%     8.9kB ± 0%   -88.33%  (p=0.000 n=8+8)
    GDA/multiply-10        55.4kB ± 0%    10.7kB ± 0%   -80.71%  (p=0.000 n=10+10)
    GDA/tointegralx-10     27.9kB ± 0%     6.2kB ± 0%   -77.89%  (p=0.000 n=10+10)
    GDA/tointegral-10      27.2kB ± 0%     6.2kB ± 0%   -77.34%  (p=0.000 n=9+10)
    GDA/subtract-10         131kB ± 0%      34kB ± 0%   -73.82%  (p=0.000 n=10+7)
    GDA/cuberoot-apd-10     265kB ± 0%      82kB ± 0%   -68.97%  (p=0.000 n=10+10)
    GDA/base-10            60.3kB ± 0%    26.2kB ± 0%   -56.61%  (p=0.000 n=10+10)
    GDA/ln-10              10.2MB ± 0%     5.0MB ± 0%   -50.58%  (p=0.000 n=10+10)
    GDA/log10-10           12.5MB ± 0%     6.4MB ± 0%   -49.20%  (p=0.000 n=10+10)
    GDA/add-10              811kB ± 0%     422kB ± 0%   -47.95%  (p=0.000 n=10+10)
    GDA/power-10           27.0MB ± 0%    14.3MB ± 0%   -47.21%  (p=0.000 n=10+10)
    GDA/plus-10            52.3kB ± 0%    40.0kB ± 0%   -23.47%  (p=0.000 n=10+8)
    GDA/exp-10             61.3MB ± 0%    55.7MB ± 0%    -9.12%  (p=0.000 n=10+10)
    
    name                 old allocs/op  new allocs/op  delta
    GDA/abs-10                238 ± 0%         0       -100.00%  (p=0.000 n=10+10)
    GDA/compare-10          1.24k ± 0%     0.00k       -100.00%  (p=0.000 n=10+10)
    GDA/comparetotal-10       881 ± 0%         0       -100.00%  (p=0.000 n=10+10)
    GDA/minus-10              274 ± 0%         0       -100.00%  (p=0.000 n=10+10)
    GDA/reduce-10             352 ± 0%         0       -100.00%  (p=0.000 n=10+10)
    GDA/remainder-10        1.40k ± 0%     0.00k ± 0%   -99.93%  (p=0.000 n=10+10)
    GDA/quantize-10         2.41k ± 0%     0.04k ± 0%   -98.18%  (p=0.000 n=10+10)
    GDA/squareroot-10        278k ± 0%        5k ± 0%   -98.09%  (p=0.000 n=10+10)
    GDA/randoms-10          42.2k ± 0%      0.9k ± 0%   -97.81%  (p=0.000 n=10+10)
    GDA/rounding-10         10.2k ± 0%      0.3k ± 0%   -96.83%  (p=0.000 n=10+10)
    GDA/divide-10           3.08k ± 0%     0.10k ± 0%   -96.72%  (p=0.000 n=10+10)
    GDA/tointegralx-10        857 ± 0%        35 ± 0%   -95.92%  (p=0.000 n=10+10)
    GDA/tointegral-10         833 ± 0%        35 ± 0%   -95.80%  (p=0.000 n=10+10)
    GDA/subtract-10         3.51k ± 0%     0.18k ± 0%   -94.76%  (p=0.000 n=10+10)
    GDA/powersqrt-10        2.63M ± 0%     0.17M ± 0%   -93.49%  (p=0.000 n=10+10)
    GDA/multiply-10         1.35k ± 0%     0.16k ± 0%   -88.41%  (p=0.000 n=10+10)
    GDA/cuberoot-apd-10     7.75k ± 0%     1.19k ± 0%   -84.59%  (p=0.000 n=10+10)
    GDA/add-10              15.2k ± 0%      3.0k ± 0%   -79.96%  (p=0.000 n=10+10)
    GDA/divideint-10          704 ± 0%       142 ± 0%   -79.83%  (p=0.000 n=10+10)
    GDA/ln-10                269k ± 0%       80k ± 0%   -70.21%  (p=0.000 n=10+10)
    GDA/log10-10             327k ± 0%      101k ± 0%   -69.04%  (p=0.000 n=10+10)
    GDA/power-10             700k ± 0%      226k ± 0%   -67.69%  (p=0.000 n=10+10)
    GDA/plus-10               706 ± 0%       229 ± 0%   -67.56%  (p=0.000 n=10+10)
    GDA/exp-10               911k ± 0%      544k ± 0%   -40.32%  (p=0.000 n=10+10)
    GDA/base-10             2.86k ± 0%     2.52k ± 0%   -12.06%  (p=0.000 n=10+10)
    

    cc. @mjibson

  • Remove unused block in sqrt

    Remove unused block in sqrt

    Neither of the branches that modified approx here were taken in any tests, so the tests still pass without them. Even though this is described in the paper, I think it is safe to remove.


    This change is Reviewable

  • Inconsistent (or buggy) Quantize behavior

    Inconsistent (or buggy) Quantize behavior

    Looks like Quantize(result, amount, 0) func works differently for:

    • when amount is of the form N.0* (where N is integer number and N != 0)
    • when amount is of the form 0.0*

    Here is a simple test to reproduce this (with v3.1.0):

    // quantize is a simple wrapper around `apd` library.
    func quantize(amount *apd.Decimal, decimalPlaces uint32, roundingRule apd.Rounder) (*apd.Decimal, error) {
    	ctx := apd.BaseContext.WithPrecision(infinitePrecision)
    	ctx.Rounding = roundingRule
    
    	var amt apd.Decimal
    	exp := -int32(decimalPlaces)
    	_, err := ctx.Quantize(&amt, amount, exp)
    
    	return &amt, err
    }
    
    func TestQuantizeRounding(t *testing.T) {
            // 1.1 -> 2 (works as expected)
    	got, err = quantize(apd.New(11, -1), 0, apd.RoundUp)
    	if err != nil {
    		t.Fatalf("unexpected error: %v", err)
    	}
    	want = apd.New(2, 0)
    	if want.CmpTotal(got) != 0 {
    		t.Fatalf("want: %+v, got: %+v", want, got)
    	}
    
            // 1.01 -> 2 (works as expected)
    	got, err := quantize(apd.New(101, -2), 0, apd.RoundUp)
    	if err != nil {
    		t.Fatalf("unexpected error: %v", err)
    	}
    	want := apd.New(2, 0)
    	if want.CmpTotal(got) != 0 {
    		t.Fatalf("want: %+v, got: %+v", want, got)
    	}
    
            // 0.1 -> 1 (works as expected)
    	got, err = quantize(apd.New(1, -1), 0, apd.RoundUp)
    	if err != nil {
    		t.Fatalf("unexpected error: %v", err)
    	}
    	want = apd.New(1, 0)
    	if want.CmpTotal(got) != 0 {
    		t.Fatalf("want: %+v, got: %+v", want, got)
    	}
    
            // 0.01 -> 1 (doesn't work as expected! instead it does 0.01 -> 0 for some reason)
    	got, err = quantize(apd.New(1, -2), 0, apd.RoundUp)
    	if err != nil {
    		t.Fatalf("unexpected error: %v", err)
    	}
    	want = apd.New(1, 0)
    	if want.CmpTotal(got) != 0 {
    		t.Fatalf("want: %+v, got: %+v", want, got)
    	}
    }
    
  • Precision clarification

    Precision clarification

    I've been using the lib with a precision of 34, and I've noticed that when using Text(), some of my decimals terminate with ...9999999 or ..0000001. I suspect it's got something to do with this.

    Is there a way I can avoid having such errors, because my PoC app requires a certain sum total to be exactly zero, even at the precision of 34, which I can't achieve with such errors. I have to use f and can't use scientific notation. I'd like to know why this error occurs, and why it can't be fixed, because I've noticed the shopspring lib having a similar problem.

  • panic with SetString(

    panic with SetString(".-400000000000000000000000000000000000000")

    This test panics:

    func TestSetStringMalformed(t *testing.T) {
    	c := &BaseContext
    	d := new(Decimal)
    	tests := []string{
    		".-4",
    		".-400000000000000000000000000000000000000",
    	}
    	for _, test := range tests {
    		_, _, err := c.SetString(d, test)
    		if err == nil {
    			t.Errorf("expected error for SetString(%q)", test)
    		}
    	}
    }
    

    The panic is in the second test case. Also, I think ".-4" should yield an error (the first test case).


    This was found (indirectly) by go-fuzz. Would there be interest in adding a fuzz function or three directly? (I don't have much time to dedicate to this, but I can at least give it a nudge, and/or continue my indirect fuzzing.)

  • How to use Quantize with limited precision context

    How to use Quantize with limited precision context

    I'm building a financial application that deals with multiple currencies and multiple Contexts with settings appropriate to each currency. The DB storage supports the widest possible range of values to permit to store any currency, DB storage uses Decimal(78,18), i.e. MaxExponent=60 and MinExponent=-18 in apd terms.

    I'm trying to quantize the value coming from DB with highest possible precision into the precision appropriate for given currency. The value is not large, but has many trailing decimal zeroes that I want to remove to bring it to correct precision.

    And for whatever reason Quantize() does not produce the result I expect, but throws an Overflow condition instead. Below is the sample code that reproduces it:

    package main
    
    import (
    	"fmt"
    	"github.com/cockroachdb/apd/v2"
    )
    
    func main() {
    	dStr := "6410.000000000000000000"    // small value with many trailing zeroes as seen in DB 
    	wideCon := &apd.Context{         // widest supported context
    		MaxExponent: 78,                
    		MinExponent: -18,
    		Traps:       apd.DefaultTraps,
    	}
    	d, _, _ := wideCon.SetString(&apd.Decimal{}, dStr)
    
    	limitCon := &apd.Context{         // limited context suitable for given currency
    		Precision:   17,
    		MaxExponent: 9,
    		MinExponent: -8,
    		Traps:      apd.DefaultTraps,
    	}
    	scratch := &apd.Decimal{}
    	con, err := limitCon.Quantize(scratch, d, limitCon.MinExponent)   // attempt to quantize to correct precision, expect to work fine 
    	fmt.Println(con, err, scratch)      // fails with `overflow` condition
    }
    

    In this piece of code the wideCon is the widest supported context in the system, i.e. the DB default context, while limitCon is the target currency context. If I understand correctly the context settings - the limitCon effectively forces the values in this context to be within 999999999.99999999 range, which is exactly what I need.

    The sample value is way below the MaxExponent limit, and I expect limitCon.Quantize() to produce same value with decimal places adjusted to fit, but instead it returns a NaN value and an Overflow condition.

    The error goes away if I increase the MaxExponent, it works at MaxExponent=12 for this value, but workarounding it this way means I will have to forfeit the ceiling check limit which I'd like to not do.

    What is a better way to achieve what I need - round an arbitrary long precision value to a constrained precision settings force-rounded per currency and with correct ceiling constraint?

Related tags
Arbitrary-precision fixed-point decimal numbers in go

decimal Arbitrary-precision fixed-point decimal numbers in go. Note: Decimal library can "only" represent numbers with a maximum of 2^31 digits after

Jan 8, 2023
Arbitrary-precision fixed-point decimal numbers in go

decimal Arbitrary-precision fixed-point decimal numbers in go. Note: Decimal library can "only" represent numbers with a maximum of 2^31 digits after

Jan 9, 2023
An arbitrary-precision decimal floating-point arithmetic package for Go

decimal Package decimal implements arbitrary-precision decimal floating-point arithmetic for Go. Rationale How computers represent numbers internally

Sep 27, 2022
An arbitrary-precision decimal floating-point arithmetic package for Go

decimal Package decimal implements arbitrary-precision decimal floating-point arithmetic for Go. Rationale How computers represent numbers internally

Sep 27, 2022
Arbitrary-precision fixed-point decimal numbers in go

decimal Arbitrary-precision fixed-point decimal numbers in go. Note: Decimal library can "only" represent numbers with a maximum of 2^31 digits after

Jan 8, 2023
Arbitrary-precision fixed-point decimal numbers in go

decimal Arbitrary-precision fixed-point decimal numbers in go. Note: Decimal library can "only" represent numbers with a maximum of 2^31 digits after

Jan 10, 2022
Converts a number to its English counterpart. Uses arbitrary precision; so a number of any size can be converted.

Converts a number to its English counterpart. Uses arbitrary precision; so a number of any size can be converted.

Dec 14, 2021
Bigint - Immutable arbitrary-precision integer for Go

bigint Go's big.Int is mutable to enable flexibility in performance tuning but s

Sep 13, 2022
High-precision indoor positioning framework, version 3.

The Framework for Internal Navigation and Discovery (FIND) is like indoor GPS for your house or business, using only a simple smartphone or laptop. Th

Jan 1, 2023
:handbag: Cache arbitrary data with an expiration time.

cache Cache arbitrary data with an expiration time. Features 0 dependencies About 100 lines of code 100% test coverage Usage // New cache c := cache.N

Jan 5, 2023
smartcrop finds good image crops for arbitrary crop sizes
smartcrop finds good image crops for arbitrary crop sizes

smartcrop smartcrop finds good image crops for arbitrary sizes. It is a pure Go implementation, based on Jonas Wagner's smartcrop.js Image: https://ww

Jan 8, 2023
Arbitrary transformations of JSON in Golang

kazaam Description Kazaam was created with the goal of supporting easy and fast transformations of JSON data with Golang. This functionality provides

Dec 18, 2022
Arbitrary expression evaluation for golang

govaluate Provides support for evaluating arbitrary C-like artithmetic/string expressions. Why can't you just write these expressions in code? Sometim

Jan 2, 2023
a Go package to interact with arbitrary JSON

go-simplejson a Go package to interact with arbitrary JSON Importing import github.com/bitly/go-simplejson Documentation Visit the docs on Go package

Dec 29, 2022
Job worker service that provides an API to run arbitrary Linux processes.
Job worker service that provides an API to run arbitrary Linux processes.

Job Scheduler Summary Prototype job worker service that provides an API to run arbitrary Linux processes. Overview Library The library (Worker) is a r

May 26, 2022
Embed arbitrary resources into a go executable at runtime, after the executable has been built.

ember Ember is a lightweight library and tool for embedding arbitrary resources into a go executable at runtime. The resources don't need to exist at

Nov 9, 2022
The k8s-generic-webhook is a library to simplify the implementation of webhooks for arbitrary customer resources (CR) in the operator-sdk or controller-runtime.

k8s-generic-webhook The k8s-generic-webhook is a library to simplify the implementation of webhooks for arbitrary customer resources (CR) in the opera

Nov 24, 2022
Convert arbitrary formats to Go Struct (including json, toml, yaml, etc.)

go2struct Convert arbitrary formats to Go Struct (including json, toml, yaml, etc.) Installation Run the following command under your project: go get

Nov 15, 2022
A skip list of arbitrary elements that can be filtered using roaring bitmaps stored in an LRU cache

Skipfilter This package provides a data structure that combines a skiplist with a roaring bitmap cache. This library was created to efficiently filter

Aug 4, 2022
Minimal go library to relay webhook events back to an arbitrary service.

hookrelay Minimal go library to relay webhook events back to an arbitrary service. With the use of a primary HTTP mux router, we are able to register

Nov 3, 2021