Dev vs Dev: Convert Integer to Binary as NSString

I recently was working on a project where I wanted to display an integer value as a binary string in Objective-C. Once I wrote what I thought were two decent implementations, I was curious to see how another developer would approach the same problem.

I asked Nick Lockwood if he would be up for coding up something similar. Bear in mind very limited requirements were provided upfront. The solution could be C function, a method or a category, and the signature for calling was undefined.

Read on to see four unique variations of how to convert an integer value into a binary NSString object.

Take 1: Convert Integer to Binary as NSString (John)

Given this is something that was meant to help with debugging, performance wasn’t a primary consideration. However, I still took the time see if I could come up with something that was unique and would be relatively efficient as well.

On my first pass I decided to use a C based array and fill each array element by masking off the rightmost bit of the incoming integer. I then shift the incoming value to the right, and go back through the same loop. Here’s how it looks:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
- (NSString *)intToBinary:(int)intValue
{
  int byteBlock = 8,            // 8 bits per byte
      totalBits = (sizeof(int)) * byteBlock, // Total bits
      binaryDigit = totalBits; // Which digit are we processing
 
  // C array - storage plus one for null
  char ndigit[totalBits + 1];
 
  while (binaryDigit-- > 0)
  {
    // Set digit in array based on rightmost bit
    ndigit[binaryDigit] = (intValue & 1) ? '1' : '0';
 
    // Shift incoming value one to right
    intValue >>= 1;
  }
 
  // Append null
  ndigit[totalBits] = 0;
 
  // Return the binary string
  return [NSString stringWithUTF8String:ndigit];
}

Let’s give the above a go with following:

int intValue = 16;
NSLog(@"int: %d", intValue);
NSLog(@"res: %@", [self intToBinary:intValue]);
 
intValue = -1;
NSLog(@"int: %d", intValue);
NSLog(@"res: %@\n\n", [self intToBinary:intValue]);
 
intValue = 2147483647;
NSLog(@"int: %d", intValue);
NSLog(@"res: %@\n\n", [self intToBinary:intValue]);
 
intValue = -2147483648;
NSLog(@"int: %d", intValue);
NSLog(@"bin: %@\n\n", [self intToBinary:intValue]);

Here’s how things look:

int: 16
bin: 00000000000000000000000000010000
 
int: -1
bin: 11111111111111111111111111111111
 
int: 2147483647
bin: 01111111111111111111111111111111
 
int: -2147483648
bin: 10000000000000000000000000000000

Take 2: Convert Integer to Binary as NSString (John)

Efficient or not, I wasn’t able to glean much from the output without a means to differentiate the bytes that make up the integer. Could the output be “pretty printed” using the C array approach, sure. Would that make for a mess of code? Most certainly. With that, I decided to give this another pass:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
- (NSString *)intToBinary:(int)intValue
{
  int byteBlock = 8,    // 8 bits per byte
      totalBits = sizeof(int) * byteBlock, // Total bits
      binaryDigit = 1;  // Current masked bit
 
  // Binary string
  NSMutableString *binaryStr = [[NSMutableString alloc] init];
 
  do
  {
    // Check next bit, shift contents left, append 0 or 1
    [binaryStr insertString:((intValue & binaryDigit) ? @"1" : @"0" ) atIndex:0];
 
    // More bits? On byte boundary ?
    if (--totalBits && !(totalBits % byteBlock))
      [binaryStr insertString:@"|" atIndex:0];
 
    // Move to next bit
    binaryDigit <<= 1;
 
  } while (totalBits);
 
  // Return binary string
  return binaryStr;
}

This approach is a little different, foregoing the C based array for an Objective-C mutable string. Also, instead of shifting the incoming value to get each bit, this time I shift the bit to check, going from the least to most significant. I was also looking to create a more readable output, which is covered by the code on lines 16-17 – when on a byte block (8 bits have been accounted for), output a vertical bar.

I generally write verbose code, and this little exercise was no different. For example, the values in lines 3 – 4 could be hardcoded and allocated inside the do loop. However, I am typically willing to make such a tradeoff for portability and/or readability.

Here’s the output for pass two:

int: 16
bin: 00000000|00000000|00000000|00010000
 
int: -1
bin: 11111111|11111111|11111111|11111111
 
int: 2147483647
bin: 01111111|11111111|11111111|11111111
 
int: -2147483648
bin: 10000000|00000000|00000000|00000000

Take 3: Convert Integer to Binary as NSString (Nick)

When John asked me to create a method to output a binary string from an integer, my first instinct was of course to check on Stack Overflow.

I’m a programmer and I enjoy solving programming problems, but I’ve always thought that effort is better spent solving new problems, not ones that are already solved. I anticipated that there would be a built-in solution, perhaps using the [NSString stringWithFormat:] method, or NSNumberFormatter. I was surprised to find that there was not.

As I learned from this question thread, some C compilers include a %b option in their printf implementations for printing binary strings, but it’s not a standard feature, and not included in the implementation included with Mac or iOS.

Before solving the problem of actually generating the string, I wanted to think about what the code interface would look like. When printing a number as a string, I usually use stringWithFormat: or NSNumberFormatter, so ideally I’d like my binary formatter to tie into one of these mechanisms.

NSNumberFormatter does not seem to support any means to extend the formatting options. Of course, I could add additional formatting properties using a category, but in order to make use of them I would have to swizzle the stringFromNumber: method, which is a hack too far for my liking.

The stringWithFormat: method is similarly unextensible. You can create custom descriptions for Objective-C classes by overriding their description method, but swizzling NSNumber’s description method to return a binary string would obviously be a bad idea (it would break code that expects it to return a decimal string), and creating a custom class to wrap an integer just so I can print it seems like an absurd degree of over-engineering.

I did find mention of a promising feature in glib called register_printf_function() that allows you to specify custom printf format characters. There’s an example of doing this here. Sadly, register_printf_function() doesn’t seem to be implemented in Mac or iOS – presumably it’s considered a security risk, or just wasn’t a priority.

I resigned myself that there would be no non-hacky way to integrate the binary string formatter into the existing Objective-C formatting mechanisms, and turned to the problem itself:

There were numerous solutions to actually generating the binary string on the Stack Overflow page I found earlier, but they felt like very “C-like” solutions. I decided to write something from scratch that makes more use of Cocoa methods and types to improve readability. It sacrifices some raw performance in the process, but unless performance is specified as a requirement, I prefer to optimise for code clarity first.

Here is my solution:

- (NSString *)binaryStringWithInteger:(NSInteger)value
{
  NSMutableString *string = [NSMutableString string];
  while (value)
  {
    [string insertString:(value & 1)? @"1": @"0" atIndex:0];
    value /= 2;
  }
  return string;
}

I think this is the shortest solution I’ve seen, as well as being quite simple to understand. The basic principle is the same as most other solutions I’ve come across – In a loop, I sample the last bit of the number by ANDing the value with 1, then shift the value by dividing it by two. What makes it so concise is that it simply prepends digits to an NSMutableString to construct the number, instead of something more complex.

I could have used the >> shift operator instead of /, but personally I find the concept of an integer divide simpler to grasp, and I can never remember which way round the shift chevrons are supposed to go! (and it makes no difference to the compiler.)

There are differences in the output of this method and John’s solutions. Notably, I do not pad the number with zeros, so an input of 4 yields “100”, an input of 1 yields “1” and an input of 0 yields an empty string.

There remains a question of where to put this method; it could be a category on NSString, or some global utility class. I would probably implement it as category on NSNumber because it makes the interface cleaner at the small expense of having to box the input as an NSNumber. That would look like this:

@interface NSNumber (Binary)
 
- (NSString *)binaryString;
 
@end
 
@implementation NSNumber (Binary)
 
- (NSString *)binaryString
{
  NSMutableString *string = [NSMutableString string];
  NSInteger value = [self integerValue];
  while (value)
  {
    [string insertString:(value & 1)? @"1": @"0" atIndex:0];
    value /= 2;
  }
  return string;
}
 
@end

And you’d call it like this:

NSInteger input = 99;
NSString *output = [@(input) binaryString]; //1100011

Take 4: Convert Integer to Binary as NSString (Nick)

I wrote my original solution independently of John’s, with no instructions about the output format, and no sneak peak at his solution. After seeing John’s approach, I was curious about whether I could output in fixed-width and 8-bit delimited formats as he has done without sacrificing the concision of my solution. Here’s what I came up with for padding the bits:

- (NSString *)paddedBinaryString
{
  NSMutableString *string = [NSMutableString string];
  NSInteger value = [self integerValue];
  for (NSInteger i = 0; i < sizeof(value) * 8; i++)
  {
    [string insertString:(value & 1)? @"1": @"0" atIndex:0];
    value /= 2;
  }
  return string;
}

Pretty simple – we just replace the while loop with a for loop that counts up the number of bits in the value (which will be 64, on modern systems). What about adding the delimiters for each byte block?

- (NSString *)delimitedBinaryString
{
  NSMutableString *string = [NSMutableString string];
  NSInteger value = [self integerValue];
  for (NSInteger i = 0; i < sizeof(value) * 8; i++)
  {
    if (i && i % 8 == 0) [string insertString:@"|" atIndex:0];
    [string insertString:(value & 1)? @"1": @"0" atIndex:0];
    value /= 2;
  }
  return string;
}

We’ve added one extra line here that basically says if the loop counter is not 0 but is exactly divisible by 8, insert a pipe character.

I was pretty pleased with this, but then I looked closer and realised that if you pull out the magic numbers into variables, and swap the for loop for a do/while, the solutions are basically the same!

  1. In your Take 1, you have binaryBlock, but I think you meant byteBlock, otherwise the code will not compile.

  2. i think in Nick’s last example value = value ยป 1 seems much better

Comments are closed.