Even and Odd Integers

I think this would be a useful addition to the standard library. In fact, I started sketching out a pitch before I found this post! :slight_smile:

I've included my pitch below which includes more details about how it satisfies the criteria mentioned in the toggle proposal.

Let me know what you think @SiliconUnicorn and others. Maybe we can team up and see if we can get some momentum behind this.

Introduction

I propose adding var isEven: Bool and var isOdd: Bool to BinaryInteger. These are convenience properties for querying the parity of the integer.

Motivation

It is sometimes useful to know the evenness or oddness (parity) of an integer and switch the behaviour based on the result. The most typical way to do this is using value % 2 == 0 to determine if value is even, or value % 2 != 0 to determine if value is odd.

// Gray background for even rows, white for odd.
view.backgroundColor = indexPath.row % 2 == 0 ? .gray : .white

// Enable button if we have odd number of photos
buttonSave.isEnabled = photos.count % 2 != 0

It is also possible to use the bitwise AND operator (value & 1 == 0) which will inevitably lead to discussions about which one is faster and attempts at profiling them, etc, etc.

There are a few more specific motivations for this proposal:

Commonality

The need to determine the parity of an integer isn’t restricted to a limited problem domain.

Readability

This proposal significantly improves readability. There is no need to understand operator precedence rules (% has higher precedence than ==) which are non-obvious.

The properties are also fewer characters wide than the modulus approach (maximum 7 characters for .isEven vs 9 for % 2 == 0) which saves horizontal space while being clearer in intent.

view.backgroundColor = indexPath.row % 2 == 0 ? .gray : .white
view.backgroundColor = indexPath.row.isEven ? .gray : .white

buttonSave.isEnabled = photos.count % 2 != 0
buttonSave.isEnabled = photos.count.isOdd

Discoverability

Determining whether a value is even or odd is a common question across programming languages, at least based on these Stack Overflow questions:
c - How do I check if an integer is even or odd? 300,000+ views
java - Check whether number is even or odd 350,000+ views
Check if a number is odd or even in python 140,000+ views

IDEs will be able to suggest .isEven and .isOdd as part of autocomplete which will aid discoverability.

Consistency

It would be relatively easy to reproduce the properties in user code but there would be benefits to having a standard implementation. It may not be obvious to some users exactly which protocol these properties belong on (Int?, SignedInteger?, FixedWidthInteger?, BinaryInteger?). This inconsistency can be seen in a popular Swift utility library which defines these properties on SignedInteger which results in the properties being inaccessible for unsigned integers.

These properties will also eliminate the need to use modulus 2 and bitwise AND 1 to determine parity.

Adding isEven and isOdd is also consistent with the .isEmpty utility property, which is a convenience for .count == 0.

if array.count == 0 { ... }
if array.isEmpty { ... }

if value % 2 == 0 { ... }
if value.isEven { ... }

Correctness

There is a minor correctness risk in misinterpreting something like value % 2 == 0, particularly when used in a more complex statement, when compared to value.isEven.

Performance

This proposal likely won’t have a major positive impact on performance but it should not introduce any additional overhead thanks to @inlineable.

Proposed solution

Add two computed properties, isEven and isOdd, to BinaryInteger

extension BinaryInteger {
    @inlinable
    /// A Boolean value indicating whether this value is even.
    ///
    /// An integer is even if it is evenly divisible by two.
    public var isEven: Bool {
        return self % 2 == 0
    }

    @inlinable
    /// A Boolean value indicating whether this value is odd.
    ///
    /// An integer is odd if it is not evenly divisible by two.
    public var isOdd: Bool {
        return self % 2 != 0
    }
}

Similar functionality exists on Ruby’s Integer type: even? and odd?.

11 Likes