Question
Comparing CGFloat Floating-Point Values in Objective-C
Question
In UIKit, CGFloat is used for resolution-independent coordinates. Is it safe to compare a view's frame.origin.x directly with 0?
if (theView.frame.origin.x == 0) {
// Perform an important operation.
}
Because CGFloat is a floating-point type, can rounding cause a value that appears to be zero to fail this comparison? Does Objective-C handle floating-point precision internally for operators such as ==, <=, >=, <, and >?
Short Answer
You will learn how CGFloat floating-point comparisons work, when an exact comparison with zero is appropriate, and when to use a tolerance for values produced by calculations. You will also see reliable Objective-C patterns for comparing UIKit coordinates.
Concept
CGFloat is a floating-point type used by Core Graphics and UIKit for values such as positions, sizes, transforms, and drawing coordinates.
Floating-point numbers cannot represent every decimal value exactly. For example, a calculation that is mathematically expected to produce 0 can sometimes produce a tiny value such as 0.0000001 or -0.0000001 instead.
Objective-C does not automatically apply a tolerance when it evaluates these operators:
== != < > <= >=
They compare the actual stored floating-point values.
An exact comparison with zero is often safe when the value was explicitly assigned zero or came from an API that guarantees an exact zero:
view.frame = CGRectMake(0, 0, 100, 100);
if (view.frame.origin.x == 0) {
// This is expected to be true.
}
However, if the coordinate was created by arithmetic, animation, scaling, division, transforms, or accumulated updates, do not assume it will be exactly zero. In that situation, compare it to a tolerance that makes sense for your UI.
Also note that UIKit coordinates are allowed to be fractional. A view at x = 0.5 is valid, especially on high-density displays or when transforms are involved.
Mental Model
Think of a floating-point number as a measuring tape with a limited number of markings. It can record many values very accurately, but it cannot mark every possible decimal distance exactly.
If you put an object directly at the wall marked 0, checking whether it is at 0 is reliable. But if you move it left and right repeatedly using measurements, it may finish extremely close to the wall without landing on the exact 0 mark.
A tolerance is like saying, “If the object is within half a pixel of the wall, treat it as being at the wall.”
Syntax and Examples
Use an exact comparison when zero is a deliberate, known value:
CGFloat x = 0;
if (x == 0) {
NSLog(@"Exactly at the left edge");
}
Use a tolerance when values may have come from calculations:
#import <math.h>
CGFloat x = calculatedX;
CGFloat tolerance = 0.01;
if (fabs(x) < tolerance) {
NSLog(@"Close enough to zero");
}
fabs(x) returns the absolute distance from zero:
fabs(0.003)is0.003fabs(-0.003)is0.003fabs(0)is0
To compare two CGFloat values, compare the distance between them:
#import <math.h>
static inline BOOL CGFloatsAreNearlyEqual(CGFloat a, CGFloat b, CGFloat tolerance) {
return fabs(a - b) < tolerance;
}
if (CGFloatsAreNearlyEqual(theView.frame.origin.x, 0, 0.01)) {
// Treat the view as being at x = 0.
}
Choose the tolerance based on the meaning of the value. For a screen position, a small number of points such as 0.01 may be suitable in some cases. For a layout decision, a tolerance such as 0.5 points may better match the idea of “visually at the edge.”
Step by Step Execution
Consider this calculation:
#import <math.h>
CGFloat start = 0.1;
CGFloat movedRight = start + 0.2;
CGFloat returned = movedRight - 0.3;
NSLog(@"%.17g", (double)returned);
if (returned == 0) {
NSLog(@"Exactly zero");
}
if (fabs(returned) < 0.000001) {
NSLog(@"Effectively zero");
}
What happens:
startstores an approximation of decimal0.1.- Adding
0.2performs floating-point arithmetic on approximations. - Subtracting
0.3may leave a very small residual value rather than exact0. returned == 0checks whether the stored bits represent exactly zero. It can be false.fabs(returned) < 0.000001checks whether the residual is small enough to be irrelevant for this task. It can be true.
The exact printed value and behavior can vary by architecture because CGFloat is typically a double on modern 64-bit Apple platforms and was historically a float on 32-bit platforms. The important rule is unchanged: arithmetic can introduce rounding.
Real World Use Cases
Floating-point comparisons appear frequently in UIKit and Core Graphics code:
- Animation completion checks: Determine whether an animated position has reached a target position. Prefer the animation completion callback when available; otherwise use a tolerance for calculated positions.
- Custom drawing: Treat a line as horizontal when its two y-values are sufficiently close.
- Gesture handling: Decide whether a drag has moved far enough to count as movement.
- Layout rules: Detect whether a view is visually at a container edge.
- Zoom and transforms: Compare calculated scale values with an expected scale such as
1.0. - Geometry calculations: Detect nearly zero widths, heights, distances, or velocities before dividing by them.
For values representing pixels or points, the best tolerance is usually a product requirement, not a language constant. Ask: how close must these values be before the difference is invisible or unimportant?
Real Codebase Usage
In production code, developers usually avoid scattering raw floating-point comparisons throughout view code. Instead, they use clear intent-based helpers and layout APIs.
Validate values before using them
#import <math.h>
CGFloat width = CGRectGetWidth(view.bounds);
if (!isfinite(width) || width <= 0) {
return;
}
CGFloat scale = 200.0 / width;
This guard clause prevents invalid calculations when a value is not finite or cannot be used as a divisor.
Use a named comparison helper
#import <math.h>
static inline BOOL IsNearlyZero(CGFloat value, CGFloat tolerance) {
return fabs(value) < tolerance;
}
if (IsNearlyZero(view.frame.origin.x, 0.5)) {
// The view is visually at the leading edge.
}
A named helper makes the business decision visible: this is not exact mathematical equality; it is a UI-specific definition of “at the edge.”
Prefer constraints for layout state
When Auto Layout controls placement, checking frame.origin.x is often the wrong source of truth. Store and inspect the relevant constraint instead:
if (leadingConstraint.constant == 0) {
// The layout rule has no leading offset.
}
If that constant is calculated, apply a tolerance there as well.
Use framework callbacks for completion
For animations, prefer APIs that report completion rather than polling a frame value:
[UIView animateWithDuration:0.25 animations:^{
self.panelView.frame = CGRectMake(0, 0, 200, 200);
} completion:^(BOOL finished) {
if (finished) {
// The animation completed normally.
}
}];
Common Mistakes
Assuming displayed output is the stored value
A debugger or log may round a tiny value and display it as 0.00. That does not mean the stored value is exactly zero.
CGFloat x = 0.00004;
NSLog(@"%.2f", (double)x); // Prints 0.00
if (x == 0) {
// This does not run.
}
Use more precision while diagnosing a problem:
NSLog(@"%.17g", (double)x);
Using CGFLOAT_EPSILON as a general UI tolerance
CGFLOAT_EPSILON describes spacing near 1.0 for the underlying floating-point representation. It is usually far too small to represent a useful visual or layout tolerance.
// Usually not a meaningful UI rule.
if (fabs(x) < CGFLOAT_EPSILON) {
// ...
}
Instead, choose a tolerance based on the coordinate system and the feature's requirement.
Using only an absolute tolerance for very large values
For values that may be very large, a fixed tolerance can be inappropriate. A relative comparison can be useful:
#import <math.h>
static inline BOOL CGFloatsAreNearlyEqualRelative(CGFloat a, CGFloat b, CGFloat tolerance) {
CGFloat difference = fabs(a - b);
CGFloat largest = MAX(fabs(a), fabs(b));
return difference <= tolerance * MAX(1.0, largest);
}
For ordinary UIKit frame coordinates, a simple absolute tolerance is often easier and more appropriate.
Comparisons
| Situation | Recommended approach | Why |
|---|---|---|
Value was explicitly assigned 0 | value == 0 | Exact zero is exactly representable, and no calculation introduced rounding. |
| Value came from arithmetic | fabs(value) < tolerance | Handles tiny positive and negative rounding residuals. |
| Compare two calculated values | fabs(a - b) < tolerance | Tests whether their difference is insignificant. |
| Check a lower or upper boundary from calculations | Include a tolerance in the boundary rule | Prevents tiny rounding differences from changing the decision. |
| Animation finished | Completion callback | The framework reports completion more reliably than frame polling. |
| Auto Layout state | Inspect constraints or layout state |
Cheat Sheet
#import <math.h>
// Exact comparison: safe for a deliberately assigned zero.
if (x == 0) {
// ...
}
// Is a calculated value close to zero?
if (fabs(x) < 0.01) {
// ...
}
// Are two calculated values close?
if (fabs(a - b) < 0.01) {
// ...
}
// Reject NaN and infinity before a critical calculation.
if (!isfinite(x)) {
return;
}
CGFloatis floating-point; it may bedoubleorfloatdepending on the platform architecture.- Objective-C comparison operators do not automatically use a tolerance.
0.0is exactly representable in binary floating point.-0.0 == 0.0is true.- Use exact equality for known, deliberately assigned values.
- Use a domain-appropriate tolerance for calculated values.
- Do not use
CGFLOAT_EPSILONas a default visual/layout tolerance. NaNis not equal to itself; validate potentially invalid results withisfinite.
FAQ
Is CGFloat == 0 safe in Objective-C?
Yes, when the value was explicitly set to zero or is guaranteed by an API to be exact zero. It may be unreliable when the value is the result of floating-point arithmetic.
Does Objective-C fix floating-point comparison errors automatically?
No. Operators such as ==, <, and >= compare the stored values directly. Objective-C does not silently apply an epsilon or tolerance.
Can frame.origin.x be a fractional value?
Yes. UIKit uses points, and coordinates can be fractional due to scaling, transforms, animations, and layout calculations.
Should I always use an epsilon to compare CGFloat values?
No. Use exact comparison for deliberately assigned constants. Use a tolerance only when calculations can create rounding differences or when your requirement is inherently approximate.
What tolerance should I use for UIKit coordinates?
Choose one based on the UI behavior. For a visually meaningful edge check, 0.5 points may be reasonable in some interfaces. For a mathematical calculation, you may need a smaller value. There is no universal correct tolerance.
Is CGFLOAT_EPSILON the right tolerance for comparing view positions?
Usually not. It measures floating-point precision near 1.0, not the distance that is meaningful in your UI.
Mini Project
Description
Build a small Objective-C utility that decides whether a draggable panel is at the left edge of its container. The panel's x-position may be computed during dragging, so the project uses a visual tolerance rather than exact equality.
Goal
Report whether a panel is at the left edge, near the left edge, or away from the edge using safe CGFloat comparisons.
Requirements
Requirement 1
Keep learning
Related questions
2D Array Loop Order and Cache Performance in C
Learn why swapping nested loops changes 2D array performance in C, using row-major memory layout, cache locality, and practical benchmarks.
Array-to-Pointer Conversion in C and C++ Explained
Learn what array-to-pointer conversion means in C and C++, how array decay works, and how it differs from a pointer to an array.
Building More Fault-Tolerant Embedded C++ Applications for Radiation-Prone ARM Systems
Learn practical C++ and compile-time techniques to reduce soft-error damage in embedded ARM systems exposed to radiation.