Floating in Uncertainty


Among the primitive data types that Objective-C offers are float and double. Similar to long, double is simply double the storage in bits of float. However, as the name suggests, both are floating-point numbers. That is, the decimal point literally “floats” around as necessary. This can lead to many subtle bugs, even between executions of the same program.

How Do They Work?

Both types have a certain numbers of bits for storage. The exact number of bits varies per implementation, just as the exact storage sizes for ints and doubles aren’t fixed. However, a floating point number effectively has two parts, the “whole” part and the “fractional” part. The floating nature comes in because the number of bits used to store each section is not always the same, and will vary depending on the sizes of the numbers involved.

The Precise Issue

Although there are an infinite number of integers, they all differ by the same amount—1. Therefore it is relatively easy to represent an integer value as bits. By contrast, however, the difference between one decimal number and another is incredibly small, and, what’s worse, this difference can change. When trying to squeeze an infinite number of digits with infinite precision into a finite number of bits, something has to give. This usually means that the floating-point value is rounded, and is not completely accurate. Here’s a simple program (using plain C) to illustrate this issue:

int main (int argc, const char *argv[]) {
	float f = 1234.123456789;
	printf("%f\n", f);
	printf("%.9f\n", f);
	return 0;
}

Output:

1234.123413
1234.123413086

The first print statement (printf() is nearly identical to NSLog(), except that it takes C-style strings instead) shows the actual value of f in memory, with ten digits in this case. Notice that the value is different from the value we assigned to the variable. This is because there are not enough bits to hold every single digit, so some of it had to be rounded away. That rounding is not perfectly accurate—it rounds the tailing ‘456789’ into ‘413’. If, however, we try to force it to display nine digits as seen in the next line, we still end up with the ‘413’, along with a tailing ‘086’—not digits that we put in.

Comparisons

How many times should this loop run?

#import <Foundation/Foundation.h>
int main (int argc, const char * argv[]){
	NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
	int count = 0;
	for (float i = 10; i != 0; i -= 0.1) {
		count++;
		printf("count is %d\n", count);
	}
	[pool drain];
    return 0;
}

Common sense tells us that it should run 100 times, but in fact this is an infinite loop. Because of these rounding errors, i will never be exactly equal to zero. It’ll come close after 100 cycles through, but it won’t be perfect. As such, when dealing with floating point numbers, you want to check to see if it’s close to a value. In the above example, the comparison should be i >= 0; (the equal bit because the floating point number might be a tiny negative value) to ensure that the loop ends.

To check if two floating point numbers are equal, you could use the == (double equals) operator, but there’s no guarantee that they will give an accurate result, especially after many calculations. A better way (albeit less efficient) would be to get the absolute value of their difference, and see if it’s less than a small number, such as 0.00001. If it’s less, you can safely assume they’re equal; otherwise, they’re not.

Floating point inaccuracies can be very difficult to debug, so it pays off to take precautions earlier. As a matter of fact, I spent about half an hour trying to figure out why something wasn’t showing up on screen, when in fact a botched conversion from integers to floating point (similar issues result) lead to everything being 0. Not fun.

Extension 12: Variable Scope


You can control a variable’s scope, or from where in your program it is accessible.

Language Directives

Up until now, we have seen that instance methods can access instance variables—a class can access its own variables (makes sense, and that’ll never change). More interesting is that subclasses can also access those same instance variables—that’s the default option. But you can change this:

  • @protected: The default case, the variable can be accessed by methods in the defining class and all subclasses.
  • @private: The instance variable can be accessed by instance methods in the defining class, but subclasses can not.
  • @public: Any class can access the variable, provided that the header of the defining class is imported, as usual.
  • @package: Any class within the same framework can access the variable. For example, a @package variable defined in NSString could be accessed by any other class in the Foundation framework, but nowhere else.

These directives act as switches, signifying that all the variables that follow are to be of that scope until another directive or a closing brace is encountered.

@interface Printer : NSObject
{
     @private
          NSInteger jobPageCount;
          double tonerLevelPercentage;
     @protected
          NSString *brand;
}
// …
@end

The @public directive allows the variable to be accessed by other methods outside of that class. Making instance variables public is considered a poor programming practice because it defeats the purpose of data encapsulation (a class hiding its instance variables).

External Global Variables

If you declare a variable, such as int globalVar = 0; outside any method or block, it is known as a global variable—it is accessible to any method or block in that file, even if you declare multiple classes (as we did in the beginning) in one file.

As a matter of fact, declaring the variable as such makes it accessible to any file that #imports it as any other variable—in this sense, it is a global variable.

To declare an external variable specifically (such as within a block, to be used in other blocks), you can use the extern keyword:

extern int extVar;

As a matter of fact, you should not assign extVar a value. The extern keyword tells the compiler that a variable should be external. The keyword is a declaration, not a definition.

Note that you can only define a global variable once, but you can specify any variable as being extern any number of times.

Static Variables

At times there may be a need to declare a variable as global but not external—that is, anything in the file can access the variable, but it cannot be accessed in another file. This is where you would use the static keyword.

Placing a line such as static int allocCounter = 0; outside any method in a file makes such a static variable. One practical application is that you can use this as a “class variable” of sorts—in this case, you would be able to use this variable to keep track of how many instances of a class were allocated, perhaps by overriding the -init method:

// Fraction.m
static int instancesCounter = 0;

- (id)init {
  extern int instancesCounter;
  instancesCounter++;
  return [super init];
}

Here, access to instancesCounter is limited to any method within the file.

  • Welcome

    My goal is to make CupsOfCocoa into a beautiful source for beginners to the iPhone platform to get started. Subscribe below for more, and stay tuned!

  • Contact Me

    If you need to contact me for any reason, feel free to send me an email.
  • The Giving Spirit

    If you've found this site helpful, would you consider donating a little sum? Any amount is appreciated...Thanks so much!

  • Roadmap

  • Enter your email address to follow this blog and receive notifications of new posts by email.

    Join 220 other followers

  • Back to the Past

    September 2020
    S M T W T F S
     12345
    6789101112
    13141516171819
    20212223242526
    27282930  
  • Time Machine

  • You count!

    • 622,768 views
  • Worldwide Stats

    free counters
%d bloggers like this: