Skip to content

Commit

Permalink
Rework sign and magnitude init (#50).
Browse files Browse the repository at this point in the history
  • Loading branch information
oscbyspro committed Aug 8, 2023
1 parent 7a3a799 commit da80591
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 135 deletions.
25 changes: 24 additions & 1 deletion Sources/NBKCoreKit/NBKBinaryInteger.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ where Magnitude: NBKUnsignedInteger, Words: Sendable {
Digit.Digit == Digit, Digit.Magnitude == Magnitude.Digit

//=------------------------------------------------------------------------=
// MARK: Initializers
// MARK: Details x Numbers
//=------------------------------------------------------------------------=

/// Creates a new instance from the given digit.
Expand All @@ -48,6 +48,29 @@ where Magnitude: NBKUnsignedInteger, Words: Sendable {
///
@inlinable init(digit: Digit)

/// Tries to create a value equal to the given `sign` and `magnitude` pair.
///
/// If the `sign` and `magnitude` pair is not representable, the result is nil.
///
/// ```
/// ┌───────┬───────────────────── → ───────────┐
/// │ sign │ magnitude │ self │
/// │───────┤───────────────────── → ───────────┤
/// │ plus │ UInt256( 1) │ Int256( 1) │
/// │ minus │ UInt256( 1) │ Int256(-1) │
/// │───────┤───────────────────── → ───────────┤
/// │ plus │ Int256.max.magnitude │ Int256.max │
/// │ minus │ Int256.min.magnitude │ Int256.min │
/// │───────┤───────────────────── → ───────────┤
/// │ plus │ UInt256.max │ nil │
/// │ minus │ UInt256.max │ nil │
/// └───────┴───────────────────── → ───────────┘
/// ```
///
/// - Note: The `sign` and `magnitude` pair (minus, zero) is represented by zero.
///
@inlinable init?(sign: FloatingPointSign, magnitude: Magnitude)

//=------------------------------------------------------------------------=
// MARK: Details x Bits
//=------------------------------------------------------------------------=
Expand Down
52 changes: 3 additions & 49 deletions Sources/NBKCoreKit/NBKFixedWidthInteger.swift
Original file line number Diff line number Diff line change
Expand Up @@ -831,60 +831,14 @@ extension NBKFixedWidthInteger {
// MARK: Initializers
//=------------------------------------------------------------------------=

/// Tries to create a representable value equal to the given `sign` and `magnitude`.
///
/// If the `sign` and `magnitude` are is not representable, the result is nil.
///
/// ```
/// ┌───────┬───────────────────── → ───────────┐
/// │ sign │ magnitude │ self │
/// │───────┤───────────────────── → ───────────┤
/// │ plus │ UInt256( 1) │ Int256( 1) │
/// │ minus │ UInt256( 1) │ Int256(-1) │
/// │───────┤───────────────────── → ───────────┤
/// │ plus │ Int256.max.magnitude │ Int256.max │
/// │ minus │ Int256.min.magnitude │ Int256.min │
/// │───────┤───────────────────── → ───────────┤
/// │ plus │ UInt256.max │ nil │
/// │ minus │ UInt256.max │ nil │
/// └───────┴───────────────────── → ───────────┘
/// ```
///
/// - Note: The `sign` and `magnitude` pair (minus, zero) is represented as zero.
///
@inlinable public static func exactly(sign: FloatingPointSign, magnitude: Magnitude) -> Self? {
@inlinable public init?(sign: FloatingPointSign, magnitude: Magnitude) {
var bitPattern = magnitude as Magnitude
var isLessThanZero = (sign == FloatingPointSign.minus)
if isLessThanZero {
isLessThanZero = !bitPattern.formTwosComplementSubsequence(true)
}

let value = Self(bitPattern: bitPattern)
return value.isLessThanZero == isLessThanZero ? value : nil
}

/// Creates the representable value closest to the given `sign` and `magnitude`.
///
/// If the `sign` and `magnitude` are greater than the maximum representable value,
/// the result is this type’s largest value. If the `sign` and `magnitude` are less
/// than the smallest representable value, the result is this type’s smallest value.
///
/// ```
/// ┌───────┬───────────────────── → ───────────┐
/// │ sign │ magnitude │ self │
/// │───────┤───────────────────── → ───────────┤
/// │ plus │ UInt256( 1) │ Int256( 1) │
/// │ minus │ UInt256( 1) │ Int256(-1) │
/// │───────┤───────────────────── → ───────────┤
/// │ plus │ Int256.max.magnitude │ Int256.max │
/// │ minus │ Int256.min.magnitude │ Int256.min │
/// │───────┤───────────────────── → ───────────┤
/// │ plus │ UInt256.max │ Int256.max │
/// │ minus │ UInt256.max │ Int256.min │
/// └───────┴───────────────────── → ───────────┘
/// ```
///
@inlinable public static func clamping(sign: FloatingPointSign, magnitude: Magnitude) -> Self {
self.exactly(sign: sign, magnitude: magnitude) ?? (sign == FloatingPointSign.minus ? Self.min : Self.max)
self.init(bitPattern: bitPattern)
guard self.isLessThanZero == isLessThanZero else { return nil }
}
}
2 changes: 1 addition & 1 deletion Sources/NBKDoubleWidthKit/NBKDoubleWidth+Text.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ extension NBKDoubleWidth {
let components = NBK.makeIntegerComponents(utf8: utf8)
let digits = NBK.UnsafeUTF8(rebasing: components.body)
guard let magnitude = Magnitude(digits: digits, radix: radix) else { return nil }
return Self.exactly(sign: components.sign, magnitude: magnitude)
return Self(sign: components.sign, magnitude: magnitude)
}

if let value { self = value } else { return nil }
Expand Down
44 changes: 12 additions & 32 deletions Tests/NBKCoreKitTests/NBKCoreInteger+Numbers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,45 +50,25 @@ final class NBKCoreIntegerTestsOnNumbers: XCTestCase {
func whereIsSigned<T>(_ type: T.Type) where T: NBKCoreInteger {
typealias M = T.Magnitude

XCTAssertEqual(T.exactly (sign: .plus, magnitude: M( 1)), T( 1))
XCTAssertEqual(T.clamping(sign: .plus, magnitude: M( 1)), T( 1))

XCTAssertEqual(T.exactly (sign: .minus, magnitude: M( 1)), T(-1))
XCTAssertEqual(T.clamping(sign: .minus, magnitude: M( 1)), T(-1))

XCTAssertEqual(T.exactly (sign: .plus, magnitude: M.max), nil)
XCTAssertEqual(T.clamping(sign: .plus, magnitude: M.max), T.max)

XCTAssertEqual(T.exactly (sign: .minus, magnitude: M.max), nil)
XCTAssertEqual(T.clamping(sign: .minus, magnitude: M.max), T.min)

XCTAssertEqual(T.exactly (sign: .plus, magnitude: T.max.magnitude), T.max)
XCTAssertEqual(T.clamping(sign: .plus, magnitude: T.max.magnitude), T.max)

XCTAssertEqual(T.exactly (sign: .minus, magnitude: T.min.magnitude), T.min)
XCTAssertEqual(T.clamping(sign: .minus, magnitude: T.min.magnitude), T.min)
XCTAssertEqual(T(sign: .plus, magnitude: M( 1)), T( 1))
XCTAssertEqual(T(sign: .minus, magnitude: M( 1)), T(-1))
XCTAssertEqual(T(sign: .plus, magnitude: M.max), nil)
XCTAssertEqual(T(sign: .minus, magnitude: M.max), nil)
XCTAssertEqual(T(sign: .plus, magnitude: T.max.magnitude), T.max)
XCTAssertEqual(T(sign: .minus, magnitude: T.min.magnitude), T.min)
}

func whereIsUnsigned<T>(_ type: T.Type) where T: NBKCoreInteger {
typealias M = T.Magnitude

XCTAssertEqual(T.exactly (sign: .plus, magnitude: M( 1)), T( 1))
XCTAssertEqual(T.clamping(sign: .plus, magnitude: M( 1)), T( 1))

XCTAssertEqual(T.exactly (sign: .minus, magnitude: M( 1)), nil)
XCTAssertEqual(T.clamping(sign: .minus, magnitude: M( 1)), T.min)

XCTAssertEqual(T.exactly (sign: .plus, magnitude: M.max), T.max)
XCTAssertEqual(T.clamping(sign: .plus, magnitude: M.max), T.max)

XCTAssertEqual(T.exactly (sign: .minus, magnitude: M.max), nil)
XCTAssertEqual(T.clamping(sign: .minus, magnitude: M.max), T.min)
XCTAssertEqual(T(sign: .plus, magnitude: M( 1)), T( 1))

XCTAssertEqual(T.exactly (sign: .plus, magnitude: T.max.magnitude), T.max)
XCTAssertEqual(T.clamping(sign: .plus, magnitude: T.max.magnitude), T.max)
XCTAssertEqual(T(sign: .minus, magnitude: M( 1)), nil)
XCTAssertEqual(T(sign: .plus, magnitude: M.max), T.max)
XCTAssertEqual(T(sign: .minus, magnitude: M.max), nil)

XCTAssertEqual(T.exactly (sign: .minus, magnitude: T.min.magnitude), T.min)
XCTAssertEqual(T.clamping(sign: .minus, magnitude: T.min.magnitude), T.min)
XCTAssertEqual(T(sign: .plus, magnitude: T.max.magnitude), T.max)
XCTAssertEqual(T(sign: .minus, magnitude: T.min.magnitude), T.min)
}

for type: T in types {
Expand Down
12 changes: 4 additions & 8 deletions Tests/NBKDoubleWidthKitBenchmarks/NBKDoubleWidth+Numbers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -437,12 +437,10 @@ final class NBKDoubleWidthBenchmarksOnNumbersAsInt256: XCTestCase {
var xyz = NBK.blackHoleIdentity((sign: FloatingPointSign.minus, magnitude: M(x64: X(0, 1, 2, 3))))

for _ in 0 ..< 1_000_000 {
NBK.blackHole(T.exactly (sign: abc.sign, magnitude: abc.magnitude))
NBK.blackHole(T.clamping(sign: abc.sign, magnitude: abc.magnitude))
NBK.blackHole(T(sign: abc.sign, magnitude: abc.magnitude))
NBK.blackHoleInoutIdentity(&abc)

NBK.blackHole(T.exactly (sign: xyz.sign, magnitude: xyz.magnitude))
NBK.blackHole(T.clamping(sign: xyz.sign, magnitude: xyz.magnitude))
NBK.blackHole(T(sign: xyz.sign, magnitude: xyz.magnitude))
NBK.blackHoleInoutIdentity(&xyz)
}
}
Expand Down Expand Up @@ -869,12 +867,10 @@ final class NBKDoubleWidthBenchmarksOnNumbersAsUInt256: XCTestCase {
var xyz = NBK.blackHoleIdentity((sign: FloatingPointSign.minus, magnitude: M(x64: X(0, 1, 2, 3))))

for _ in 0 ..< 1_000_000 {
NBK.blackHole(T.exactly (sign: abc.sign, magnitude: abc.magnitude))
NBK.blackHole(T.clamping(sign: abc.sign, magnitude: abc.magnitude))
NBK.blackHole(T(sign: abc.sign, magnitude: abc.magnitude))
NBK.blackHoleInoutIdentity(&abc)

NBK.blackHole(T.exactly (sign: xyz.sign, magnitude: xyz.magnitude))
NBK.blackHole(T.clamping(sign: xyz.sign, magnitude: xyz.magnitude))
NBK.blackHole(T(sign: xyz.sign, magnitude: xyz.magnitude))
NBK.blackHoleInoutIdentity(&xyz)
}
}
Expand Down
60 changes: 16 additions & 44 deletions Tests/NBKDoubleWidthKitTests/NBKDoubleWidth+Numbers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -312,31 +312,17 @@ final class NBKDoubleWidthTestsOnNumbersAsInt256: XCTestCase {
//=------------------------------------------------------------------------=

func testsFromSignAndMagnitude() {
XCTAssertEqual(T.exactly (sign: .plus, magnitude: M( 1)), T( 1))
XCTAssertEqual(T.clamping(sign: .plus, magnitude: M( 1)), T( 1))

XCTAssertEqual(T.exactly (sign: .minus, magnitude: M( 1)), T(-1))
XCTAssertEqual(T.clamping(sign: .minus, magnitude: M( 1)), T(-1))

XCTAssertEqual(T.exactly (sign: .plus, magnitude: M.max), nil)
XCTAssertEqual(T.clamping(sign: .plus, magnitude: M.max), T.max)

XCTAssertEqual(T.exactly (sign: .minus, magnitude: M.max), nil)
XCTAssertEqual(T.clamping(sign: .minus, magnitude: M.max), T.min)

XCTAssertEqual(T.exactly (sign: .plus, magnitude: T.max.magnitude), T.max)
XCTAssertEqual(T.clamping(sign: .plus, magnitude: T.max.magnitude), T.max)

XCTAssertEqual(T.exactly (sign: .minus, magnitude: T.min.magnitude), T.min)
XCTAssertEqual(T.clamping(sign: .minus, magnitude: T.min.magnitude), T.min)
XCTAssertEqual(T(sign: .plus, magnitude: M( 1)), T( 1))
XCTAssertEqual(T(sign: .minus, magnitude: M( 1)), T(-1))
XCTAssertEqual(T(sign: .plus, magnitude: M.max), nil)
XCTAssertEqual(T(sign: .minus, magnitude: M.max), nil)
XCTAssertEqual(T(sign: .plus, magnitude: T.max.magnitude), T.max)
XCTAssertEqual(T(sign: .minus, magnitude: T.min.magnitude), T.min)
}

func testsFromSignAndMagnitudeAsPlusMinusZero() {
XCTAssertEqual(T.exactly (sign: .plus, magnitude: M( )), T( ))
XCTAssertEqual(T.clamping(sign: .plus, magnitude: M( )), T( ))

XCTAssertEqual(T.exactly (sign: .minus, magnitude: M( )), T( ))
XCTAssertEqual(T.clamping(sign: .minus, magnitude: M( )), T( ))
XCTAssertEqual(T(sign: .plus, magnitude: M( )), T( ))
XCTAssertEqual(T(sign: .minus, magnitude: M( )), T( ))
}

//=------------------------------------------------------------------------=
Expand Down Expand Up @@ -641,31 +627,17 @@ final class NBKDoubleWidthTestsOnNumbersAsUInt256: XCTestCase {
//=------------------------------------------------------------------------=

func testsFromSignAndMagnitude() {
XCTAssertEqual(T.exactly (sign: .plus, magnitude: M( 1)), T( 1))
XCTAssertEqual(T.clamping(sign: .plus, magnitude: M( 1)), T( 1))

XCTAssertEqual(T.exactly (sign: .minus, magnitude: M( 1)), nil)
XCTAssertEqual(T.clamping(sign: .minus, magnitude: M( 1)), T.min)

XCTAssertEqual(T.exactly (sign: .plus, magnitude: M.max), T.max)
XCTAssertEqual(T.clamping(sign: .plus, magnitude: M.max), T.max)

XCTAssertEqual(T.exactly (sign: .minus, magnitude: M.max), nil)
XCTAssertEqual(T.clamping(sign: .minus, magnitude: M.max), T.min)

XCTAssertEqual(T.exactly (sign: .plus, magnitude: T.max.magnitude), T.max)
XCTAssertEqual(T.clamping(sign: .plus, magnitude: T.max.magnitude), T.max)

XCTAssertEqual(T.exactly (sign: .minus, magnitude: T.min.magnitude), T.min)
XCTAssertEqual(T.clamping(sign: .minus, magnitude: T.min.magnitude), T.min)
XCTAssertEqual(T(sign: .plus, magnitude: M( 1)), T( 1))
XCTAssertEqual(T(sign: .minus, magnitude: M( 1)), nil)
XCTAssertEqual(T(sign: .plus, magnitude: M.max), T.max)
XCTAssertEqual(T(sign: .minus, magnitude: M.max), nil)
XCTAssertEqual(T(sign: .plus, magnitude: T.max.magnitude), T.max)
XCTAssertEqual(T(sign: .minus, magnitude: T.min.magnitude), T.min)
}

func testsFromSignAndMagnitudeAsPlusMinusZero() {
XCTAssertEqual(T.exactly (sign: .plus, magnitude: M( )), T( ))
XCTAssertEqual(T.clamping(sign: .plus, magnitude: M( )), T( ))

XCTAssertEqual(T.exactly (sign: .minus, magnitude: M( )), T( ))
XCTAssertEqual(T.clamping(sign: .minus, magnitude: M( )), T( ))
XCTAssertEqual(T(sign: .plus, magnitude: M( )), T( ))
XCTAssertEqual(T(sign: .minus, magnitude: M( )), T( ))
}

//=------------------------------------------------------------------------=
Expand Down

0 comments on commit da80591

Please sign in to comment.