Skip to content

Instantly share code, notes, and snippets.

@wircho
Created August 4, 2016 15:04
Show Gist options
  • Save wircho/c382d297ad07cf6b705246abd20b47f9 to your computer and use it in GitHub Desktop.
Save wircho/c382d297ad07cf6b705246abd20b47f9 to your computer and use it in GitHub Desktop.
Arithmetic operators that throw on integer type overflow and floating point type errors.
/*
* ThrowingOperators.swift
*/
import Darwin
import CoreGraphics
// MARK: Operators
infix operator &&+ {associativity left precedence 140}
infix operator &&- {associativity left precedence 140}
infix operator &&* {associativity left precedence 150}
infix operator &&/ {associativity left precedence 150}
infix operator &&% {associativity left precedence 150}
prefix operator &&? {}
// MARK: Error type
public struct ArithmeticOperationError: ErrorType {
public enum Type {
case overflow
case nan
case infinity
}
public enum Operation {
case addition
case subtraction
case multiplication
case division
case remainder
case other(String?)
}
let type:Type
let operation:Operation
init(_ type:Type, _ operation:Operation) {
self.type = type
self.operation = operation
}
}
// MARK: Integer operator implementations
public func &&+<T:IntegerArithmeticType>(lhs:T,rhs:T) throws -> T {
let (result,overflow) = T.addWithOverflow(lhs,rhs)
guard !overflow else {
throw ArithmeticOperationError(.overflow,.addition)
}
return result
}
public func &&-<T:IntegerArithmeticType>(lhs:T,rhs:T) throws -> T {
let (result,overflow) = T.subtractWithOverflow(lhs,rhs)
guard !overflow else {
throw ArithmeticOperationError(.overflow,.subtraction)
}
return result
}
public func &&*<T:IntegerArithmeticType>(lhs:T,rhs:T) throws -> T {
let (result,overflow) = T.multiplyWithOverflow(lhs,rhs)
guard !overflow else {
throw ArithmeticOperationError(.overflow,.multiplication)
}
return result
}
public func &&/<T:IntegerArithmeticType>(lhs:T,rhs:T) throws -> T {
let (result,overflow) = T.divideWithOverflow(lhs,rhs)
guard !overflow else {
throw ArithmeticOperationError(.overflow,.division)
}
return result
}
public func &&%<T:IntegerArithmeticType>(lhs:T,rhs:T) throws -> T {
let (result,overflow) = T.remainderWithOverflow(lhs,rhs)
guard !overflow else {
throw ArithmeticOperationError(.overflow,.remainder)
}
return result
}
// MARK: Floating point helpers
private func enforce<T:FloatingPointType>(value:T,_ op:ArithmeticOperationError.Operation) throws -> T {
guard !value.isInfinite else {
throw ArithmeticOperationError(.infinity,op)
}
guard !value.isNaN else {
throw ArithmeticOperationError(.nan,op)
}
return value
}
public func throwing<T:FloatingPointType>(value:T,_ debug:String? = nil) throws -> T {
return try enforce(value,.other(debug))
}
public prefix func &&?<T:FloatingPointType>(value:T) throws -> T {
return try throwing(value)
}
// MARK: Encapsulating Double, Float, and CGFloat
public protocol FloatingPointArithmeticType: FloatingPointType {
static func +(_:Self,_:Self) -> Self
static func -(_:Self,_:Self) -> Self
static func *(_:Self,_:Self) -> Self
static func /(_:Self,_:Self) -> Self
}
extension Double: FloatingPointArithmeticType {}
extension Float: FloatingPointArithmeticType {}
extension CGFloat: FloatingPointArithmeticType {}
// MARK: Floating point operator implementations
public func &&+<T:FloatingPointArithmeticType>(lhs:T,rhs:T) throws -> T {
return try enforce(lhs + rhs, .addition)
}
public func &&-<T:FloatingPointArithmeticType>(lhs:T,rhs:T) throws -> T {
return try enforce(lhs - rhs, .subtraction)
}
public func &&*<T:FloatingPointArithmeticType>(lhs:T,rhs:T) throws -> T {
return try enforce(lhs * rhs, .multiplication)
}
public func &&/<T:FloatingPointArithmeticType>(lhs:T,rhs:T) throws -> T {
return try enforce(lhs / rhs, .division)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment