I have to implement some business logic that will round a number (typically a double) upwards. Given that this number comes from a third-party and I don't know how they come to calculate it I'm anticipating errors in it. My proposed routine to mitigate rounding errors (like Math.ceil(1.000000000000002) = 2) is as follows:
export const roundupDiscount = (discount: number) => {
// We have to give advantage to the customer for the discount value when rounding,
// that is to say, we will sacrifice the penny when we can't be exact. This means
// we have to always round the discount upwards (so we can't Math.round because
// half the time we'll round in the wrong direction.)
// To account for numbers like, say, 1.000000000000002 (i.e. (1 / 5 / 7) * 5 * 7))
// then we subtract off a "small" number (scaled with the magnitude of discount)
// which will nudge below the number we will round up to
if (!discount) return 0 // discount shouldn't be negative
const magnitude = Math.floor(Math.log(discount) / Math.log(10))
const epsilon = Math.pow(10, magnitude - 14)
return Math.ceil(discount - epsilon)
}
Given that a double has between 15-17 significant digits in it, I'm choosing epsilon, arbitrarily, such that I have a couple of trailing digits to play with.
Can anybody spot any cases where this is going to get things wrong? simplify this thing, or, given this is node.js point me towards an npm module that does this kind of thing for me?
Note: I have to get this past code-review at work so I'd much prefer if there was a good library out there that I can use instead and not even bother submitting this!
roundupDiscount
should do, not more on how it works, but what it should do. \$\endgroup\$