Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prebid Core: Allow Cpm Custom Rounding #9018

Merged
merged 5 commits into from
Oct 6, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions src/cpmBucketManager.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {find} from './polyfill.js';
import { isEmpty } from './utils.js';
import { config } from './config.js';

const _defaultPrecision = 2;
const _lgPriceConfig = {
Expand Down Expand Up @@ -118,17 +119,30 @@ function getCpmTarget(cpm, bucket, granularityMultiplier) {
const precision = typeof bucket.precision !== 'undefined' ? bucket.precision : _defaultPrecision;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change LGTM, but isn't the use case for this already covered by this configurable precision (as explained here)? This is probably more about documentation, it's not clear to me why I'd want to use one over the other.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Precision states where the rounding will occur, but the rounding is always down. This change allows for other rounding methods.

e.g.
Current
cpm = 2.1456, increment = .05, precision = 3
precise cpm = 2.145, rounded down to nearest increment = 2.10
Final result = 2.100

With alternate rounding
cpm = 2.1456, increment = 0.5 precision = 4
precise cpm = 2.1456, rounded with Math.round to nearest increment = 2.15
Final result = 2.1500

At least, that's how I understand it. As far as I can tell, the precision determines what is being rounded down, but it is always rounded down, and always to the nearest increment, which in effect just tacks extra zeroes to the end of the result.

Then again, I'm mostly basing that off the unit tests, which aren't super comprehensive for precision, and the documentation, which only shows the default of 2. If precision is supposed to affect rounding in any way, I don't get it. As long as the precision is greater than or equal to the number of decimal places in the increment, rounding will be unaffected by precision, which makes this change useful.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, I thought you could get the equivalent of Math.round by increasing the precision by 1, but that's not exactly what happens.

const increment = bucket.increment * granularityMultiplier;
const bucketMin = bucket.min * granularityMultiplier;
let roundingFunction = Math.floor;
let customRoundingFunction = config.getConfig('cpmRoundingFunction');
if (typeof customRoundingFunction === 'function') {
roundingFunction = customRoundingFunction;
}

// start increments at the bucket min and then add bucket min back to arrive at the correct rounding
// note - we're padding the values to avoid using decimals in the math prior to flooring
// this is done as JS can return values slightly below the expected mark which would skew the price bucket target
// (eg 4.01 / 0.01 = 400.99999999999994)
// min precison should be 2 to move decimal place over.
let pow = Math.pow(10, precision + 2);
let cpmToFloor = ((cpm * pow) - (bucketMin * pow)) / (increment * pow);
let cpmTarget = ((Math.floor(cpmToFloor)) * increment) + bucketMin;
let cpmToRound = ((cpm * pow) - (bucketMin * pow)) / (increment * pow);
let cpmTarget;
// It is likely that we will be passed {cpmRoundingFunction: roundingFunction()}
// rather than the expected {cpmRoundingFunction: roundingFunction}. Default back to floor in that case
try {
cpmTarget = (roundingFunction(cpmToRound) * increment) + bucketMin;
} catch (err) {
cpmTarget = (Math.floor(cpmToRound) * increment) + bucketMin;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we log a warning here so easier to debug when this exception occurs?

}
// 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);
}
Expand Down
Loading