# math.basis-points 100 basis points is 1 percent is a ratio of 0.01. The three units differ only by powers of ten, so converting between them is moving a decimal point, and `convertRate` does exactly that on the text of the number. `"0.07"` as a ratio is `"7"` percent, where `0.07 * 100` in floating point is `7.000000000000001`. Values are decimal strings in and out, never floats, so any rate a person can write down converts without loss and without a size limit. Output is the shortest form: no leading zeros, no trailing fractional zeros, no trailing point, and zero is `"0"` (never `"-0"`). Input is strict: an optional leading `-`, digits, and optionally `.` followed by digits. `"12.5%"`, `"+5"`, `".5"`, `"5."`, `"1e3"`, `"1,5"` and spaces are all errors. Strip a `%` sign before calling; this is not a parser for free-form text. The registry keeps rates as integer basis points, so two functions cover the common edges. `toBasisPoints(value, unit)` returns an integer and refuses a rate that is not a whole number of basis points (12.345% is 1234.5 basis points, which no integer holds; rounding it is the caller's policy) or is beyond ±(2^53 - 1). `fromBasisPoints(bp, unit)` renders an integer basis-point rate as the shortest decimal string in another unit. This is a group of three functions, each in its own file. `toBasisPoints` and `fromBasisPoints` are built on `convertRate`, so installing either with `only=` brings `convertRate` too. ## What changed from 1.0.0 1.0.0 was one function, `convertRate`, with `toBasisPoints` and `fromBasisPoints` exported beside it but unpinned: no signature in the manifest and no vectors. 2.0.0 is a group in which both are published functions with their own signatures and vectors. `convertRate` is unchanged and keeps every 1.0.0 vector. `fromBasisPoints` changed behaviour, because the three languages did not agree on it in 1.0.0: TypeScript refused a value beyond ±(2^53 - 1) and a fraction ("basisPoints must be a safe integer"), Python refused only a non-integer (with a different message) and converted any size, and Rust converted any `i64`. In 2.0.0 all three refuse a fraction with "basisPoints must be an integer" and a value beyond ±(2^53 - 1) with "basisPoints is outside the safe integer range". `toBasisPoints` gives the same answers and errors as before. That behaviour change, and the move to one module per function (the group module `math_basis_points` still re-exports all three; the Python module no longer exports its `MAX_SAFE` constant), make this a major version. Nothing in the registry depended on 1.0.0.