Reputation: 1269
I'm working on an image scaling application, and I'm given a scaling ratio and a rectangle. I need to multiply the rectangle with the ratio but also make sure that the resulting rectangle is composed of only whole numbers (i.e no floating points). If not, I want to expand the source Rectangle so that the above condition is true.
At the moment I'm brute forcing it (code below). But I'm pretty sure there's a way of doing this using clever math.
RECT AdjustRectWholeNumber(RECT SrcRect, float ratio)
{
RECT adjustedRect = SrcRect;
while (!isWholeNumber(adjustedRect.left * ratio))
{
adjustedRect.left--;
}
while (!isWholeNumber(adjustedRect.top * ratio))
{
adjustedRect.top--;
}
while (!isWholeNumber(adjustedRect.right * ratio))
{
adjustedRect.right++;
}
while (!isWholeNumber(adjustedRect.bottom * ratio))
{
adjustedRect.bottom++;
}
return adjustedRect;
}
Example: Given rect (78,188,1068,790) and ratio 1.25, it should return (76, 188, 1068, 792)
Reasoning: If I was to multiply (78,188,1068,790) by 1.25, it would give me (97.5, 188, 1068, 987.5). This is not ideal because if I round (up or down) the scaled rectangle I lose precision which shows up as small corruption artifacts on the screen. Where as if we expand the source rectangle such that after multiplying by the scaling factor we get all whole numbers, no precision is lost, and thereby no corruption is seen.
NOTE: The scaling ratio is guaranteed to be able to produce whole numbers
Any help is appreciated!
EDIT: Adding code for isWholeNumber
as requested
bool isWholeNumber(float f)
{
if (floor(f) == f)
return true;
return false;
}
To clarify let's assume that the only ratios I will ever get are (1.25, 1.5, 1.75, 2.0, 2.25)
Upvotes: 0
Views: 553
Reputation: 23788
I don't think that generally you issue as stated has a good solution. Not all "good" fractions has exact representation as a floating point number (for example 4/3 doesn't). Generally exact representation is possible only for those fractions that has a power of 2 as a denominator. And if you try to use float
approximation for "bad" fractions as is, you'll get a fraction with a denominator of about 2^-20
i.e. around 10^-6
fitting which makes no sense. Thus the obvious suggestion is to use some explicit custom type to represent fractions as an explicit (integer) nominator and denominator. And if you have an explicit value for denominator the task becomes trivial. Alternatively if you are sure that
the only ratios I will ever get are (1.25, 1.5, 1.75, 2.0, 2.25)
you can have a simple function that would match values and say if the denominator is 1
, 2
or 4
. If you have more possible values but all fractions have power of 2 denominators, you also might consider using frexp
/frexpf
to get exponent (power of 2 of the denominator)
So now when you have an integer denominator, everything is very easy: you just need to find a number that is divisible by it. You can do it with integer division and multiplication (assuming your value is positive):
int roundDown(int value, int denominator) {
return (value / denominator) * denominator;
}
int roundUp(int value, int denominator) {
return ((value + denominator - 1) / denominator) * denominator;
}
Upvotes: 2