diff --git a/src/cpmBucketManager.js b/src/cpmBucketManager.js index 0061d35f483..c2250838bcb 100644 --- a/src/cpmBucketManager.js +++ b/src/cpmBucketManager.js @@ -100,7 +100,7 @@ function getCpmStringValue(cpm, config, granularityMultiplier) { } }); if (bucket) { - cpmStr = getCpmTarget(cpm, bucket.increment, bucket.precision, granularityMultiplier); + cpmStr = getCpmTarget(cpm, bucket, granularityMultiplier); } return cpmStr; } @@ -118,12 +118,17 @@ function isValidPriceConfig(config) { return isValid; } -function getCpmTarget(cpm, increment, precision, granularityMultiplier) { - if (typeof precision === 'undefined') { - precision = _defaultPrecision; - } - let bucketSize = 1 / (increment * granularityMultiplier); - return (Math.floor(cpm * bucketSize) / bucketSize).toFixed(precision); +function getCpmTarget(cpm, bucket, granularityMultiplier) { + const precision = typeof bucket.precision !== 'undefined' ? bucket.precision : _defaultPrecision; + const increment = bucket.increment * granularityMultiplier; + const bucketMin = bucket.min * granularityMultiplier; + + // start increments at the bucket min and then add bucket min back to arrive at the correct rounding + let cpmTarget = ((Math.floor((cpm - bucketMin) / increment)) * increment) + bucketMin; + // force to 10 decimal places to deal with imprecise decimal/binary conversions + // (for example 0.1 * 3 = 0.30000000000000004) + cpmTarget = Number(cpmTarget.toFixed(10)); + return cpmTarget.toFixed(precision); } export { getPriceBucketString, isValidPriceConfig }; diff --git a/test/spec/cpmBucketManager_spec.js b/test/spec/cpmBucketManager_spec.js index e5e2d03c66c..3d56299ebfd 100644 --- a/test/spec/cpmBucketManager_spec.js +++ b/test/spec/cpmBucketManager_spec.js @@ -35,6 +35,29 @@ describe('cpmBucketManager', () => { expect(JSON.stringify(output)).to.deep.equal(expected); }); + it('gets the correct custom bucket strings with irregular increment', () => { + let cpm = 14.50908; + let customConfig = { + 'buckets': [{ + 'precision': 4, + 'min': 0, + 'max': 4, + 'increment': 0.01, + }, + { + 'precision': 4, + 'min': 4, + 'max': 18, + 'increment': 0.3, + 'cap': true + } + ] + }; + let expected = '{"low":"5.00","med":"14.50","high":"14.50","auto":"14.50","dense":"14.50","custom":"14.5000"}'; + let output = getPriceBucketString(cpm, customConfig); + expect(JSON.stringify(output)).to.deep.equal(expected); + }); + it('gets the correct custom bucket strings in non-USD currency', () => { let cpm = 16.50908 * 110.49; let customConfig = {