-
-
Save CommanderPho/f975e54fbe10d8f1d5f483bf4fa734a7 to your computer and use it in GitHub Desktop.
Arithmetic operators that throw on integer type overflow and floating point type errors.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// ThrowingMathematicalOperators.swift | |
// Dose | |
// | |
// Created by Pho Hale on 6/26/18. | |
// Copyright © 2018 Pho Hale. All rights reserved. | |
// | |
import Darwin | |
import Swift | |
import CoreGraphics | |
// MARK: Operators | |
infix operator &&+: AdditionPrecedence | |
infix operator &&-: AdditionPrecedence | |
infix operator &&*: MultiplicationPrecedence | |
infix operator &&/: MultiplicationPrecedence | |
infix operator &&%: MultiplicationPrecedence | |
prefix operator &&? | |
// MARK: Error type | |
public struct ArithmeticOperationError: Error { | |
public enum OverflowType { | |
case overflow | |
case nan | |
case infinity | |
} | |
public enum Operation { | |
case addition | |
case subtraction | |
case multiplication | |
case division | |
case remainder | |
case other(String?) | |
} | |
let type:OverflowType | |
let operation:Operation | |
init(_ type:OverflowType, _ operation:Operation) { | |
self.type = type | |
self.operation = operation | |
} | |
} | |
// MARK: Integer operator implementations | |
public func &&+<T:FixedWidthInteger>(lhs:T,rhs:T) throws -> T { | |
let (result,overflow) = lhs.addingReportingOverflow(rhs) | |
guard !overflow else { | |
throw ArithmeticOperationError(.overflow,.addition) | |
} | |
return result | |
} | |
public func &&-<T:FixedWidthInteger>(lhs:T,rhs:T) throws -> T { | |
let (result,overflow) = lhs.subtractingReportingOverflow(rhs) | |
guard !overflow else { | |
throw ArithmeticOperationError(.overflow,.subtraction) | |
} | |
return result | |
} | |
public func &&*<T:FixedWidthInteger>(lhs:T,rhs:T) throws -> T { | |
let (result,overflow) = lhs.multipliedReportingOverflow(by: rhs) | |
guard !overflow else { | |
throw ArithmeticOperationError(.overflow,.multiplication) | |
} | |
return result | |
} | |
public func &&/<T:FixedWidthInteger>(lhs:T,rhs:T) throws -> T { | |
// let (result,overflow) = T.divideWithOverflow(lhs,rhs) | |
let (result,overflow) = lhs.remainderReportingOverflow(dividingBy: rhs) | |
guard !overflow else { | |
throw ArithmeticOperationError(.overflow,.division) | |
} | |
return result | |
} | |
public func &&%<T:FixedWidthInteger>(lhs:T,rhs:T) throws -> T { | |
// let (result,overflow) = T.remainderWithOverflow(lhs,rhs) | |
let (result,overflow) = lhs.remainderReportingOverflow(dividingBy: rhs) | |
guard !overflow else { | |
throw ArithmeticOperationError(.overflow,.remainder) | |
} | |
return result | |
} | |
// MARK: Floating point helpers | |
private func enforce<T:FloatingPoint>(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:FloatingPoint>(value:T,_ debug:String? = nil) throws -> T { | |
return try enforce(value: value, .other(debug)) | |
} | |
public prefix func &&?<T:FloatingPoint>(value:T) throws -> T { | |
return try throwing(value: value) | |
} | |
// MARK: Encapsulating Double, Float, and CGFloat | |
public protocol FloatingPointArithmeticType: FloatingPoint { | |
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(value: lhs + rhs, .addition) | |
} | |
public func &&-<T:FloatingPointArithmeticType>(lhs:T,rhs:T) throws -> T { | |
return try enforce(value: lhs - rhs, .subtraction) | |
} | |
public func &&*<T:FloatingPointArithmeticType>(lhs:T,rhs:T) throws -> T { | |
return try enforce(value: lhs * rhs, .multiplication) | |
} | |
public func &&/<T:FloatingPointArithmeticType>(lhs:T,rhs:T) throws -> T { | |
return try enforce(value: lhs / rhs, .division) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment