Reputation: 43
Normally, when we want to test fractional numbers for equality, we do so with some amount of uncertainty, because of approximate nature of IEEE754.
if (fabs(one_float - other_float) < SUFFICIENTLY_SMALL) {
consider them equal;
}
Other approach might be to cast the floats to integers of specific magnitude, and compare resulting integers instead.
if ((uint)one_float == (uint)other_float) {
consider them equal;
}
But consider the situation where our floats never undergo any arithmetics, and the only thing we do with them is assignment.
// Might be called at any moment
void resize(float new_width, float new_height)
{
if (current_width == new_width && current_height == new_height) {
return;
}
accomodate framebuffers;
update uniforms;
do stuff;
current_width = new_width;
current_height = new_height;
}
In the example above, we have an event handler that may fire up spontaneously, and we want to reallocate resources only if real resize occurred. We might take the usual path with approximate comparison, but that seems like a waste, because that handler will fire up pretty often. As far as I know, floats are assigned just as everything else, using memory move; and equality operator performs just the memory comparison. So it looks like we are in a safe harbour. When checked on x86 and ARM, this assumption holds, but I just wanted to be sure. Maybe it's backed by some specs?
So, is there something that might change floats when only assigning them? Is the described approach viable?
Upvotes: 4
Views: 1200
Reputation: 6136
In the case you mentioned it is safe to use ==
to compare because of the following lines at the end of the function:
current_width = new_width;
current_height = new_height;
any change to new_width
or new_height
will fail the if statement and you get the behavior you wanted.
abs()
function usually is used when there is one variable float which is assigned dynamically in the program and one constant float which you want to use as a reference. Something like:
bool isEqual(float first, float second)
{
if(fabs(first-second)<0.0001)
return true;
return false;
}
int main()
{
float x = (float) 1 / 3;
if(isEqual(x,0.3333))
printf("It is equal\n");
else
printf("It is not equal\n");
}
Upvotes: 1
Reputation: 31153
A mass of "should"s follows.
I don't think there's anything that says an assignment from float to float (current_width = new_width
) couldn't alter the value, but I would be surprised if such a thing existed. There should be no reason to make assignment between variables of the same type anything else than a direct copy.
If the incoming new_width
and new_height
keep their values until they change, then this comparison should not have any issues. But if they are calculated before every call they might change their value, depending on how the calculation is done. So it's not only this function that needs to be checked.
The C 2011 standard says that calculations may use bigger precision than the format you assign to, but nothing specific about assigning a variable to another. So the only imprecision should be in the calculation stage. The "simple assignment" part (6.5.16.1) says:
In simple assignment (=), the value of the right operand is converted to the type of the assignment expression and replaces the value stored in the object designated by the left operand.
So if the types already match, there should be no need for conversion.
So, simply put: if you don't recalculate the incoming value on every call the comparison for equality should hold true. But is there really a case where your framebuffers are sized as floats and not integers?
Upvotes: 3
Reputation: 11648
Have a look here for how to compare two floats.
https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
Find the test with "ULP, he said nervously". You can compare the floats as ints but have a look at how it's been done in the article ie (I hope I'm not breaching any licensing here, if so please delete the code that follows it's not mine)
bool AlmostEqualUlps(float A, float B, int maxUlpsDiff){
Float_t uA(A);
Float_t uB(B);
// Different signs means they do not match.
if (uA.Negative() != uB.Negative()) {
// Check for equality to make sure +0==-0
if (A == B) {
return true;
}
return false;
}
// Find the difference in ULPs.
int ulpsDiff = abs(uA.i - uB.i);
if (ulpsDiff <= maxUlpsDiff) {
return true;
}
return false;
}
Please read the rest of that article, it's really good.
Upvotes: -1