Last active
June 13, 2021 14:45
-
-
Save therealbnut/50755d8e2192c140f41a3c0b94661c32 to your computer and use it in GitHub Desktop.
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
public struct BouyantPointNumber: Hashable { | |
public typealias Storage = Double | |
private var storage: Storage | |
@_transparent | |
private static var scaleFactor: Storage { | |
return 10.0 | |
} | |
@_transparent | |
private var scaleFactor: Storage { | |
return BouyantPointNumber.scaleFactor | |
} | |
private init(storage: Storage) { | |
self.storage = storage | |
} | |
private init(unscaled storage: Storage) { | |
self.storage = storage * BouyantPointNumber.scaleFactor | |
} | |
} | |
extension BouyantPointNumber: Comparable { | |
public static func < (lhs: BouyantPointNumber, rhs: BouyantPointNumber) -> Bool { | |
return lhs.storage < rhs.storage | |
} | |
} | |
extension BouyantPointNumber: SignedNumeric { | |
public typealias Magnitude = BouyantPointNumber | |
public typealias IntegerLiteralType = Storage.IntegerLiteralType | |
public init(integerLiteral value: Storage.IntegerLiteralType) { | |
self.init(unscaled: Storage(value)) | |
} | |
public init?<T>(exactly source: T) where T : BinaryInteger { | |
guard let source = Storage.IntegerLiteralType(exactly: source) else { | |
return nil | |
} | |
let (value, overflow) = source.multipliedReportingOverflow(by: 100) | |
if !overflow { | |
self.init(unscaled: Storage(value)) | |
} | |
else if source.trailingZeroBitCount >= 7 /* 1<<7 == 128 */ { | |
self.init(unscaled: Storage(value)) | |
} | |
else { | |
return nil | |
} | |
} | |
public var magnitude: BouyantPointNumber { | |
return BouyantPointNumber(storage: storage.magnitude) | |
} | |
public static func * (lhs: BouyantPointNumber, rhs: BouyantPointNumber) -> BouyantPointNumber { | |
return BouyantPointNumber(storage: lhs.storage * rhs.storage / scaleFactor) | |
} | |
public static func *= (lhs: inout BouyantPointNumber, rhs: BouyantPointNumber) { | |
lhs.storage *= rhs.storage / scaleFactor | |
} | |
public static func + (lhs: BouyantPointNumber, rhs: BouyantPointNumber) -> BouyantPointNumber { | |
return BouyantPointNumber(storage: lhs.storage + rhs.storage) | |
} | |
public static func += (lhs: inout BouyantPointNumber, rhs: BouyantPointNumber) { | |
lhs.storage += rhs.storage | |
} | |
public static func - (lhs: BouyantPointNumber, rhs: BouyantPointNumber) -> BouyantPointNumber { | |
return BouyantPointNumber(storage: lhs.storage - rhs.storage) | |
} | |
public static func -= (lhs: inout BouyantPointNumber, rhs: BouyantPointNumber) { | |
lhs.storage -= rhs.storage | |
} | |
} | |
// TODO: Decimal currently uses Double as its FloatLiteralType, which is problematic (0.1 + 0.2 != 0.3). | |
// | |
// ExpressibleByFloatLiteral should maybe be ExpressibleByNumericLiteral and have a signature like: | |
// init(sign:significand:decimalExponent:) for 1.234 | |
// init(sign:significand:binaryExponent:) for 0x1.3p0 | |
// | |
// It should use `init(sign:significand:binaryExponent:)` whenever it's precise. | |
// | |
// The significand and exponent should be anything which is ExpressibleByIntegerLiteral. | |
// ExpressibleByIntegerLiteral is equivalent to ExpressibleByNumericLiteral where Exponent is Unsigned. | |
// | |
// This may require these initializers to be failable. | |
// It also will require optimisations to make it as efficient as it was previously. | |
// | |
// There should be errors if the sign is negative, but it's not a SignedNumeric. | |
// There should be warnings if the exponent is negative, but it's an integer. | |
extension BouyantPointNumber: ExpressibleByFloatLiteral { | |
public typealias FloatLiteralType = Decimal.FloatLiteralType | |
public init(floatLiteral value: FloatLiteralType) { | |
self.init(unscaled: Storage(truncating: Decimal(floatLiteral: value) as NSDecimalNumber)) | |
} | |
} | |
extension BouyantPointNumber: CustomStringConvertible { | |
public var description: String { | |
return String(describing: Decimal(self.storage) / 10.0) | |
} | |
} | |
print(0.1 as BouyantPointNumber + 0.2 == 0.3) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment