{"addressing":"content-addressed","addressedBy":"7418dfc5de093b77643d5be7852083c24fbf7fb3ab0f89056b6a0983ad97bc12","canonicalText":"{\"formatVersion\":1,\"frozen\":{\"address\":{\"language\":\"typescript\",\"major\":1,\"name\":\"number/round\"},\"benchmarks\":{\"profiles\":[{\"class\":\"shortened\",\"data\":{\"fields\":[],\"kind\":\"record\"},\"description\":\"Amounts with a third decimal rounded to two places, the dominant shape in pricing, tax and invoicing code.\",\"name\":\"money-to-the-cent\",\"samples\":{\"kind\":\"carried\",\"values\":[{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":19.994}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":2}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":1234.561}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":2}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":-75.128}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":2}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":0.333}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":2}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":99999.999}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":2}}],\"kind\":\"record\"}]}},{\"class\":\"at-a-tie\",\"data\":{\"fields\":[],\"kind\":\"record\"},\"description\":\"Values sitting exactly on the half-cent, where the rounding rule is the whole answer and every built-in spelling parts from this contract.\",\"name\":\"half-cents\",\"samples\":{\"kind\":\"carried\",\"values\":[{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":1.005}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":2}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":0.015}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":2}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":2.675}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":2}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":-8.575}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":2}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":162.295}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":2}}],\"kind\":\"record\"}]}},{\"class\":\"already-exact\",\"data\":{\"fields\":[],\"kind\":\"record\"},\"description\":\"Values already carrying no more decimals than the caller asked for. Timed apart because an implementation reading the shortest decimal answers these without rounding anything, and a caller normalising a column of amounts hits this path on most rows.\",\"name\":\"nothing-to-drop\",\"samples\":{\"kind\":\"carried\",\"values\":[{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":42}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":2}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":19.99}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":2}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":-0.5}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":4}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":1e+21}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":2}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":1.7976931348623157e+308}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":2}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":0}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":0}}],\"kind\":\"record\"}]}},{\"class\":\"shortened\",\"data\":{\"fields\":[],\"kind\":\"record\"},\"description\":\"Values whose shortest decimal is as long as a double gets, to expose an implementation whose cost grows with the digit string rather than with the places asked for. The last one carries every digit through the increment, since rounding it grows the string.\",\"name\":\"seventeen-significant-digits\",\"samples\":{\"kind\":\"carried\",\"values\":[{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":0.30000000000000004}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":2}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":1.2345678901234567}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":2}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":1.2345678901234567}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":15}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":0.9999999999999999}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":15}}],\"kind\":\"record\"}]}},{\"class\":\"refused\",\"data\":{\"fields\":[],\"kind\":\"record\"},\"description\":\"Calls that must answer null. Measured separately because an implementation may take a very different path when it refuses, and a caller validating user input hits that path most.\",\"name\":\"refused-calls\",\"samples\":{\"kind\":\"carried\",\"values\":[{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"number\",\"literal\":\"nan\"}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":2}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"number\",\"literal\":\"infinity\"}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":2}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"number\",\"literal\":\"negative-infinity\"}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":0}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":1.5}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":1.5}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":1.5}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":-1}}],\"kind\":\"record\"}]}}],\"vocabulary\":[{\"meaning\":\"the value carries no more decimals than were asked for\",\"name\":\"already-exact\"},{\"meaning\":\"the value sits exactly on a half, where the rule is the answer\",\"name\":\"at-a-tie\"},{\"meaning\":\"digits are dropped and a carry may run\",\"name\":\"shortened\"},{\"meaning\":\"every sample is refused, which is a different path\",\"name\":\"refused\"}]},\"caseTables\":[{\"cases\":[{\"data\":{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":1.005}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":2}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":1.01}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":null}}],\"kind\":\"record\"},\"group\":\"the-decimal-a-double-cannot-hold\",\"id\":\"the-half-cent-to-fixed-loses\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"The canonical instance. The double nearest 1.005 is 1.00499999999999989..., so `(1.005).toFixed(2)` answers \\\"1.00\\\" and the multiply answers 1. A caller who wrote 1.005 wrote the decimal, and the decimal is what is rounded.\"},{\"data\":{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":0.015}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":2}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":0.02}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":null}}],\"kind\":\"record\"},\"group\":\"the-decimal-a-double-cannot-hold\",\"id\":\"the-smallest-half-cent-to-fixed-loses\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"The first value of the declared sweep that `toFixed` gets wrong, so the table carries the boundary the figure in `theTraps` starts counting from rather than only a memorable middle.\"},{\"data\":{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":0.145}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":2}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":0.15}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":null}}],\"kind\":\"record\"},\"group\":\"the-decimal-a-double-cannot-hold\",\"id\":\"the-half-cent-the-multiply-loses\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"The first value the multiply gets wrong, and it is not the first `toFixed` gets wrong. The two spellings fail on different values, which is why the contract names two traps and not one told twice.\"},{\"data\":{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":2.675}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":2}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":2.68}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":null}}],\"kind\":\"record\"},\"group\":\"the-decimal-a-double-cannot-hold\",\"id\":\"a-price-with-three-decimals\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"The instance every article about floating point uses, kept because a reader arriving from one of them looks for it and has to find this contract answering 2.68.\"},{\"data\":{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":0.30000000000000004}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":2}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":0.3}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":null}}],\"kind\":\"record\"},\"group\":\"the-decimal-a-double-cannot-hold\",\"id\":\"a-sum-that-is-not-its-parts\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"What `0.1 + 0.2` really is. Rounding is the usual repair for it, and this row says the repair works: seventeen significant digits in, two out, with nothing left of the error.\"},{\"data\":{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":0.5}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":0}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":1}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":null}}],\"kind\":\"record\"},\"group\":\"which-way-a-tie-goes\",\"id\":\"a-positive-half\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"Away from zero, which on a positive value is the direction everybody agrees on.\"},{\"data\":{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":-0.5}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":0}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":-1}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":null}}],\"kind\":\"record\"},\"group\":\"which-way-a-tie-goes\",\"id\":\"a-negative-half\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"Where `Math.round` parts company: it answers `-0`, because it breaks a tie towards positive infinity. This contract is symmetric, so a refund of half a unit rounds to the same magnitude as the charge it reverses.\"},{\"data\":{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":-1.5}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":0}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":-2}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":null}}],\"kind\":\"record\"},\"group\":\"which-way-a-tie-goes\",\"id\":\"a-negative-one-and-a-half\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"The same parting one unit along, where `Math.round` answers -1. Two rows rather than one because -0 could be read as a sign question and this one cannot.\"},{\"data\":{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":-2.5}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":0}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":-3}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":null}}],\"kind\":\"record\"},\"group\":\"which-way-a-tie-goes\",\"id\":\"a-negative-two-and-a-half\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"The row that separates this contract from half-to-even as well as from `Math.round`. Banker's rounding answers -2 here and -2 for -1.5; this contract answers -3 and -2, and `inputDomain` says which of the two jobs it is for.\"},{\"data\":{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":0.4999}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":0}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":0}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":null}}],\"kind\":\"record\"},\"group\":\"which-way-a-tie-goes\",\"id\":\"just-below-a-tie\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"The other side of the boundary, so that the group settles where the tie *is* and not only what happens on it. Without it a contract that rounded everything up would satisfy the four rows above.\"},{\"data\":{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":-0.001}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":2}},{\"name\":\"expected\",\"value\":{\"kind\":\"number\",\"literal\":\"negative-zero\"}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":null}}],\"kind\":\"record\"},\"group\":\"the-sign-of-zero\",\"id\":\"an-amount-that-rounds-away-to-nothing\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"A tenth of a penny off an account, rounded to the penny. The answer is negative zero: the amount is gone and the direction is not, which is what a ledger line needs.\"},{\"data\":{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"number\",\"literal\":\"negative-zero\"}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":2}},{\"name\":\"expected\",\"value\":{\"kind\":\"number\",\"literal\":\"negative-zero\"}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":null}}],\"kind\":\"record\"},\"group\":\"the-sign-of-zero\",\"id\":\"a-negative-zero-stays-negative\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"Nothing is dropped, so the value is answered back - and `String(-0)` is \\\"0\\\", so an implementation reading the sign off the text loses it here and nowhere else.\"},{\"data\":{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":0.001}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":2}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":0}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":null}}],\"kind\":\"record\"},\"group\":\"the-sign-of-zero\",\"id\":\"a-positive-amount-that-rounds-away-to-nothing\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"The row that makes the one above mean something. An implementation answering `-0` to everything would pass both rows before it, and fails here.\"},{\"data\":{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":0.9999999999999999}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":15}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":1}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":null}}],\"kind\":\"record\"},\"group\":\"a-carry-that-runs\",\"id\":\"a-carry-through-every-digit\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"Sixteen nines, every one of which carries. The answer is one digit longer than the digits kept, which is the case an increment written as an arithmetic add gets wrong at the seventeenth digit.\"},{\"data\":{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":9.995}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":2}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":10}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":null}}],\"kind\":\"record\"},\"group\":\"a-carry-that-runs\",\"id\":\"a-carry-that-crosses-ten\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"A tie whose carry changes the number of digits before the point as well as after it, so an implementation splitting the string on the decimal point has to put it back somewhere else.\"},{\"data\":{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":99.995}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":2}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":100}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":null}}],\"kind\":\"record\"},\"group\":\"a-carry-that-runs\",\"id\":\"a-carry-that-crosses-a-hundred\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"The same crossing one order up, where the carry runs through two nines rather than one. Both rows are here because a carry that stops after one digit passes the first and fails this.\"},{\"data\":{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":1e+21}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":2}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":1e+21}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":null}}],\"kind\":\"record\"},\"group\":\"a-magnitude-no-fixed-notation-holds\",\"id\":\"a-value-written-in-exponent-notation\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"The threshold where `String` stops writing digits and starts writing `1e+21`. An implementation reading the shortest decimal has to read that form, and one that splits on the decimal point alone reads the exponent as a fraction.\"},{\"data\":{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":1.7976931348623157e+308}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":0}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":1.7976931348623157e+308}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":null}}],\"kind\":\"record\"},\"group\":\"a-magnitude-no-fixed-notation-holds\",\"id\":\"the-largest-double\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"Nothing to drop at any non-negative place count, so the value is answered back. It is here because an implementation that reassembles unconditionally can overflow to Infinity, which the contract forbids.\"},{\"data\":{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":5e-324}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":323}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":1e-323}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":null}}],\"kind\":\"record\"},\"group\":\"a-magnitude-no-fixed-notation-holds\",\"id\":\"the-smallest-double-carried-up\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"The smallest positive double, rounded one place above itself. The answer is the next denormal up, so the row settles that the rule keeps working where the doubles stop being evenly spaced.\"},{\"data\":{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":5e-324}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":0}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":0}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":null}}],\"kind\":\"record\"},\"group\":\"a-magnitude-no-fixed-notation-holds\",\"id\":\"the-smallest-double-dropped\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"The same value with every digit dropped. The dropped part opens with a zero rather than the five, because the five sits 324 places down - so it rounds to nothing, and an implementation looking at the first significant digit instead of the first dropped one answers 1.\"},{\"data\":{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":1.5}},{\"name\":\"places\",\"value\":{\"kind\":\"number\",\"literal\":\"negative-zero\"}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":2}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":null}}],\"kind\":\"record\"},\"group\":\"the-place-count-at-its-edges\",\"id\":\"a-place-count-of-negative-zero\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"Negative zero is a count of zero, not a negative count: `Number.isInteger(-0)` is true and `-0 < 0` is false. This row exists because it is settled by no property of this contract - measured, a perturbation refusing it reddened none of the ten guards, so the decision would otherwise be kept by nothing.\"},{\"data\":{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":1.5}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":400}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":1.5}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":null}}],\"kind\":\"record\"},\"group\":\"the-place-count-at-its-edges\",\"id\":\"a-place-count-past-what-to-fixed-accepts\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"Where this contract and `toFixed` disagree about whether there is a question. `(1.5).toFixed(400)` throws a RangeError; asking for more places than the value carries is a request that changes nothing, and the answer is the value.\"},{\"data\":{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":1.5}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":1e+21}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":1.5}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":null}}],\"kind\":\"record\"},\"group\":\"the-place-count-at-its-edges\",\"id\":\"a-place-count-larger-than-any-decimal\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"The same question with a place count no string could ever be built to. It is answered without building one, which is why there is no `places-out-of-range` in the reason set.\"},{\"data\":{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":19.99}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":2}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":19.99}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":null}}],\"kind\":\"record\"},\"group\":\"the-place-count-at-its-edges\",\"id\":\"every-place-already-there\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"The ordinary shape of the same thing, and most of the calls a real caller makes: a column of amounts normalised to two places, of which nearly all are already at two places.\"},{\"data\":{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"number\",\"literal\":\"nan\"}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":2}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":null}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":\"value-not-finite\"}}],\"kind\":\"record\"},\"group\":\"what-is-refused\",\"id\":\"not-a-number\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"There is no decimal to round. Answering NaN would let the value keep travelling, which is the whole reason `number/parse@1` refuses to produce one.\"},{\"data\":{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"number\",\"literal\":\"infinity\"}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":2}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":null}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":\"value-not-finite\"}}],\"kind\":\"record\"},\"group\":\"what-is-refused\",\"id\":\"positive-infinity\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"The same refusal for the same reason, and one message covers both without lying.\"},{\"data\":{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"number\",\"literal\":\"negative-infinity\"}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":2}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":null}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":\"value-not-finite\"}}],\"kind\":\"record\"},\"group\":\"what-is-refused\",\"id\":\"negative-infinity\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"Both infinities rather than one, because an implementation testing `value === Infinity` passes the row above and fails this.\"},{\"data\":{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":1.5}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":1.5}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":null}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":\"places-not-whole\"}}],\"kind\":\"record\"},\"group\":\"what-is-refused\",\"id\":\"a-fractional-place-count\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"A place count is a count. Half a decimal place is a bug in the calling code, and the reason says so rather than guessing at one or two.\"},{\"data\":{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":1.5}},{\"name\":\"places\",\"value\":{\"kind\":\"number\",\"literal\":\"nan\"}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":null}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":\"places-not-whole\"}}],\"kind\":\"record\"},\"group\":\"what-is-refused\",\"id\":\"a-place-count-that-is-not-a-number\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"The shape a place count reaches when it is computed from something absent. `Number.isInteger(NaN)` is false, so one test covers it with the row above.\"},{\"data\":{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":1.5}},{\"name\":\"places\",\"value\":{\"kind\":\"number\",\"literal\":\"infinity\"}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":null}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":\"places-not-whole\"}}],\"kind\":\"record\"},\"group\":\"what-is-refused\",\"id\":\"an-infinite-place-count\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"The third shape the same test covers, and the one that separates this reason from a range check: a finite place count of 1e21 is answered and an infinite one is refused.\"},{\"data\":{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":1.5}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":-1}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":null}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":\"places-negative\"}}],\"kind\":\"record\"},\"group\":\"what-is-refused\",\"id\":\"a-negative-place-count\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"A caller asking to round to tens. It is a real job and it is not this one - `inputDomain` refuses it in as many words - so the reason is its own rather than folded into the one above: that repair is upstream in the calling code, and this one is to reach for a different tool.\"}],\"groups\":[{\"id\":\"the-decimal-a-double-cannot-hold\",\"note\":\"The reason this contract exists. Each value below is one a caller typed and the machine stored as something slightly smaller, so a built-in that rounds what was stored answers a penny short.\",\"title\":\"The decimal a double cannot hold\"},{\"id\":\"which-way-a-tie-goes\",\"note\":null,\"title\":\"Which way a tie goes\"},{\"id\":\"the-sign-of-zero\",\"note\":\"A refund that rounds away to nothing keeps the mark saying which way the money went. It is why the contract compares answers with `Object.is` rather than with `===`.\",\"title\":\"The sign of zero\"},{\"id\":\"a-carry-that-runs\",\"note\":null,\"title\":\"A carry that runs\"},{\"id\":\"a-magnitude-no-fixed-notation-holds\",\"note\":\"Values whose shortest decimal is written in exponent notation, at both ends of the double range. `toFixed` refuses some of these outright.\",\"title\":\"A magnitude no fixed notation holds\"},{\"id\":\"the-place-count-at-its-edges\",\"note\":null,\"title\":\"The place count at its edges\"},{\"id\":\"what-is-refused\",\"note\":null,\"title\":\"What is refused\"}],\"name\":\"edge-cases\",\"purpose\":\"the calls this contract settles, every answer computed in integer arithmetic beforehand\"}],\"environments\":[\"node\",\"browser\",\"bun\"],\"harness\":[{\"bytes\":21301,\"path\":\"contract.ts\",\"sha256\":\"9870dbb73cde74bccbdb99671ef847314c43e64151888befb6d347e00927eb24\"},{\"bytes\":4406,\"path\":\"edge-cases.test.ts\",\"sha256\":\"dc20b234e100e526a7cddd4a8671064faa983ee1db4632e4907e13bbdcf1ebdf\"},{\"bytes\":16304,\"path\":\"edge-cases.ts\",\"sha256\":\"fda85449edae85e8b5520105025e9d07abec40010be3935a471b2b2e694a841a\"},{\"bytes\":9531,\"path\":\"language.test.ts\",\"sha256\":\"d51910ddcd9d77ab12e0da00281497a960af4e9474179f80a7bcb39651af53f5\"},{\"bytes\":5390,\"path\":\"profiles.test.ts\",\"sha256\":\"e62a3a9fad608906c90a9570be33e627b171fb16395bfa98669bc9493c9ad402\"},{\"bytes\":8916,\"path\":\"properties.test.ts\",\"sha256\":\"4bd57039b4b3d9f6b7fe67e1e115200d0357575e7ecc96b331f064ab442e1c5b\"},{\"bytes\":6522,\"path\":\"reference.ts\",\"sha256\":\"081bc28160cac23dd3c1aefb707643c6a9216ddc6811056e6bdc535b9e065bdd\"},{\"bytes\":1361,\"path\":\"signature.test-d.ts\",\"sha256\":\"190227f86ca421a194cf1b4c44a8e9e06d646c76ca019de7c52fdde54b3ac749\"}],\"identity\":{\"description\":\"Rounds a number to a fixed number of decimal places in JavaScript and TypeScript, answering the decimal the caller wrote rather than the double the machine stored. The two spellings a caller reaches for first are wrong, silently: Number((1.005).toFixed(2)) answers 1 and Math.round(1.005 * 100) / 100 answers 1, because the double nearest 1.005 sits below it. Neither ever returns NaN, so nothing announces the loss - a half-cent goes missing and the total is short. Math.round carries a second, unrelated fault: it breaks a tie towards positive infinity, so Math.round(-0.5) is -0 and Math.round(-2.5) is -2, which makes a refund round the opposite way from the charge it reverses. The one spelling in the language that answers correctly is Intl.NumberFormat with a rounding mode, and it hands back a locale-formatted string: converting it back with Number is the third trap, because \\\"1,000.01\\\" is NaN. This contract answers a number, breaks every tie away from zero, and refuses rather than guesses.\",\"exportName\":\"round\",\"inputDomain\":\"Finite doubles carrying an amount somebody wrote down: prices, totals, tax lines, measurements, percentages, chart axes. It is not a formatter - the answer is a number and never text - not a decimal arithmetic library, not a currency type, not banker's rounding, and not a way to round to tens or hundreds, which is why a negative place count is refused rather than answered.\",\"relationToTheLanguage\":\"The language ships `toFixed`, which answers a string and rounds the stored double rather than the decimal the caller wrote, and `Math.round`, which breaks a tie towards positive infinity rather than away from zero. `Intl.NumberFormat` with `roundingMode: \\\"halfExpand\\\"` agrees with this contract and answers locale-formatted text.\",\"searchAliases\":[\"round to 2 decimal places\",\"round to decimals\",\"round number javascript\",\"toFixed\",\"to fixed\",\"round half up\",\"round money\",\"round price\",\"floating point rounding\",\"round to nearest cent\"],\"summary\":\"Round a number to a fixed number of decimal places, or null when the call cannot be answered.\"},\"ownDeclarations\":[{\"executableBy\":{\"battery\":\"number-round-spec\",\"guard\":\"the-stored-double-and-not-the-written-decimal-over-the-sweep\"},\"name\":\"THE_SWEEP\",\"value\":{\"fields\":[{\"name\":\"thousandths\",\"value\":{\"kind\":\"primitive\",\"value\":1000000}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":2}}],\"kind\":\"record\"},\"verification\":\"executable\"},{\"executableBy\":{\"battery\":\"number-round-spec\",\"guard\":\"the-stored-double-and-not-the-written-decimal-parts-from-this-contract\"},\"name\":\"theTraps\",\"value\":{\"entries\":[{\"fields\":[{\"name\":\"name\",\"value\":{\"kind\":\"primitive\",\"value\":\"the-stored-double-and-not-the-written-decimal\"}},{\"name\":\"spelling\",\"value\":{\"kind\":\"primitive\",\"value\":\"Number(value.toFixed(places))\"}},{\"name\":\"statement\",\"value\":{\"kind\":\"primitive\",\"value\":\"Rounds the double the machine stored rather than the decimal the caller wrote, so a value that sits a hair below its own half-cent rounds down.\"}},{\"name\":\"partsFrom\",\"value\":{\"entries\":[{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":1.005}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":2}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":0.015}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":2}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":2.675}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":2}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":8.575}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":2}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":162.295}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":2}}],\"kind\":\"record\"}],\"kind\":\"list\"}},{\"name\":\"wrongOnTheSweep\",\"value\":{\"kind\":\"primitive\",\"value\":48000}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"name\",\"value\":{\"kind\":\"primitive\",\"value\":\"the-error-moved-and-not-removed\"}},{\"name\":\"spelling\",\"value\":{\"kind\":\"primitive\",\"value\":\"Math.round(value * 10 ** places) / 10 ** places\"}},{\"name\":\"statement\",\"value\":{\"kind\":\"primitive\",\"value\":\"Multiplying by a power of ten introduces an error of its own, so the tie the caller wrote is not the tie `Math.round` is shown - and `Math.round` breaks a tie towards positive infinity, which rounds a refund the opposite way from the charge it reverses.\"}},{\"name\":\"partsFrom\",\"value\":{\"entries\":[{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":0.145}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":2}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":1.005}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":2}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":-0.5}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":0}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":-1.5}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":0}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":-2.5}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":0}}],\"kind\":\"record\"}],\"kind\":\"list\"}},{\"name\":\"wrongOnTheSweep\",\"value\":{\"kind\":\"primitive\",\"value\":4588}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"name\",\"value\":{\"kind\":\"primitive\",\"value\":\"the-right-answer-as-text\"}},{\"name\":\"spelling\",\"value\":{\"kind\":\"primitive\",\"value\":\"Number(value.toLocaleString(locale, { maximumFractionDigits: places }))\"}},{\"name\":\"statement\",\"value\":{\"kind\":\"primitive\",\"value\":\"Rounds correctly and answers text, so reading it back as a number meets the grouping separator: every amount below a thousand survives and every amount above it is NaN.\"}},{\"name\":\"partsFrom\",\"value\":{\"entries\":[{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":999.995}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":2}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":1000.005}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":2}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"value\",\"value\":{\"kind\":\"primitive\",\"value\":1234.567}},{\"name\":\"places\",\"value\":{\"kind\":\"primitive\",\"value\":2}}],\"kind\":\"record\"}],\"kind\":\"list\"}},{\"name\":\"wrongOnTheSweep\",\"value\":{\"kind\":\"primitive\",\"value\":6}}],\"kind\":\"record\"}],\"kind\":\"list\"},\"verification\":\"executable\"}],\"properties\":{\"runs\":1000,\"universal\":[{\"applicable\":false,\"name\":\"never mutates its arguments\",\"reason\":\"The signature takes two `number` arguments, primitives immutable by construction in JavaScript. No implementation, correct or broken, can violate this, so a test asserting it would be structurally incapable of failing.\"},{\"applicable\":true,\"name\":\"deterministic\",\"reason\":\"Violable in practice, and witnessed by an implementation that hoists the carry out of the increment loop - the shape a variable reaches when it is made \\\"reusable\\\" - so that a carry left by an earlier call decides this one and two identical consecutive calls answer differently. Two caches keyed on one of the two arguments were measured as candidates and both were rejected: the probe primes them itself, so the pair of calls agrees and the defect surfaces on the specific properties instead. A third was rejected for a reason worth carrying past this contract - an accumulator that appends rather than replaces drives the digit string past what a double holds, both calls answer Infinity, and `outputsAreEqual` is `Object.is`, which judges two infinities equal. On a numeric contract an overflow can absorb a determinism signal. This property is ordered under `no ambient input` rather than independent of it: every mutant measured to redden it reddens that one too, and the memoise-last mutant reddens that one and not this.\"},{\"applicable\":true,\"name\":\"no ambient input\",\"reason\":\"Violable in practice: this contract reads two numbers and answers a third, so the call history is the only ambient input it can plausibly acquire. Witnessed by an implementation holding the last call answered and serving it whenever the value matches, which answers the previous place count - the shape a caller rounding one amount to two places and then to none would meet. The property interleaves a probe with an arbitrary history and requires the probe to answer identically either way. Measured: a memo keyed on the value alone stays invisible to it, because the probe primes the memo before the history runs, which is the same limit `number/parse@1` records of its own caches.\"},{\"applicable\":false,\"name\":\"no ambient output\",\"reason\":\"Not reachable by a property - a test cannot observe a write that happened before it ran, and a correct memoising cache is indistinguishable from a defect by behaviour alone.\"}]},\"sharedHarness\":[{\"bytes\":16621,\"path\":\"packages/catalogue/every-contract.ts\",\"sha256\":\"651fcc16fdc900bcded8e572bc5bb044c52322bb9c04bc2c05630e2761d5cc16\"},{\"bytes\":9777,\"path\":\"packages/catalogue/identifier.ts\",\"sha256\":\"8ab32ffcd699e39aa4fcb7604722f28dc12b723fca3a126a413c3e0a26ff3ff6\"}],\"surface\":{\"couplingRule\":\"round(v, p) === null if and only if describeRoundFailure(v, p) !== null, for every v and p\",\"exports\":[{\"name\":\"round\",\"parameters\":[{\"name\":\"value\",\"type\":\"number\"},{\"name\":\"places\",\"type\":\"number\"}],\"role\":\"the-answer\",\"text\":\"(value: number, places: number) => number | null\",\"typeName\":\"Round\"},{\"name\":\"describeRoundFailure\",\"parameters\":[{\"name\":\"value\",\"type\":\"number\"},{\"name\":\"places\",\"type\":\"number\"}],\"role\":\"the-diagnostic\",\"text\":\"(value: number, places: number) => RoundFailureReason | null\",\"typeName\":\"DescribeRoundFailure\"}],\"failureReasons\":[\"value-not-finite\",\"places-not-whole\",\"places-negative\"],\"supportingTypes\":[]}},\"unit\":\"contract\"}","formatVersion":1}