From 405aaecfa482a5abb9074105f7a2f9d3b984e05e Mon Sep 17 00:00:00 2001 From: Xiaodi Wu <13952+xwu@users.noreply.github.com> Date: Sun, 29 Aug 2021 09:52:37 -0400 Subject: [PATCH 1/4] Use 'Decimal.ulp' to compute 'nextUp' and 'nextDown'. --- Darwin/Foundation-swiftoverlay/Decimal.swift | 8 ++------ Sources/Foundation/Decimal.swift | 8 ++------ 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/Darwin/Foundation-swiftoverlay/Decimal.swift b/Darwin/Foundation-swiftoverlay/Decimal.swift index da07d8e31f..74cef952c7 100644 --- a/Darwin/Foundation-swiftoverlay/Decimal.swift +++ b/Darwin/Foundation-swiftoverlay/Decimal.swift @@ -557,15 +557,11 @@ extension Decimal { } public var nextUp: Decimal { - return self + Decimal( - _exponent: _exponent, _length: 1, _isNegative: 0, _isCompact: 1, - _reserved: 0, _mantissa: (0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000)) + return self + ulp } public var nextDown: Decimal { - return self - Decimal( - _exponent: _exponent, _length: 1, _isNegative: 0, _isCompact: 1, - _reserved: 0, _mantissa: (0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000)) + return -(-self).nextUp } /// The IEEE 754 "class" of this type. diff --git a/Sources/Foundation/Decimal.swift b/Sources/Foundation/Decimal.swift index cd775362b4..571ff694f9 100644 --- a/Sources/Foundation/Decimal.swift +++ b/Sources/Foundation/Decimal.swift @@ -725,15 +725,11 @@ extension Decimal { } public var nextUp: Decimal { - return self + Decimal( - _exponent: _exponent, _length: 1, _isNegative: 0, _isCompact: 1, - _reserved: 0, _mantissa: (0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000)) + return self + ulp } public var nextDown: Decimal { - return self - Decimal( - _exponent: _exponent, _length: 1, _isNegative: 0, _isCompact: 1, - _reserved: 0, _mantissa: (0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000)) + return -(-self).nextUp } /// The IEEE 754 "class" of this type. From e8c418c411900395aad9a35be33b2ab24bbdbce9 Mon Sep 17 00:00:00 2001 From: Xiaodi Wu <13952+xwu@users.noreply.github.com> Date: Sun, 29 Aug 2021 17:38:13 -0400 Subject: [PATCH 2/4] [SR-14974] Fix 'Decimal.ulp' to reflect spacing between consecutive values. --- .../TestDecimal.swift | 52 +++++--- Darwin/Foundation-swiftoverlay/Decimal.swift | 66 ++++++++- Sources/Foundation/Decimal.swift | 125 ++++++++++-------- Tests/Foundation/Tests/TestDecimal.swift | 52 +++++--- 4 files changed, 199 insertions(+), 96 deletions(-) diff --git a/Darwin/Foundation-swiftoverlay-Tests/TestDecimal.swift b/Darwin/Foundation-swiftoverlay-Tests/TestDecimal.swift index 78a4882b71..8e10580274 100644 --- a/Darwin/Foundation-swiftoverlay-Tests/TestDecimal.swift +++ b/Darwin/Foundation-swiftoverlay-Tests/TestDecimal.swift @@ -208,21 +208,6 @@ class TestDecimal : XCTestCase { XCTAssertEqual(11, sm5) XCTAssertEqual(12, sm6) XCTAssertEqual(13, sm7) - - let ulp = explicit.ulp - XCTAssertEqual(0x7f, ulp.exponent) - XCTAssertEqual(1, ulp._length) - XCTAssertEqual(0, ulp._isNegative) - XCTAssertEqual(1, ulp._isCompact) - XCTAssertEqual(0, ulp._reserved) - XCTAssertEqual(1, ulp._mantissa.0) - XCTAssertEqual(0, ulp._mantissa.1) - XCTAssertEqual(0, ulp._mantissa.2) - XCTAssertEqual(0, ulp._mantissa.3) - XCTAssertEqual(0, ulp._mantissa.4) - XCTAssertEqual(0, ulp._mantissa.5) - XCTAssertEqual(0, ulp._mantissa.6) - XCTAssertEqual(0, ulp._mantissa.7) } func test_Maths() { @@ -287,8 +272,6 @@ class TestDecimal : XCTestCase { XCTAssertTrue(Decimal(2) < Decimal(3)) XCTAssertTrue(Decimal(3) > Decimal(2)) XCTAssertEqual(Decimal(-9), Decimal(1) - Decimal(10)) - XCTAssertEqual(Decimal(3), Decimal(2).nextUp) - XCTAssertEqual(Decimal(2), Decimal(3).nextDown) XCTAssertEqual(Decimal(1.234), abs(Decimal(1.234))) XCTAssertEqual(Decimal(1.234), abs(Decimal(-1.234))) if #available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) { @@ -608,8 +591,41 @@ class TestDecimal : XCTestCase { } func test_ULP() { - let x = 0.1 as Decimal + var x = 0.1 as Decimal XCTAssertFalse(x.ulp > x) + + x = .nan + XCTAssertTrue(x.ulp.isNaN) + XCTAssertTrue(x.nextDown.isNaN) + XCTAssertTrue(x.nextUp.isNaN) + + x = .greatestFiniteMagnitude + XCTAssertEqual(x.ulp, Decimal(string: "1e127")!) + XCTAssertEqual(x.nextDown, x - Decimal(string: "1e127")!) + XCTAssertTrue(x.nextUp.isNaN) + + x = 1 + XCTAssertEqual(x.ulp, Decimal(string: "1e-38")!) + XCTAssertEqual(x.nextDown, x - Decimal(string: "1e-38")!) + XCTAssertEqual(x.nextUp, x + Decimal(string: "1e-38")!) + + x = .leastNonzeroMagnitude + // XCTAssertEqual(x.ulp, x) // SR-6671 + // XCTAssertEqual(x.nextDown, 0) // SR-6671 + // XCTAssertEqual(x.nextUp, x + x) // SR-6671 + XCTAssertNotEqual(x.ulp, 0) + XCTAssertNotEqual(x.nextDown, x) + XCTAssertNotEqual(x.nextUp, x) + + x = 0 + XCTAssertEqual(x.ulp, Decimal(string: "1e-128")!) + XCTAssertEqual(x.nextDown, -Decimal(string: "1e-128")!) + XCTAssertEqual(x.nextUp, Decimal(string: "1e-128")!) + + x = -1 + XCTAssertEqual(x.ulp, (1 as Decimal).ulp) + XCTAssertEqual(x.nextDown, -((1 as Decimal).nextUp)) + XCTAssertEqual(x.nextUp, -((1 as Decimal).nextDown)) } func test_unconditionallyBridgeFromObjectiveC() { diff --git a/Darwin/Foundation-swiftoverlay/Decimal.swift b/Darwin/Foundation-swiftoverlay/Decimal.swift index 74cef952c7..9064f66cc0 100644 --- a/Darwin/Foundation-swiftoverlay/Decimal.swift +++ b/Darwin/Foundation-swiftoverlay/Decimal.swift @@ -324,6 +324,57 @@ extension Decimal : Strideable { } } +extension Decimal { + // (Used by `_powersOfTen` and `ulp`; note that the representation isn't compact.) + fileprivate init(_length: UInt32, _mantissa: (UInt16, UInt16, UInt16, UInt16, UInt16, UInt16, UInt16, UInt16)) { + self.init(_exponent: 0, _length: _length, _isNegative: 0, _isCompact: 0, + _reserved: 0, _mantissa: _mantissa) + } +} + +private let _powersOfTen = [ +/*^00*/ 1 as Decimal, +/*^01*/ Decimal(_length: 1, _mantissa: (0x000a,0,0,0,0,0,0,0)), +/*^02*/ Decimal(_length: 1, _mantissa: (0x0064,0,0,0,0,0,0,0)), +/*^03*/ Decimal(_length: 1, _mantissa: (0x03e8,0,0,0,0,0,0,0)), +/*^04*/ Decimal(_length: 1, _mantissa: (0x2710,0,0,0,0,0,0,0)), +/*^05*/ Decimal(_length: 2, _mantissa: (0x86a0, 0x0001,0,0,0,0,0,0)), +/*^06*/ Decimal(_length: 2, _mantissa: (0x4240, 0x000f,0,0,0,0,0,0)), +/*^07*/ Decimal(_length: 2, _mantissa: (0x9680, 0x0098,0,0,0,0,0,0)), +/*^08*/ Decimal(_length: 2, _mantissa: (0xe100, 0x05f5,0,0,0,0,0,0)), +/*^09*/ Decimal(_length: 2, _mantissa: (0xca00, 0x3b9a,0,0,0,0,0,0)), +/*^10*/ Decimal(_length: 3, _mantissa: (0xe400, 0x540b, 0x0002,0,0,0,0,0)), +/*^11*/ Decimal(_length: 3, _mantissa: (0xe800, 0x4876, 0x0017,0,0,0,0,0)), +/*^12*/ Decimal(_length: 3, _mantissa: (0x1000, 0xd4a5, 0x00e8,0,0,0,0,0)), +/*^13*/ Decimal(_length: 3, _mantissa: (0xa000, 0x4e72, 0x0918,0,0,0,0,0)), +/*^14*/ Decimal(_length: 3, _mantissa: (0x4000, 0x107a, 0x5af3,0,0,0,0,0)), +/*^15*/ Decimal(_length: 4, _mantissa: (0x8000, 0xa4c6, 0x8d7e, 0x0003,0,0,0,0)), +/*^16*/ Decimal(_length: 4, _mantissa: (0x0000, 0x6fc1, 0x86f2, 0x0023,0,0,0,0)), +/*^17*/ Decimal(_length: 4, _mantissa: (0x0000, 0x5d8a, 0x4578, 0x0163,0,0,0,0)), +/*^18*/ Decimal(_length: 4, _mantissa: (0x0000, 0xa764, 0xb6b3, 0x0de0,0,0,0,0)), +/*^19*/ Decimal(_length: 4, _mantissa: (0x0000, 0x89e8, 0x2304, 0x8ac7,0,0,0,0)), +/*^20*/ Decimal(_length: 5, _mantissa: (0x0000, 0x6310, 0x5e2d, 0x6bc7, 0x0005,0,0,0)), +/*^21*/ Decimal(_length: 5, _mantissa: (0x0000, 0xdea0, 0xadc5, 0x35c9, 0x0036,0,0,0)), +/*^22*/ Decimal(_length: 5, _mantissa: (0x0000, 0xb240, 0xc9ba, 0x19e0, 0x021e,0,0,0)), +/*^23*/ Decimal(_length: 5, _mantissa: (0x0000, 0xf680, 0xe14a, 0x02c7, 0x152d,0,0,0)), +/*^24*/ Decimal(_length: 5, _mantissa: (0x0000, 0xa100, 0xcced, 0x1bce, 0xd3c2,0,0,0)), +/*^25*/ Decimal(_length: 6, _mantissa: (0x0000, 0x4a00, 0x0148, 0x1614, 0x4595, 0x0008,0,0)), +/*^26*/ Decimal(_length: 6, _mantissa: (0x0000, 0xe400, 0x0cd2, 0xdcc8, 0xb7d2, 0x0052,0,0)), +/*^27*/ Decimal(_length: 6, _mantissa: (0x0000, 0xe800, 0x803c, 0x9fd0, 0x2e3c, 0x033b,0,0)), +/*^28*/ Decimal(_length: 6, _mantissa: (0x0000, 0x1000, 0x0261, 0x3e25, 0xce5e, 0x204f,0,0)), +/*^29*/ Decimal(_length: 7, _mantissa: (0x0000, 0xa000, 0x17ca, 0x6d72, 0x0fae, 0x431e, 0x0001,0)), +/*^30*/ Decimal(_length: 7, _mantissa: (0x0000, 0x4000, 0xedea, 0x4674, 0x9cd0, 0x9f2c, 0x000c,0)), +/*^31*/ Decimal(_length: 7, _mantissa: (0x0000, 0x8000, 0x4b26, 0xc091, 0x2022, 0x37be, 0x007e,0)), +/*^32*/ Decimal(_length: 7, _mantissa: (0x0000, 0x0000, 0xef81, 0x85ac, 0x415b, 0x2d6d, 0x04ee,0)), +/*^33*/ Decimal(_length: 7, _mantissa: (0x0000, 0x0000, 0x5b0a, 0x38c1, 0x8d93, 0xc644, 0x314d,0)), +/*^34*/ Decimal(_length: 8, _mantissa: (0x0000, 0x0000, 0x8e64, 0x378d, 0x87c0, 0xbead, 0xed09, 0x0001)), +/*^35*/ Decimal(_length: 8, _mantissa: (0x0000, 0x0000, 0x8fe8, 0x2b87, 0x4d82, 0x72c7, 0x4261, 0x0013)), +/*^36*/ Decimal(_length: 8, _mantissa: (0x0000, 0x0000, 0x9f10, 0xb34b, 0x0715, 0x7bc9, 0x97ce, 0x00c0)), +/*^37*/ Decimal(_length: 8, _mantissa: (0x0000, 0x0000, 0x36a0, 0x00f4, 0x46d9, 0xd5da, 0xee10, 0x0785)), +/*^38*/ Decimal(_length: 8, _mantissa: (0x0000, 0x0000, 0x2240, 0x098a, 0xc47a, 0x5a86, 0x4ca8, 0x4b3b)) +/*^39 is on 9 shorts.*/ +] + // The methods in this extension exist to match the protocol requirements of // FloatingPoint, even if we can't conform directly. // @@ -550,9 +601,20 @@ extension Decimal { } public var ulp: Decimal { - if !self.isFinite { return Decimal.nan } + guard isFinite else { return .nan } + + let exponent: Int32 + if isZero { + exponent = .min + } else { + let significand = Decimal(_length: _length, _mantissa: _mantissa) + let maxPowerOfTen = _powersOfTen.count + let powerOfTen = _powersOfTen.firstIndex { $0 > significand } ?? maxPowerOfTen + exponent = _exponent &- Int32(maxPowerOfTen &- powerOfTen) + } + return Decimal( - _exponent: _exponent, _length: 1, _isNegative: 0, _isCompact: 1, + _exponent: max(exponent, -128), _length: 1, _isNegative: 0, _isCompact: 1, _reserved: 0, _mantissa: (0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000)) } diff --git a/Sources/Foundation/Decimal.swift b/Sources/Foundation/Decimal.swift index 571ff694f9..b2a3eac49b 100644 --- a/Sources/Foundation/Decimal.swift +++ b/Sources/Foundation/Decimal.swift @@ -492,6 +492,57 @@ extension Decimal : Strideable { } } +extension Decimal { + // (Used by `_powersOfTen` and `ulp`; note that the representation isn't compact.) + fileprivate init(_length: UInt32, _mantissa: (UInt16, UInt16, UInt16, UInt16, UInt16, UInt16, UInt16, UInt16)) { + self.init(_exponent: 0, _length: _length, _isNegative: 0, _isCompact: 0, + _reserved: 0, _mantissa: _mantissa) + } +} + +private let _powersOfTen = [ +/*^00*/ 1 as Decimal, +/*^01*/ Decimal(_length: 1, _mantissa: (0x000a,0,0,0,0,0,0,0)), +/*^02*/ Decimal(_length: 1, _mantissa: (0x0064,0,0,0,0,0,0,0)), +/*^03*/ Decimal(_length: 1, _mantissa: (0x03e8,0,0,0,0,0,0,0)), +/*^04*/ Decimal(_length: 1, _mantissa: (0x2710,0,0,0,0,0,0,0)), +/*^05*/ Decimal(_length: 2, _mantissa: (0x86a0, 0x0001,0,0,0,0,0,0)), +/*^06*/ Decimal(_length: 2, _mantissa: (0x4240, 0x000f,0,0,0,0,0,0)), +/*^07*/ Decimal(_length: 2, _mantissa: (0x9680, 0x0098,0,0,0,0,0,0)), +/*^08*/ Decimal(_length: 2, _mantissa: (0xe100, 0x05f5,0,0,0,0,0,0)), +/*^09*/ Decimal(_length: 2, _mantissa: (0xca00, 0x3b9a,0,0,0,0,0,0)), +/*^10*/ Decimal(_length: 3, _mantissa: (0xe400, 0x540b, 0x0002,0,0,0,0,0)), +/*^11*/ Decimal(_length: 3, _mantissa: (0xe800, 0x4876, 0x0017,0,0,0,0,0)), +/*^12*/ Decimal(_length: 3, _mantissa: (0x1000, 0xd4a5, 0x00e8,0,0,0,0,0)), +/*^13*/ Decimal(_length: 3, _mantissa: (0xa000, 0x4e72, 0x0918,0,0,0,0,0)), +/*^14*/ Decimal(_length: 3, _mantissa: (0x4000, 0x107a, 0x5af3,0,0,0,0,0)), +/*^15*/ Decimal(_length: 4, _mantissa: (0x8000, 0xa4c6, 0x8d7e, 0x0003,0,0,0,0)), +/*^16*/ Decimal(_length: 4, _mantissa: (0x0000, 0x6fc1, 0x86f2, 0x0023,0,0,0,0)), +/*^17*/ Decimal(_length: 4, _mantissa: (0x0000, 0x5d8a, 0x4578, 0x0163,0,0,0,0)), +/*^18*/ Decimal(_length: 4, _mantissa: (0x0000, 0xa764, 0xb6b3, 0x0de0,0,0,0,0)), +/*^19*/ Decimal(_length: 4, _mantissa: (0x0000, 0x89e8, 0x2304, 0x8ac7,0,0,0,0)), +/*^20*/ Decimal(_length: 5, _mantissa: (0x0000, 0x6310, 0x5e2d, 0x6bc7, 0x0005,0,0,0)), +/*^21*/ Decimal(_length: 5, _mantissa: (0x0000, 0xdea0, 0xadc5, 0x35c9, 0x0036,0,0,0)), +/*^22*/ Decimal(_length: 5, _mantissa: (0x0000, 0xb240, 0xc9ba, 0x19e0, 0x021e,0,0,0)), +/*^23*/ Decimal(_length: 5, _mantissa: (0x0000, 0xf680, 0xe14a, 0x02c7, 0x152d,0,0,0)), +/*^24*/ Decimal(_length: 5, _mantissa: (0x0000, 0xa100, 0xcced, 0x1bce, 0xd3c2,0,0,0)), +/*^25*/ Decimal(_length: 6, _mantissa: (0x0000, 0x4a00, 0x0148, 0x1614, 0x4595, 0x0008,0,0)), +/*^26*/ Decimal(_length: 6, _mantissa: (0x0000, 0xe400, 0x0cd2, 0xdcc8, 0xb7d2, 0x0052,0,0)), +/*^27*/ Decimal(_length: 6, _mantissa: (0x0000, 0xe800, 0x803c, 0x9fd0, 0x2e3c, 0x033b,0,0)), +/*^28*/ Decimal(_length: 6, _mantissa: (0x0000, 0x1000, 0x0261, 0x3e25, 0xce5e, 0x204f,0,0)), +/*^29*/ Decimal(_length: 7, _mantissa: (0x0000, 0xa000, 0x17ca, 0x6d72, 0x0fae, 0x431e, 0x0001,0)), +/*^30*/ Decimal(_length: 7, _mantissa: (0x0000, 0x4000, 0xedea, 0x4674, 0x9cd0, 0x9f2c, 0x000c,0)), +/*^31*/ Decimal(_length: 7, _mantissa: (0x0000, 0x8000, 0x4b26, 0xc091, 0x2022, 0x37be, 0x007e,0)), +/*^32*/ Decimal(_length: 7, _mantissa: (0x0000, 0x0000, 0xef81, 0x85ac, 0x415b, 0x2d6d, 0x04ee,0)), +/*^33*/ Decimal(_length: 7, _mantissa: (0x0000, 0x0000, 0x5b0a, 0x38c1, 0x8d93, 0xc644, 0x314d,0)), +/*^34*/ Decimal(_length: 8, _mantissa: (0x0000, 0x0000, 0x8e64, 0x378d, 0x87c0, 0xbead, 0xed09, 0x0001)), +/*^35*/ Decimal(_length: 8, _mantissa: (0x0000, 0x0000, 0x8fe8, 0x2b87, 0x4d82, 0x72c7, 0x4261, 0x0013)), +/*^36*/ Decimal(_length: 8, _mantissa: (0x0000, 0x0000, 0x9f10, 0xb34b, 0x0715, 0x7bc9, 0x97ce, 0x00c0)), +/*^37*/ Decimal(_length: 8, _mantissa: (0x0000, 0x0000, 0x36a0, 0x00f4, 0x46d9, 0xd5da, 0xee10, 0x0785)), +/*^38*/ Decimal(_length: 8, _mantissa: (0x0000, 0x0000, 0x2240, 0x098a, 0xc47a, 0x5a86, 0x4ca8, 0x4b3b)) +/*^39 is on 9 shorts.*/ +] + // The methods in this extension exist to match the protocol requirements of // FloatingPoint, even if we can't conform directly. // @@ -718,9 +769,20 @@ extension Decimal { } public var ulp: Decimal { - if !self.isFinite { return Decimal.nan } + guard isFinite else { return .nan } + + let exponent: Int32 + if isZero { + exponent = .min + } else { + let significand = Decimal(_length: _length, _mantissa: _mantissa) + let maxPowerOfTen = _powersOfTen.count + let powerOfTen = _powersOfTen.firstIndex { $0 > significand } ?? maxPowerOfTen + exponent = _exponent &- Int32(maxPowerOfTen &- powerOfTen) + } + return Decimal( - _exponent: _exponent, _length: 1, _isNegative: 0, _isCompact: 1, + _exponent: max(exponent, -128), _length: 1, _isNegative: 0, _isCompact: 1, _reserved: 0, _mantissa: (0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000)) } @@ -1216,14 +1278,14 @@ fileprivate func integerMultiplyByPowerOf10(_ result: in } result = left - let maxpow10 = pow10.count - 1 + let maxpow10 = _powersOfTen.count - 1 var error:NSDecimalNumber.CalculationError = .noError while power > maxpow10 { var big = T() power -= maxpow10 - let p10 = pow10[maxpow10] + let p10 = _powersOfTen[maxpow10] if !isNegative { error = integerMultiply(&big,result,p10) @@ -1245,7 +1307,7 @@ fileprivate func integerMultiplyByPowerOf10(_ result: in var big = T() // Handle the rest of the power (<= maxpow10) - let p10 = pow10[Int(power)] + let p10 = _powersOfTen[Int(power)] if !isNegative { error = integerMultiply(&big, result, p10) @@ -1929,15 +1991,6 @@ fileprivate struct WideDecimal : VariableLengthNumber { extension Decimal { fileprivate static let maxSize: UInt32 = UInt32(NSDecimalMaxSize) - fileprivate init(length: UInt32, mantissa: (UInt16, UInt16, UInt16, UInt16, UInt16, UInt16, UInt16, UInt16)) { - precondition(length <= 15) - self._mantissa = mantissa - self.__exponent = 0 - self.__lengthAndFlags = 0 - self.__reserved = 0 - self._length = length - } - fileprivate var isCompact: Bool { get { return _isCompact != 0 @@ -2196,50 +2249,6 @@ extension Decimal { } } -fileprivate let pow10 = [ -/*^00*/ Decimal(length: 1, mantissa:( 0x0001,0,0,0,0,0,0,0)), -/*^01*/ Decimal(length: 1, mantissa:( 0x000a,0,0,0,0,0,0,0)), -/*^02*/ Decimal(length: 1, mantissa:( 0x0064,0,0,0,0,0,0,0)), -/*^03*/ Decimal(length: 1, mantissa:( 0x03e8,0,0,0,0,0,0,0)), -/*^04*/ Decimal(length: 1, mantissa:( 0x2710,0,0,0,0,0,0,0)), -/*^05*/ Decimal(length: 2, mantissa:( 0x86a0, 0x0001,0,0,0,0,0,0)), -/*^06*/ Decimal(length: 2, mantissa:( 0x4240, 0x000f,0,0,0,0,0,0)), -/*^07*/ Decimal(length: 2, mantissa:( 0x9680, 0x0098,0,0,0,0,0,0)), -/*^08*/ Decimal(length: 2, mantissa:( 0xe100, 0x05f5,0,0,0,0,0,0)), -/*^09*/ Decimal(length: 2, mantissa:( 0xca00, 0x3b9a,0,0,0,0,0,0)), -/*^10*/ Decimal(length: 3, mantissa:( 0xe400, 0x540b, 0x0002,0,0,0,0,0)), -/*^11*/ Decimal(length: 3, mantissa:( 0xe800, 0x4876, 0x0017,0,0,0,0,0)), -/*^12*/ Decimal(length: 3, mantissa:( 0x1000, 0xd4a5, 0x00e8,0,0,0,0,0)), -/*^13*/ Decimal(length: 3, mantissa:( 0xa000, 0x4e72, 0x0918,0,0,0,0,0)), -/*^14*/ Decimal(length: 3, mantissa:( 0x4000, 0x107a, 0x5af3,0,0,0,0,0)), -/*^15*/ Decimal(length: 4, mantissa:( 0x8000, 0xa4c6, 0x8d7e, 0x0003,0,0,0,0)), -/*^16*/ Decimal(length: 4, mantissa:( 0x0000, 0x6fc1, 0x86f2, 0x0023,0,0,0,0)), -/*^17*/ Decimal(length: 4, mantissa:( 0x0000, 0x5d8a, 0x4578, 0x0163,0,0,0,0)), -/*^18*/ Decimal(length: 4, mantissa:( 0x0000, 0xa764, 0xb6b3, 0x0de0,0,0,0,0)), -/*^19*/ Decimal(length: 4, mantissa:( 0x0000, 0x89e8, 0x2304, 0x8ac7,0,0,0,0)), -/*^20*/ Decimal(length: 5, mantissa:( 0x0000, 0x6310, 0x5e2d, 0x6bc7, 0x0005,0,0,0)), -/*^21*/ Decimal(length: 5, mantissa:( 0x0000, 0xdea0, 0xadc5, 0x35c9, 0x0036,0,0,0)), -/*^22*/ Decimal(length: 5, mantissa:( 0x0000, 0xb240, 0xc9ba, 0x19e0, 0x021e,0,0,0)), -/*^23*/ Decimal(length: 5, mantissa:( 0x0000, 0xf680, 0xe14a, 0x02c7, 0x152d,0,0,0)), -/*^24*/ Decimal(length: 5, mantissa:( 0x0000, 0xa100, 0xcced, 0x1bce, 0xd3c2,0,0,0)), -/*^25*/ Decimal(length: 6, mantissa:( 0x0000, 0x4a00, 0x0148, 0x1614, 0x4595, 0x0008,0,0)), -/*^26*/ Decimal(length: 6, mantissa:( 0x0000, 0xe400, 0x0cd2, 0xdcc8, 0xb7d2, 0x0052,0,0)), -/*^27*/ Decimal(length: 6, mantissa:( 0x0000, 0xe800, 0x803c, 0x9fd0, 0x2e3c, 0x033b,0,0)), -/*^28*/ Decimal(length: 6, mantissa:( 0x0000, 0x1000, 0x0261, 0x3e25, 0xce5e, 0x204f,0,0)), -/*^29*/ Decimal(length: 7, mantissa:( 0x0000, 0xa000, 0x17ca, 0x6d72, 0x0fae, 0x431e, 0x0001,0)), -/*^30*/ Decimal(length: 7, mantissa:( 0x0000, 0x4000, 0xedea, 0x4674, 0x9cd0, 0x9f2c, 0x000c,0)), -/*^31*/ Decimal(length: 7, mantissa:( 0x0000, 0x8000, 0x4b26, 0xc091, 0x2022, 0x37be, 0x007e,0)), -/*^32*/ Decimal(length: 7, mantissa:( 0x0000, 0x0000, 0xef81, 0x85ac, 0x415b, 0x2d6d, 0x04ee,0)), -/*^33*/ Decimal(length: 7, mantissa:( 0x0000, 0x0000, 0x5b0a, 0x38c1, 0x8d93, 0xc644, 0x314d,0)), -/*^34*/ Decimal(length: 8, mantissa:( 0x0000, 0x0000, 0x8e64, 0x378d, 0x87c0, 0xbead, 0xed09, 0x0001)), -/*^35*/ Decimal(length: 8, mantissa:( 0x0000, 0x0000, 0x8fe8, 0x2b87, 0x4d82, 0x72c7, 0x4261, 0x0013)), -/*^36*/ Decimal(length: 8, mantissa:( 0x0000, 0x0000, 0x9f10, 0xb34b, 0x0715, 0x7bc9, 0x97ce, 0x00c0)), -/*^37*/ Decimal(length: 8, mantissa:( 0x0000, 0x0000, 0x36a0, 0x00f4, 0x46d9, 0xd5da, 0xee10, 0x0785)), -/*^38*/ Decimal(length: 8, mantissa:( 0x0000, 0x0000, 0x2240, 0x098a, 0xc47a, 0x5a86, 0x4ca8, 0x4b3b)) -/*^39 is on 9 shorts. */ -] - - // Could be silently inexact for float and double. extension Scanner { diff --git a/Tests/Foundation/Tests/TestDecimal.swift b/Tests/Foundation/Tests/TestDecimal.swift index 1b250abeb0..1b02de88c5 100644 --- a/Tests/Foundation/Tests/TestDecimal.swift +++ b/Tests/Foundation/Tests/TestDecimal.swift @@ -267,21 +267,6 @@ class TestDecimal: XCTestCase { XCTAssertEqual(11, sm5) XCTAssertEqual(12, sm6) XCTAssertEqual(13, sm7) - - let ulp = explicit.ulp - XCTAssertEqual(0x7f, ulp.exponent) - XCTAssertEqual(1, ulp._length) - XCTAssertEqual(0, ulp._isNegative) - XCTAssertEqual(1, ulp._isCompact) - XCTAssertEqual(0, ulp._reserved) - XCTAssertEqual(1, ulp._mantissa.0) - XCTAssertEqual(0, ulp._mantissa.1) - XCTAssertEqual(0, ulp._mantissa.2) - XCTAssertEqual(0, ulp._mantissa.3) - XCTAssertEqual(0, ulp._mantissa.4) - XCTAssertEqual(0, ulp._mantissa.5) - XCTAssertEqual(0, ulp._mantissa.6) - XCTAssertEqual(0, ulp._mantissa.7) } func test_Maths() { @@ -374,8 +359,6 @@ class TestDecimal: XCTestCase { XCTAssertEqual((1234 as Double).hashValue, Decimal(1234).hashValue) XCTAssertEqual(Decimal(-9), Decimal(1) - Decimal(10)) - XCTAssertEqual(Decimal(3), Decimal(2).nextUp) - XCTAssertEqual(Decimal(2), Decimal(3).nextDown) XCTAssertEqual(Decimal(1.234), abs(Decimal(1.234))) XCTAssertEqual(Decimal(1.234), abs(Decimal(-1.234))) XCTAssertEqual((0 as Decimal).magnitude, 0 as Decimal) @@ -860,8 +843,41 @@ class TestDecimal: XCTestCase { } func test_ULP() { - let x = 0.1 as Decimal + var x = 0.1 as Decimal XCTAssertFalse(x.ulp > x) + + x = .nan + XCTAssertTrue(x.ulp.isNaN) + XCTAssertTrue(x.nextDown.isNaN) + XCTAssertTrue(x.nextUp.isNaN) + + x = .greatestFiniteMagnitude + XCTAssertEqual(x.ulp, Decimal(string: "1e127")!) + XCTAssertEqual(x.nextDown, x - Decimal(string: "1e127")!) + XCTAssertTrue(x.nextUp.isNaN) + + x = 1 + XCTAssertEqual(x.ulp, Decimal(string: "1e-38")!) + XCTAssertEqual(x.nextDown, x - Decimal(string: "1e-38")!) + XCTAssertEqual(x.nextUp, x + Decimal(string: "1e-38")!) + + x = .leastNonzeroMagnitude + // XCTAssertEqual(x.ulp, x) // SR-6671 + // XCTAssertEqual(x.nextDown, 0) // SR-6671 + // XCTAssertEqual(x.nextUp, x + x) // SR-6671 + XCTAssertNotEqual(x.ulp, 0) + XCTAssertNotEqual(x.nextDown, x) + XCTAssertNotEqual(x.nextUp, x) + + x = 0 + XCTAssertEqual(x.ulp, Decimal(string: "1e-128")!) + XCTAssertEqual(x.nextDown, -Decimal(string: "1e-128")!) + XCTAssertEqual(x.nextUp, Decimal(string: "1e-128")!) + + x = -1 + XCTAssertEqual(x.ulp, (1 as Decimal).ulp) + XCTAssertEqual(x.nextDown, -((1 as Decimal).nextUp)) + XCTAssertEqual(x.nextUp, -((1 as Decimal).nextDown)) } func test_ZeroPower() { From aed94d486f15a0318ec538be20b72c2dd4ac7a1e Mon Sep 17 00:00:00 2001 From: Xiaodi Wu <13952+xwu@users.noreply.github.com> Date: Sun, 5 Sep 2021 14:24:47 -0400 Subject: [PATCH 3/4] Fix a subtle bug in Decimal.ulp and adjust tests. --- .../TestDecimal.swift | 40 ++++- Darwin/Foundation-swiftoverlay/Decimal.swift | 99 +++++++------ Sources/Foundation/Decimal.swift | 140 ++++++++++++------ Tests/Foundation/Tests/TestDecimal.swift | 40 ++++- 4 files changed, 208 insertions(+), 111 deletions(-) diff --git a/Darwin/Foundation-swiftoverlay-Tests/TestDecimal.swift b/Darwin/Foundation-swiftoverlay-Tests/TestDecimal.swift index 8e10580274..05c6d0a4c0 100644 --- a/Darwin/Foundation-swiftoverlay-Tests/TestDecimal.swift +++ b/Darwin/Foundation-swiftoverlay-Tests/TestDecimal.swift @@ -604,16 +604,34 @@ class TestDecimal : XCTestCase { XCTAssertEqual(x.nextDown, x - Decimal(string: "1e127")!) XCTAssertTrue(x.nextUp.isNaN) + // '4' is an important value to test because the max supported + // significand of this type is not 10 ** 38 - 1 but rather 2 ** 128 - 1, + // for which reason '4.ulp' is not equal to '1.ulp' despite having the + // same decimal exponent. + x = 4 + XCTAssertEqual(x.ulp, Decimal(string: "1e-37")!) + XCTAssertEqual(x.nextDown, x - Decimal(string: "1e-37")!) + XCTAssertEqual(x.nextUp, x + Decimal(string: "1e-37")!) + XCTAssertEqual(x.nextDown.nextUp, x) + XCTAssertEqual(x.nextUp.nextDown, x) + XCTAssertNotEqual(x.nextDown, x) + XCTAssertNotEqual(x.nextUp, x) + x = 1 XCTAssertEqual(x.ulp, Decimal(string: "1e-38")!) XCTAssertEqual(x.nextDown, x - Decimal(string: "1e-38")!) XCTAssertEqual(x.nextUp, x + Decimal(string: "1e-38")!) + XCTAssertEqual(x.nextDown.nextUp, x) + XCTAssertEqual(x.nextUp.nextDown, x) + XCTAssertNotEqual(x.nextDown, x) + XCTAssertNotEqual(x.nextUp, x) x = .leastNonzeroMagnitude - // XCTAssertEqual(x.ulp, x) // SR-6671 - // XCTAssertEqual(x.nextDown, 0) // SR-6671 - // XCTAssertEqual(x.nextUp, x + x) // SR-6671 - XCTAssertNotEqual(x.ulp, 0) + XCTAssertEqual(x.ulp, x) + XCTAssertEqual(x.nextDown, 0) + XCTAssertEqual(x.nextUp, x + x) + XCTAssertEqual(x.nextDown.nextUp, x) + XCTAssertEqual(x.nextUp.nextDown, x) XCTAssertNotEqual(x.nextDown, x) XCTAssertNotEqual(x.nextUp, x) @@ -621,11 +639,19 @@ class TestDecimal : XCTestCase { XCTAssertEqual(x.ulp, Decimal(string: "1e-128")!) XCTAssertEqual(x.nextDown, -Decimal(string: "1e-128")!) XCTAssertEqual(x.nextUp, Decimal(string: "1e-128")!) + XCTAssertEqual(x.nextDown.nextUp, x) + XCTAssertEqual(x.nextUp.nextDown, x) + XCTAssertNotEqual(x.nextDown, x) + XCTAssertNotEqual(x.nextUp, x) x = -1 - XCTAssertEqual(x.ulp, (1 as Decimal).ulp) - XCTAssertEqual(x.nextDown, -((1 as Decimal).nextUp)) - XCTAssertEqual(x.nextUp, -((1 as Decimal).nextDown)) + XCTAssertEqual(x.ulp, Decimal(string: "1e-38")!) + XCTAssertEqual(x.nextDown, x - Decimal(string: "1e-38")!) + XCTAssertEqual(x.nextUp, x + Decimal(string: "1e-38")!) + XCTAssertEqual(x.nextDown.nextUp, x) + XCTAssertEqual(x.nextUp.nextDown, x) + XCTAssertNotEqual(x.nextDown, x) + XCTAssertNotEqual(x.nextUp, x) } func test_unconditionallyBridgeFromObjectiveC() { diff --git a/Darwin/Foundation-swiftoverlay/Decimal.swift b/Darwin/Foundation-swiftoverlay/Decimal.swift index 9064f66cc0..2551046f4d 100644 --- a/Darwin/Foundation-swiftoverlay/Decimal.swift +++ b/Darwin/Foundation-swiftoverlay/Decimal.swift @@ -324,55 +324,55 @@ extension Decimal : Strideable { } } -extension Decimal { - // (Used by `_powersOfTen` and `ulp`; note that the representation isn't compact.) - fileprivate init(_length: UInt32, _mantissa: (UInt16, UInt16, UInt16, UInt16, UInt16, UInt16, UInt16, UInt16)) { - self.init(_exponent: 0, _length: _length, _isNegative: 0, _isCompact: 0, +private extension Decimal { + // Creates a value with zero exponent. + // (Used by `_powersOfTenDividingUInt128Max`.) + init(_length: UInt32, _isCompact: UInt32, _mantissa: (UInt16, UInt16, UInt16, UInt16, UInt16, UInt16, UInt16, UInt16)) { + self.init(_exponent: 0, _length: _length, _isNegative: 0, _isCompact: _isCompact, _reserved: 0, _mantissa: _mantissa) } } -private let _powersOfTen = [ -/*^00*/ 1 as Decimal, -/*^01*/ Decimal(_length: 1, _mantissa: (0x000a,0,0,0,0,0,0,0)), -/*^02*/ Decimal(_length: 1, _mantissa: (0x0064,0,0,0,0,0,0,0)), -/*^03*/ Decimal(_length: 1, _mantissa: (0x03e8,0,0,0,0,0,0,0)), -/*^04*/ Decimal(_length: 1, _mantissa: (0x2710,0,0,0,0,0,0,0)), -/*^05*/ Decimal(_length: 2, _mantissa: (0x86a0, 0x0001,0,0,0,0,0,0)), -/*^06*/ Decimal(_length: 2, _mantissa: (0x4240, 0x000f,0,0,0,0,0,0)), -/*^07*/ Decimal(_length: 2, _mantissa: (0x9680, 0x0098,0,0,0,0,0,0)), -/*^08*/ Decimal(_length: 2, _mantissa: (0xe100, 0x05f5,0,0,0,0,0,0)), -/*^09*/ Decimal(_length: 2, _mantissa: (0xca00, 0x3b9a,0,0,0,0,0,0)), -/*^10*/ Decimal(_length: 3, _mantissa: (0xe400, 0x540b, 0x0002,0,0,0,0,0)), -/*^11*/ Decimal(_length: 3, _mantissa: (0xe800, 0x4876, 0x0017,0,0,0,0,0)), -/*^12*/ Decimal(_length: 3, _mantissa: (0x1000, 0xd4a5, 0x00e8,0,0,0,0,0)), -/*^13*/ Decimal(_length: 3, _mantissa: (0xa000, 0x4e72, 0x0918,0,0,0,0,0)), -/*^14*/ Decimal(_length: 3, _mantissa: (0x4000, 0x107a, 0x5af3,0,0,0,0,0)), -/*^15*/ Decimal(_length: 4, _mantissa: (0x8000, 0xa4c6, 0x8d7e, 0x0003,0,0,0,0)), -/*^16*/ Decimal(_length: 4, _mantissa: (0x0000, 0x6fc1, 0x86f2, 0x0023,0,0,0,0)), -/*^17*/ Decimal(_length: 4, _mantissa: (0x0000, 0x5d8a, 0x4578, 0x0163,0,0,0,0)), -/*^18*/ Decimal(_length: 4, _mantissa: (0x0000, 0xa764, 0xb6b3, 0x0de0,0,0,0,0)), -/*^19*/ Decimal(_length: 4, _mantissa: (0x0000, 0x89e8, 0x2304, 0x8ac7,0,0,0,0)), -/*^20*/ Decimal(_length: 5, _mantissa: (0x0000, 0x6310, 0x5e2d, 0x6bc7, 0x0005,0,0,0)), -/*^21*/ Decimal(_length: 5, _mantissa: (0x0000, 0xdea0, 0xadc5, 0x35c9, 0x0036,0,0,0)), -/*^22*/ Decimal(_length: 5, _mantissa: (0x0000, 0xb240, 0xc9ba, 0x19e0, 0x021e,0,0,0)), -/*^23*/ Decimal(_length: 5, _mantissa: (0x0000, 0xf680, 0xe14a, 0x02c7, 0x152d,0,0,0)), -/*^24*/ Decimal(_length: 5, _mantissa: (0x0000, 0xa100, 0xcced, 0x1bce, 0xd3c2,0,0,0)), -/*^25*/ Decimal(_length: 6, _mantissa: (0x0000, 0x4a00, 0x0148, 0x1614, 0x4595, 0x0008,0,0)), -/*^26*/ Decimal(_length: 6, _mantissa: (0x0000, 0xe400, 0x0cd2, 0xdcc8, 0xb7d2, 0x0052,0,0)), -/*^27*/ Decimal(_length: 6, _mantissa: (0x0000, 0xe800, 0x803c, 0x9fd0, 0x2e3c, 0x033b,0,0)), -/*^28*/ Decimal(_length: 6, _mantissa: (0x0000, 0x1000, 0x0261, 0x3e25, 0xce5e, 0x204f,0,0)), -/*^29*/ Decimal(_length: 7, _mantissa: (0x0000, 0xa000, 0x17ca, 0x6d72, 0x0fae, 0x431e, 0x0001,0)), -/*^30*/ Decimal(_length: 7, _mantissa: (0x0000, 0x4000, 0xedea, 0x4674, 0x9cd0, 0x9f2c, 0x000c,0)), -/*^31*/ Decimal(_length: 7, _mantissa: (0x0000, 0x8000, 0x4b26, 0xc091, 0x2022, 0x37be, 0x007e,0)), -/*^32*/ Decimal(_length: 7, _mantissa: (0x0000, 0x0000, 0xef81, 0x85ac, 0x415b, 0x2d6d, 0x04ee,0)), -/*^33*/ Decimal(_length: 7, _mantissa: (0x0000, 0x0000, 0x5b0a, 0x38c1, 0x8d93, 0xc644, 0x314d,0)), -/*^34*/ Decimal(_length: 8, _mantissa: (0x0000, 0x0000, 0x8e64, 0x378d, 0x87c0, 0xbead, 0xed09, 0x0001)), -/*^35*/ Decimal(_length: 8, _mantissa: (0x0000, 0x0000, 0x8fe8, 0x2b87, 0x4d82, 0x72c7, 0x4261, 0x0013)), -/*^36*/ Decimal(_length: 8, _mantissa: (0x0000, 0x0000, 0x9f10, 0xb34b, 0x0715, 0x7bc9, 0x97ce, 0x00c0)), -/*^37*/ Decimal(_length: 8, _mantissa: (0x0000, 0x0000, 0x36a0, 0x00f4, 0x46d9, 0xd5da, 0xee10, 0x0785)), -/*^38*/ Decimal(_length: 8, _mantissa: (0x0000, 0x0000, 0x2240, 0x098a, 0xc47a, 0x5a86, 0x4ca8, 0x4b3b)) -/*^39 is on 9 shorts.*/ +private let _powersOfTenDividingUInt128Max = [ +/* 10**00 dividing UInt128.max is deliberately omitted. */ +/* 10**01 */ Decimal(_length: 8, _isCompact: 1, _mantissa: (0x9999, 0x9999, 0x9999, 0x9999, 0x9999, 0x9999, 0x9999, 0x1999)), +/* 10**02 */ Decimal(_length: 8, _isCompact: 1, _mantissa: (0xf5c2, 0x5c28, 0xc28f, 0x28f5, 0x8f5c, 0xf5c2, 0x5c28, 0x028f)), +/* 10**03 */ Decimal(_length: 8, _isCompact: 1, _mantissa: (0x1893, 0x5604, 0x2d0e, 0x9db2, 0xa7ef, 0x4bc6, 0x8937, 0x0041)), +/* 10**04 */ Decimal(_length: 8, _isCompact: 1, _mantissa: (0x0275, 0x089a, 0x9e1b, 0x295e, 0x10cb, 0xbac7, 0x8db8, 0x0006)), +/* 10**05 */ Decimal(_length: 7, _isCompact: 1, _mantissa: (0x3372, 0x80dc, 0x0fcf, 0x8423, 0x1b47, 0xac47, 0xa7c5,0)), +/* 10**06 */ Decimal(_length: 7, _isCompact: 1, _mantissa: (0x3858, 0xf349, 0xb4c7, 0x8d36, 0xb5ed, 0xf7a0, 0x10c6,0)), +/* 10**07 */ Decimal(_length: 7, _isCompact: 1, _mantissa: (0xec08, 0x6520, 0x787a, 0xf485, 0xabca, 0x7f29, 0x01ad,0)), +/* 10**08 */ Decimal(_length: 7, _isCompact: 1, _mantissa: (0x4acd, 0x7083, 0xbf3f, 0x1873, 0xc461, 0xf31d, 0x002a,0)), +/* 10**09 */ Decimal(_length: 7, _isCompact: 1, _mantissa: (0x5447, 0x8b40, 0x2cb9, 0xb5a5, 0xfa09, 0x4b82, 0x0004,0)), +/* 10**10 */ Decimal(_length: 6, _isCompact: 1, _mantissa: (0xa207, 0x5ab9, 0xeadf, 0x5ef6, 0x7f67, 0x6df3,0,0)), +/* 10**11 */ Decimal(_length: 6, _isCompact: 1, _mantissa: (0xf69a, 0xef78, 0x4aaf, 0xbcb2, 0xbff0, 0x0afe,0,0)), +/* 10**12 */ Decimal(_length: 6, _isCompact: 1, _mantissa: (0x7f0f, 0x97f2, 0xa111, 0x12de, 0x7998, 0x0119,0,0)), +/* 10**13 */ Decimal(_length: 6, _isCompact: 0, _mantissa: (0x0cb4, 0xc265, 0x7681, 0x6849, 0x25c2, 0x001c,0,0)), +/* 10**14 */ Decimal(_length: 6, _isCompact: 1, _mantissa: (0x4e12, 0x603d, 0x2573, 0x70d4, 0xd093, 0x0002,0,0)), +/* 10**15 */ Decimal(_length: 5, _isCompact: 1, _mantissa: (0x87ce, 0x566c, 0x9d58, 0xbe7b, 0x480e,0,0,0)), +/* 10**16 */ Decimal(_length: 5, _isCompact: 1, _mantissa: (0xda61, 0x6f0a, 0xf622, 0xaca5, 0x0734,0,0,0)), +/* 10**17 */ Decimal(_length: 5, _isCompact: 1, _mantissa: (0x4909, 0xa4b4, 0x3236, 0x77aa, 0x00b8,0,0,0)), +/* 10**18 */ Decimal(_length: 5, _isCompact: 1, _mantissa: (0xa0e7, 0x43ab, 0xd1d2, 0x725d, 0x0012,0,0,0)), +/* 10**19 */ Decimal(_length: 5, _isCompact: 1, _mantissa: (0xc34a, 0x6d2a, 0x94fb, 0xd83c, 0x0001,0,0,0)), +/* 10**20 */ Decimal(_length: 4, _isCompact: 1, _mantissa: (0x46ba, 0x2484, 0x4219, 0x2f39,0,0,0,0)), +/* 10**21 */ Decimal(_length: 4, _isCompact: 1, _mantissa: (0xd3df, 0x83a6, 0xed02, 0x04b8,0,0,0,0)), +/* 10**22 */ Decimal(_length: 4, _isCompact: 1, _mantissa: (0x7b96, 0x405d, 0xe480, 0x0078,0,0,0,0)), +/* 10**23 */ Decimal(_length: 4, _isCompact: 1, _mantissa: (0x5928, 0xa009, 0x16d9, 0x000c,0,0,0,0)), +/* 10**24 */ Decimal(_length: 4, _isCompact: 1, _mantissa: (0x88ea, 0x299a, 0x357c, 0x0001,0,0,0,0)), +/* 10**25 */ Decimal(_length: 3, _isCompact: 1, _mantissa: (0xda7d, 0xd0f5, 0x1ef2,0,0,0,0,0)), +/* 10**26 */ Decimal(_length: 3, _isCompact: 1, _mantissa: (0x95d9, 0x4818, 0x0318,0,0,0,0,0)), +/* 10**27 */ Decimal(_length: 3, _isCompact: 0, _mantissa: (0xdbc8, 0x3a68, 0x004f,0,0,0,0,0)), +/* 10**28 */ Decimal(_length: 3, _isCompact: 1, _mantissa: (0xaf94, 0xec3d, 0x0007,0,0,0,0,0)), +/* 10**29 */ Decimal(_length: 2, _isCompact: 1, _mantissa: (0xf7f5, 0xcad2,0,0,0,0,0,0)), +/* 10**30 */ Decimal(_length: 2, _isCompact: 1, _mantissa: (0x4bfe, 0x1448,0,0,0,0,0,0)), +/* 10**31 */ Decimal(_length: 2, _isCompact: 1, _mantissa: (0x3acc, 0x0207,0,0,0,0,0,0)), +/* 10**32 */ Decimal(_length: 2, _isCompact: 1, _mantissa: (0xec47, 0x0033,0,0,0,0,0,0)), +/* 10**33 */ Decimal(_length: 2, _isCompact: 1, _mantissa: (0x313a, 0x0005,0,0,0,0,0,0)), +/* 10**34 */ Decimal(_length: 1, _isCompact: 1, _mantissa: (0x84ec,0,0,0,0,0,0,0)), +/* 10**35 */ Decimal(_length: 1, _isCompact: 1, _mantissa: (0x0d4a,0,0,0,0,0,0,0)), +/* 10**36 */ Decimal(_length: 1, _isCompact: 0, _mantissa: (0x0154,0,0,0,0,0,0,0)), +/* 10**37 */ Decimal(_length: 1, _isCompact: 1, _mantissa: (0x0022,0,0,0,0,0,0,0)), +/* 10**38 */ Decimal(_length: 1, _isCompact: 1, _mantissa: (0x0003,0,0,0,0,0,0,0)) ] // The methods in this extension exist to match the protocol requirements of @@ -607,10 +607,11 @@ extension Decimal { if isZero { exponent = .min } else { - let significand = Decimal(_length: _length, _mantissa: _mantissa) - let maxPowerOfTen = _powersOfTen.count - let powerOfTen = _powersOfTen.firstIndex { $0 > significand } ?? maxPowerOfTen - exponent = _exponent &- Int32(maxPowerOfTen &- powerOfTen) + let significand_ = significand + let shift = + _powersOfTenDividingUInt128Max.firstIndex { significand_ > $0 } + ?? _powersOfTenDividingUInt128Max.count + exponent = _exponent &- Int32(shift) } return Decimal( diff --git a/Sources/Foundation/Decimal.swift b/Sources/Foundation/Decimal.swift index b2a3eac49b..b09d9ecaf5 100644 --- a/Sources/Foundation/Decimal.swift +++ b/Sources/Foundation/Decimal.swift @@ -492,55 +492,98 @@ extension Decimal : Strideable { } } -extension Decimal { - // (Used by `_powersOfTen` and `ulp`; note that the representation isn't compact.) - fileprivate init(_length: UInt32, _mantissa: (UInt16, UInt16, UInt16, UInt16, UInt16, UInt16, UInt16, UInt16)) { - self.init(_exponent: 0, _length: _length, _isNegative: 0, _isCompact: 0, +private extension Decimal { + // Creates a value with zero exponent. + // (Used by `_powersOfTen*`.) + init(_length: UInt32, _isCompact: UInt32, _mantissa: (UInt16, UInt16, UInt16, UInt16, UInt16, UInt16, UInt16, UInt16)) { + self.init(_exponent: 0, _length: _length, _isNegative: 0, _isCompact: _isCompact, _reserved: 0, _mantissa: _mantissa) } } private let _powersOfTen = [ -/*^00*/ 1 as Decimal, -/*^01*/ Decimal(_length: 1, _mantissa: (0x000a,0,0,0,0,0,0,0)), -/*^02*/ Decimal(_length: 1, _mantissa: (0x0064,0,0,0,0,0,0,0)), -/*^03*/ Decimal(_length: 1, _mantissa: (0x03e8,0,0,0,0,0,0,0)), -/*^04*/ Decimal(_length: 1, _mantissa: (0x2710,0,0,0,0,0,0,0)), -/*^05*/ Decimal(_length: 2, _mantissa: (0x86a0, 0x0001,0,0,0,0,0,0)), -/*^06*/ Decimal(_length: 2, _mantissa: (0x4240, 0x000f,0,0,0,0,0,0)), -/*^07*/ Decimal(_length: 2, _mantissa: (0x9680, 0x0098,0,0,0,0,0,0)), -/*^08*/ Decimal(_length: 2, _mantissa: (0xe100, 0x05f5,0,0,0,0,0,0)), -/*^09*/ Decimal(_length: 2, _mantissa: (0xca00, 0x3b9a,0,0,0,0,0,0)), -/*^10*/ Decimal(_length: 3, _mantissa: (0xe400, 0x540b, 0x0002,0,0,0,0,0)), -/*^11*/ Decimal(_length: 3, _mantissa: (0xe800, 0x4876, 0x0017,0,0,0,0,0)), -/*^12*/ Decimal(_length: 3, _mantissa: (0x1000, 0xd4a5, 0x00e8,0,0,0,0,0)), -/*^13*/ Decimal(_length: 3, _mantissa: (0xa000, 0x4e72, 0x0918,0,0,0,0,0)), -/*^14*/ Decimal(_length: 3, _mantissa: (0x4000, 0x107a, 0x5af3,0,0,0,0,0)), -/*^15*/ Decimal(_length: 4, _mantissa: (0x8000, 0xa4c6, 0x8d7e, 0x0003,0,0,0,0)), -/*^16*/ Decimal(_length: 4, _mantissa: (0x0000, 0x6fc1, 0x86f2, 0x0023,0,0,0,0)), -/*^17*/ Decimal(_length: 4, _mantissa: (0x0000, 0x5d8a, 0x4578, 0x0163,0,0,0,0)), -/*^18*/ Decimal(_length: 4, _mantissa: (0x0000, 0xa764, 0xb6b3, 0x0de0,0,0,0,0)), -/*^19*/ Decimal(_length: 4, _mantissa: (0x0000, 0x89e8, 0x2304, 0x8ac7,0,0,0,0)), -/*^20*/ Decimal(_length: 5, _mantissa: (0x0000, 0x6310, 0x5e2d, 0x6bc7, 0x0005,0,0,0)), -/*^21*/ Decimal(_length: 5, _mantissa: (0x0000, 0xdea0, 0xadc5, 0x35c9, 0x0036,0,0,0)), -/*^22*/ Decimal(_length: 5, _mantissa: (0x0000, 0xb240, 0xc9ba, 0x19e0, 0x021e,0,0,0)), -/*^23*/ Decimal(_length: 5, _mantissa: (0x0000, 0xf680, 0xe14a, 0x02c7, 0x152d,0,0,0)), -/*^24*/ Decimal(_length: 5, _mantissa: (0x0000, 0xa100, 0xcced, 0x1bce, 0xd3c2,0,0,0)), -/*^25*/ Decimal(_length: 6, _mantissa: (0x0000, 0x4a00, 0x0148, 0x1614, 0x4595, 0x0008,0,0)), -/*^26*/ Decimal(_length: 6, _mantissa: (0x0000, 0xe400, 0x0cd2, 0xdcc8, 0xb7d2, 0x0052,0,0)), -/*^27*/ Decimal(_length: 6, _mantissa: (0x0000, 0xe800, 0x803c, 0x9fd0, 0x2e3c, 0x033b,0,0)), -/*^28*/ Decimal(_length: 6, _mantissa: (0x0000, 0x1000, 0x0261, 0x3e25, 0xce5e, 0x204f,0,0)), -/*^29*/ Decimal(_length: 7, _mantissa: (0x0000, 0xa000, 0x17ca, 0x6d72, 0x0fae, 0x431e, 0x0001,0)), -/*^30*/ Decimal(_length: 7, _mantissa: (0x0000, 0x4000, 0xedea, 0x4674, 0x9cd0, 0x9f2c, 0x000c,0)), -/*^31*/ Decimal(_length: 7, _mantissa: (0x0000, 0x8000, 0x4b26, 0xc091, 0x2022, 0x37be, 0x007e,0)), -/*^32*/ Decimal(_length: 7, _mantissa: (0x0000, 0x0000, 0xef81, 0x85ac, 0x415b, 0x2d6d, 0x04ee,0)), -/*^33*/ Decimal(_length: 7, _mantissa: (0x0000, 0x0000, 0x5b0a, 0x38c1, 0x8d93, 0xc644, 0x314d,0)), -/*^34*/ Decimal(_length: 8, _mantissa: (0x0000, 0x0000, 0x8e64, 0x378d, 0x87c0, 0xbead, 0xed09, 0x0001)), -/*^35*/ Decimal(_length: 8, _mantissa: (0x0000, 0x0000, 0x8fe8, 0x2b87, 0x4d82, 0x72c7, 0x4261, 0x0013)), -/*^36*/ Decimal(_length: 8, _mantissa: (0x0000, 0x0000, 0x9f10, 0xb34b, 0x0715, 0x7bc9, 0x97ce, 0x00c0)), -/*^37*/ Decimal(_length: 8, _mantissa: (0x0000, 0x0000, 0x36a0, 0x00f4, 0x46d9, 0xd5da, 0xee10, 0x0785)), -/*^38*/ Decimal(_length: 8, _mantissa: (0x0000, 0x0000, 0x2240, 0x098a, 0xc47a, 0x5a86, 0x4ca8, 0x4b3b)) -/*^39 is on 9 shorts.*/ +/* 10**00 */ Decimal(_length: 1, _isCompact: 1, _mantissa: (0x0001,0,0,0,0,0,0,0)), +/* 10**01 */ Decimal(_length: 1, _isCompact: 0, _mantissa: (0x000a,0,0,0,0,0,0,0)), +/* 10**02 */ Decimal(_length: 1, _isCompact: 0, _mantissa: (0x0064,0,0,0,0,0,0,0)), +/* 10**03 */ Decimal(_length: 1, _isCompact: 0, _mantissa: (0x03e8,0,0,0,0,0,0,0)), +/* 10**04 */ Decimal(_length: 1, _isCompact: 0, _mantissa: (0x2710,0,0,0,0,0,0,0)), +/* 10**05 */ Decimal(_length: 2, _isCompact: 0, _mantissa: (0x86a0, 0x0001,0,0,0,0,0,0)), +/* 10**06 */ Decimal(_length: 2, _isCompact: 0, _mantissa: (0x4240, 0x000f,0,0,0,0,0,0)), +/* 10**07 */ Decimal(_length: 2, _isCompact: 0, _mantissa: (0x9680, 0x0098,0,0,0,0,0,0)), +/* 10**08 */ Decimal(_length: 2, _isCompact: 0, _mantissa: (0xe100, 0x05f5,0,0,0,0,0,0)), +/* 10**09 */ Decimal(_length: 2, _isCompact: 0, _mantissa: (0xca00, 0x3b9a,0,0,0,0,0,0)), +/* 10**10 */ Decimal(_length: 3, _isCompact: 0, _mantissa: (0xe400, 0x540b, 0x0002,0,0,0,0,0)), +/* 10**11 */ Decimal(_length: 3, _isCompact: 0, _mantissa: (0xe800, 0x4876, 0x0017,0,0,0,0,0)), +/* 10**12 */ Decimal(_length: 3, _isCompact: 0, _mantissa: (0x1000, 0xd4a5, 0x00e8,0,0,0,0,0)), +/* 10**13 */ Decimal(_length: 3, _isCompact: 0, _mantissa: (0xa000, 0x4e72, 0x0918,0,0,0,0,0)), +/* 10**14 */ Decimal(_length: 3, _isCompact: 0, _mantissa: (0x4000, 0x107a, 0x5af3,0,0,0,0,0)), +/* 10**15 */ Decimal(_length: 4, _isCompact: 0, _mantissa: (0x8000, 0xa4c6, 0x8d7e, 0x0003,0,0,0,0)), +/* 10**16 */ Decimal(_length: 4, _isCompact: 0, _mantissa: (0x0000, 0x6fc1, 0x86f2, 0x0023,0,0,0,0)), +/* 10**17 */ Decimal(_length: 4, _isCompact: 0, _mantissa: (0x0000, 0x5d8a, 0x4578, 0x0163,0,0,0,0)), +/* 10**18 */ Decimal(_length: 4, _isCompact: 0, _mantissa: (0x0000, 0xa764, 0xb6b3, 0x0de0,0,0,0,0)), +/* 10**19 */ Decimal(_length: 4, _isCompact: 0, _mantissa: (0x0000, 0x89e8, 0x2304, 0x8ac7,0,0,0,0)), +/* 10**20 */ Decimal(_length: 5, _isCompact: 0, _mantissa: (0x0000, 0x6310, 0x5e2d, 0x6bc7, 0x0005,0,0,0)), +/* 10**21 */ Decimal(_length: 5, _isCompact: 0, _mantissa: (0x0000, 0xdea0, 0xadc5, 0x35c9, 0x0036,0,0,0)), +/* 10**22 */ Decimal(_length: 5, _isCompact: 0, _mantissa: (0x0000, 0xb240, 0xc9ba, 0x19e0, 0x021e,0,0,0)), +/* 10**23 */ Decimal(_length: 5, _isCompact: 0, _mantissa: (0x0000, 0xf680, 0xe14a, 0x02c7, 0x152d,0,0,0)), +/* 10**24 */ Decimal(_length: 5, _isCompact: 0, _mantissa: (0x0000, 0xa100, 0xcced, 0x1bce, 0xd3c2,0,0,0)), +/* 10**25 */ Decimal(_length: 6, _isCompact: 0, _mantissa: (0x0000, 0x4a00, 0x0148, 0x1614, 0x4595, 0x0008,0,0)), +/* 10**26 */ Decimal(_length: 6, _isCompact: 0, _mantissa: (0x0000, 0xe400, 0x0cd2, 0xdcc8, 0xb7d2, 0x0052,0,0)), +/* 10**27 */ Decimal(_length: 6, _isCompact: 0, _mantissa: (0x0000, 0xe800, 0x803c, 0x9fd0, 0x2e3c, 0x033b,0,0)), +/* 10**28 */ Decimal(_length: 6, _isCompact: 0, _mantissa: (0x0000, 0x1000, 0x0261, 0x3e25, 0xce5e, 0x204f,0,0)), +/* 10**29 */ Decimal(_length: 7, _isCompact: 0, _mantissa: (0x0000, 0xa000, 0x17ca, 0x6d72, 0x0fae, 0x431e, 0x0001,0)), +/* 10**30 */ Decimal(_length: 7, _isCompact: 0, _mantissa: (0x0000, 0x4000, 0xedea, 0x4674, 0x9cd0, 0x9f2c, 0x000c,0)), +/* 10**31 */ Decimal(_length: 7, _isCompact: 0, _mantissa: (0x0000, 0x8000, 0x4b26, 0xc091, 0x2022, 0x37be, 0x007e,0)), +/* 10**32 */ Decimal(_length: 7, _isCompact: 0, _mantissa: (0x0000, 0x0000, 0xef81, 0x85ac, 0x415b, 0x2d6d, 0x04ee,0)), +/* 10**33 */ Decimal(_length: 7, _isCompact: 0, _mantissa: (0x0000, 0x0000, 0x5b0a, 0x38c1, 0x8d93, 0xc644, 0x314d,0)), +/* 10**34 */ Decimal(_length: 8, _isCompact: 0, _mantissa: (0x0000, 0x0000, 0x8e64, 0x378d, 0x87c0, 0xbead, 0xed09, 0x0001)), +/* 10**35 */ Decimal(_length: 8, _isCompact: 0, _mantissa: (0x0000, 0x0000, 0x8fe8, 0x2b87, 0x4d82, 0x72c7, 0x4261, 0x0013)), +/* 10**36 */ Decimal(_length: 8, _isCompact: 0, _mantissa: (0x0000, 0x0000, 0x9f10, 0xb34b, 0x0715, 0x7bc9, 0x97ce, 0x00c0)), +/* 10**37 */ Decimal(_length: 8, _isCompact: 0, _mantissa: (0x0000, 0x0000, 0x36a0, 0x00f4, 0x46d9, 0xd5da, 0xee10, 0x0785)), +/* 10**38 */ Decimal(_length: 8, _isCompact: 0, _mantissa: (0x0000, 0x0000, 0x2240, 0x098a, 0xc47a, 0x5a86, 0x4ca8, 0x4b3b)) +/* 10**39 is on 9 shorts. */ +] + +private let _powersOfTenDividingUInt128Max = [ +/* 10**00 dividing UInt128.max is deliberately omitted. */ +/* 10**01 */ Decimal(_length: 8, _isCompact: 1, _mantissa: (0x9999, 0x9999, 0x9999, 0x9999, 0x9999, 0x9999, 0x9999, 0x1999)), +/* 10**02 */ Decimal(_length: 8, _isCompact: 1, _mantissa: (0xf5c2, 0x5c28, 0xc28f, 0x28f5, 0x8f5c, 0xf5c2, 0x5c28, 0x028f)), +/* 10**03 */ Decimal(_length: 8, _isCompact: 1, _mantissa: (0x1893, 0x5604, 0x2d0e, 0x9db2, 0xa7ef, 0x4bc6, 0x8937, 0x0041)), +/* 10**04 */ Decimal(_length: 8, _isCompact: 1, _mantissa: (0x0275, 0x089a, 0x9e1b, 0x295e, 0x10cb, 0xbac7, 0x8db8, 0x0006)), +/* 10**05 */ Decimal(_length: 7, _isCompact: 1, _mantissa: (0x3372, 0x80dc, 0x0fcf, 0x8423, 0x1b47, 0xac47, 0xa7c5,0)), +/* 10**06 */ Decimal(_length: 7, _isCompact: 1, _mantissa: (0x3858, 0xf349, 0xb4c7, 0x8d36, 0xb5ed, 0xf7a0, 0x10c6,0)), +/* 10**07 */ Decimal(_length: 7, _isCompact: 1, _mantissa: (0xec08, 0x6520, 0x787a, 0xf485, 0xabca, 0x7f29, 0x01ad,0)), +/* 10**08 */ Decimal(_length: 7, _isCompact: 1, _mantissa: (0x4acd, 0x7083, 0xbf3f, 0x1873, 0xc461, 0xf31d, 0x002a,0)), +/* 10**09 */ Decimal(_length: 7, _isCompact: 1, _mantissa: (0x5447, 0x8b40, 0x2cb9, 0xb5a5, 0xfa09, 0x4b82, 0x0004,0)), +/* 10**10 */ Decimal(_length: 6, _isCompact: 1, _mantissa: (0xa207, 0x5ab9, 0xeadf, 0x5ef6, 0x7f67, 0x6df3,0,0)), +/* 10**11 */ Decimal(_length: 6, _isCompact: 1, _mantissa: (0xf69a, 0xef78, 0x4aaf, 0xbcb2, 0xbff0, 0x0afe,0,0)), +/* 10**12 */ Decimal(_length: 6, _isCompact: 1, _mantissa: (0x7f0f, 0x97f2, 0xa111, 0x12de, 0x7998, 0x0119,0,0)), +/* 10**13 */ Decimal(_length: 6, _isCompact: 0, _mantissa: (0x0cb4, 0xc265, 0x7681, 0x6849, 0x25c2, 0x001c,0,0)), +/* 10**14 */ Decimal(_length: 6, _isCompact: 1, _mantissa: (0x4e12, 0x603d, 0x2573, 0x70d4, 0xd093, 0x0002,0,0)), +/* 10**15 */ Decimal(_length: 5, _isCompact: 1, _mantissa: (0x87ce, 0x566c, 0x9d58, 0xbe7b, 0x480e,0,0,0)), +/* 10**16 */ Decimal(_length: 5, _isCompact: 1, _mantissa: (0xda61, 0x6f0a, 0xf622, 0xaca5, 0x0734,0,0,0)), +/* 10**17 */ Decimal(_length: 5, _isCompact: 1, _mantissa: (0x4909, 0xa4b4, 0x3236, 0x77aa, 0x00b8,0,0,0)), +/* 10**18 */ Decimal(_length: 5, _isCompact: 1, _mantissa: (0xa0e7, 0x43ab, 0xd1d2, 0x725d, 0x0012,0,0,0)), +/* 10**19 */ Decimal(_length: 5, _isCompact: 1, _mantissa: (0xc34a, 0x6d2a, 0x94fb, 0xd83c, 0x0001,0,0,0)), +/* 10**20 */ Decimal(_length: 4, _isCompact: 1, _mantissa: (0x46ba, 0x2484, 0x4219, 0x2f39,0,0,0,0)), +/* 10**21 */ Decimal(_length: 4, _isCompact: 1, _mantissa: (0xd3df, 0x83a6, 0xed02, 0x04b8,0,0,0,0)), +/* 10**22 */ Decimal(_length: 4, _isCompact: 1, _mantissa: (0x7b96, 0x405d, 0xe480, 0x0078,0,0,0,0)), +/* 10**23 */ Decimal(_length: 4, _isCompact: 1, _mantissa: (0x5928, 0xa009, 0x16d9, 0x000c,0,0,0,0)), +/* 10**24 */ Decimal(_length: 4, _isCompact: 1, _mantissa: (0x88ea, 0x299a, 0x357c, 0x0001,0,0,0,0)), +/* 10**25 */ Decimal(_length: 3, _isCompact: 1, _mantissa: (0xda7d, 0xd0f5, 0x1ef2,0,0,0,0,0)), +/* 10**26 */ Decimal(_length: 3, _isCompact: 1, _mantissa: (0x95d9, 0x4818, 0x0318,0,0,0,0,0)), +/* 10**27 */ Decimal(_length: 3, _isCompact: 0, _mantissa: (0xdbc8, 0x3a68, 0x004f,0,0,0,0,0)), +/* 10**28 */ Decimal(_length: 3, _isCompact: 1, _mantissa: (0xaf94, 0xec3d, 0x0007,0,0,0,0,0)), +/* 10**29 */ Decimal(_length: 2, _isCompact: 1, _mantissa: (0xf7f5, 0xcad2,0,0,0,0,0,0)), +/* 10**30 */ Decimal(_length: 2, _isCompact: 1, _mantissa: (0x4bfe, 0x1448,0,0,0,0,0,0)), +/* 10**31 */ Decimal(_length: 2, _isCompact: 1, _mantissa: (0x3acc, 0x0207,0,0,0,0,0,0)), +/* 10**32 */ Decimal(_length: 2, _isCompact: 1, _mantissa: (0xec47, 0x0033,0,0,0,0,0,0)), +/* 10**33 */ Decimal(_length: 2, _isCompact: 1, _mantissa: (0x313a, 0x0005,0,0,0,0,0,0)), +/* 10**34 */ Decimal(_length: 1, _isCompact: 1, _mantissa: (0x84ec,0,0,0,0,0,0,0)), +/* 10**35 */ Decimal(_length: 1, _isCompact: 1, _mantissa: (0x0d4a,0,0,0,0,0,0,0)), +/* 10**36 */ Decimal(_length: 1, _isCompact: 0, _mantissa: (0x0154,0,0,0,0,0,0,0)), +/* 10**37 */ Decimal(_length: 1, _isCompact: 1, _mantissa: (0x0022,0,0,0,0,0,0,0)), +/* 10**38 */ Decimal(_length: 1, _isCompact: 1, _mantissa: (0x0003,0,0,0,0,0,0,0)) ] // The methods in this extension exist to match the protocol requirements of @@ -775,10 +818,11 @@ extension Decimal { if isZero { exponent = .min } else { - let significand = Decimal(_length: _length, _mantissa: _mantissa) - let maxPowerOfTen = _powersOfTen.count - let powerOfTen = _powersOfTen.firstIndex { $0 > significand } ?? maxPowerOfTen - exponent = _exponent &- Int32(maxPowerOfTen &- powerOfTen) + let significand_ = significand + let shift = + _powersOfTenDividingUInt128Max.firstIndex { significand_ > $0 } + ?? _powersOfTenDividingUInt128Max.count + exponent = _exponent &- Int32(shift) } return Decimal( diff --git a/Tests/Foundation/Tests/TestDecimal.swift b/Tests/Foundation/Tests/TestDecimal.swift index 1b02de88c5..c449241c90 100644 --- a/Tests/Foundation/Tests/TestDecimal.swift +++ b/Tests/Foundation/Tests/TestDecimal.swift @@ -856,16 +856,34 @@ class TestDecimal: XCTestCase { XCTAssertEqual(x.nextDown, x - Decimal(string: "1e127")!) XCTAssertTrue(x.nextUp.isNaN) + // '4' is an important value to test because the max supported + // significand of this type is not 10 ** 38 - 1 but rather 2 ** 128 - 1, + // for which reason '4.ulp' is not equal to '1.ulp' despite having the + // same decimal exponent. + x = 4 + XCTAssertEqual(x.ulp, Decimal(string: "1e-37")!) + XCTAssertEqual(x.nextDown, x - Decimal(string: "1e-37")!) + XCTAssertEqual(x.nextUp, x + Decimal(string: "1e-37")!) + XCTAssertEqual(x.nextDown.nextUp, x) + XCTAssertEqual(x.nextUp.nextDown, x) + XCTAssertNotEqual(x.nextDown, x) + XCTAssertNotEqual(x.nextUp, x) + x = 1 XCTAssertEqual(x.ulp, Decimal(string: "1e-38")!) XCTAssertEqual(x.nextDown, x - Decimal(string: "1e-38")!) XCTAssertEqual(x.nextUp, x + Decimal(string: "1e-38")!) + XCTAssertEqual(x.nextDown.nextUp, x) + XCTAssertEqual(x.nextUp.nextDown, x) + XCTAssertNotEqual(x.nextDown, x) + XCTAssertNotEqual(x.nextUp, x) x = .leastNonzeroMagnitude - // XCTAssertEqual(x.ulp, x) // SR-6671 - // XCTAssertEqual(x.nextDown, 0) // SR-6671 - // XCTAssertEqual(x.nextUp, x + x) // SR-6671 - XCTAssertNotEqual(x.ulp, 0) + XCTAssertEqual(x.ulp, x) + XCTAssertEqual(x.nextDown, 0) + XCTAssertEqual(x.nextUp, x + x) + XCTAssertEqual(x.nextDown.nextUp, x) + XCTAssertEqual(x.nextUp.nextDown, x) XCTAssertNotEqual(x.nextDown, x) XCTAssertNotEqual(x.nextUp, x) @@ -873,11 +891,19 @@ class TestDecimal: XCTestCase { XCTAssertEqual(x.ulp, Decimal(string: "1e-128")!) XCTAssertEqual(x.nextDown, -Decimal(string: "1e-128")!) XCTAssertEqual(x.nextUp, Decimal(string: "1e-128")!) + XCTAssertEqual(x.nextDown.nextUp, x) + XCTAssertEqual(x.nextUp.nextDown, x) + XCTAssertNotEqual(x.nextDown, x) + XCTAssertNotEqual(x.nextUp, x) x = -1 - XCTAssertEqual(x.ulp, (1 as Decimal).ulp) - XCTAssertEqual(x.nextDown, -((1 as Decimal).nextUp)) - XCTAssertEqual(x.nextUp, -((1 as Decimal).nextDown)) + XCTAssertEqual(x.ulp, Decimal(string: "1e-38")!) + XCTAssertEqual(x.nextDown, x - Decimal(string: "1e-38")!) + XCTAssertEqual(x.nextUp, x + Decimal(string: "1e-38")!) + XCTAssertEqual(x.nextDown.nextUp, x) + XCTAssertEqual(x.nextUp.nextDown, x) + XCTAssertNotEqual(x.nextDown, x) + XCTAssertNotEqual(x.nextUp, x) } func test_ZeroPower() { From 376c6b28aedb4100917cae3b75aad4cfdf1125c1 Mon Sep 17 00:00:00 2001 From: Xiaodi Wu <13952+xwu@users.noreply.github.com> Date: Tue, 7 Sep 2021 18:05:43 -0400 Subject: [PATCH 4/4] Fix implementation of Decimal.nextUp. --- .../TestDecimal.swift | 12 ++++++++++++ Darwin/Foundation-swiftoverlay/Decimal.swift | 17 +++++++++++++++++ Sources/Foundation/Decimal.swift | 17 +++++++++++++++++ Tests/Foundation/Tests/TestDecimal.swift | 12 ++++++++++++ 4 files changed, 58 insertions(+) diff --git a/Darwin/Foundation-swiftoverlay-Tests/TestDecimal.swift b/Darwin/Foundation-swiftoverlay-Tests/TestDecimal.swift index 05c6d0a4c0..6203fbefe7 100644 --- a/Darwin/Foundation-swiftoverlay-Tests/TestDecimal.swift +++ b/Darwin/Foundation-swiftoverlay-Tests/TestDecimal.swift @@ -617,6 +617,18 @@ class TestDecimal : XCTestCase { XCTAssertNotEqual(x.nextDown, x) XCTAssertNotEqual(x.nextUp, x) + // For similar reasons, '3.40282366920938463463374607431768211455', + // which has the same significand as 'Decimal.greatestFiniteMagnitude', + // is an important value to test because the distance to the next + // representable value is more than 'ulp' and instead requires + // incrementing '_exponent'. + x = Decimal(string: "3.40282366920938463463374607431768211455")! + XCTAssertEqual(x.ulp, Decimal(string: "0.00000000000000000000000000000000000001")!) + XCTAssertEqual(x.nextUp, Decimal(string: "3.4028236692093846346337460743176821146")!) + x = Decimal(string: "3.4028236692093846346337460743176821146")! + XCTAssertEqual(x.ulp, Decimal(string: "0.0000000000000000000000000000000000001")!) + XCTAssertEqual(x.nextDown, Decimal(string: "3.40282366920938463463374607431768211455")!) + x = 1 XCTAssertEqual(x.ulp, Decimal(string: "1e-38")!) XCTAssertEqual(x.nextDown, x - Decimal(string: "1e-38")!) diff --git a/Darwin/Foundation-swiftoverlay/Decimal.swift b/Darwin/Foundation-swiftoverlay/Decimal.swift index 2551046f4d..cdaa525a0a 100644 --- a/Darwin/Foundation-swiftoverlay/Decimal.swift +++ b/Darwin/Foundation-swiftoverlay/Decimal.swift @@ -620,6 +620,23 @@ extension Decimal { } public var nextUp: Decimal { + if _isNegative == 1 { + if _exponent > -128 + && (_mantissa.0, _mantissa.1, _mantissa.2, _mantissa.3) == (0x999a, 0x9999, 0x9999, 0x9999) + && (_mantissa.4, _mantissa.5, _mantissa.6, _mantissa.7) == (0x9999, 0x9999, 0x9999, 0x1999) { + return Decimal( + _exponent: _exponent &- 1, _length: 8, _isNegative: 1, _isCompact: 1, + _reserved: 0, _mantissa: (0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff)) + } + } else { + if _exponent < 127 + && (_mantissa.0, _mantissa.1, _mantissa.2, _mantissa.3) == (0xffff, 0xffff, 0xffff, 0xffff) + && (_mantissa.4, _mantissa.5, _mantissa.6, _mantissa.7) == (0xffff, 0xffff, 0xffff, 0xffff) { + return Decimal( + _exponent: _exponent &+ 1, _length: 8, _isNegative: 0, _isCompact: 1, + _reserved: 0, _mantissa: (0x999a, 0x9999, 0x9999, 0x9999, 0x9999, 0x9999, 0x9999, 0x1999)) + } + } return self + ulp } diff --git a/Sources/Foundation/Decimal.swift b/Sources/Foundation/Decimal.swift index b09d9ecaf5..3b3ba7e8c9 100644 --- a/Sources/Foundation/Decimal.swift +++ b/Sources/Foundation/Decimal.swift @@ -831,6 +831,23 @@ extension Decimal { } public var nextUp: Decimal { + if _isNegative == 1 { + if _exponent > -128 + && (_mantissa.0, _mantissa.1, _mantissa.2, _mantissa.3) == (0x999a, 0x9999, 0x9999, 0x9999) + && (_mantissa.4, _mantissa.5, _mantissa.6, _mantissa.7) == (0x9999, 0x9999, 0x9999, 0x1999) { + return Decimal( + _exponent: _exponent &- 1, _length: 8, _isNegative: 1, _isCompact: 1, + _reserved: 0, _mantissa: (0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff)) + } + } else { + if _exponent < 127 + && (_mantissa.0, _mantissa.1, _mantissa.2, _mantissa.3) == (0xffff, 0xffff, 0xffff, 0xffff) + && (_mantissa.4, _mantissa.5, _mantissa.6, _mantissa.7) == (0xffff, 0xffff, 0xffff, 0xffff) { + return Decimal( + _exponent: _exponent &+ 1, _length: 8, _isNegative: 0, _isCompact: 1, + _reserved: 0, _mantissa: (0x999a, 0x9999, 0x9999, 0x9999, 0x9999, 0x9999, 0x9999, 0x1999)) + } + } return self + ulp } diff --git a/Tests/Foundation/Tests/TestDecimal.swift b/Tests/Foundation/Tests/TestDecimal.swift index c449241c90..5286d9ded1 100644 --- a/Tests/Foundation/Tests/TestDecimal.swift +++ b/Tests/Foundation/Tests/TestDecimal.swift @@ -869,6 +869,18 @@ class TestDecimal: XCTestCase { XCTAssertNotEqual(x.nextDown, x) XCTAssertNotEqual(x.nextUp, x) + // For similar reasons, '3.40282366920938463463374607431768211455', + // which has the same significand as 'Decimal.greatestFiniteMagnitude', + // is an important value to test because the distance to the next + // representable value is more than 'ulp' and instead requires + // incrementing '_exponent'. + x = Decimal(string: "3.40282366920938463463374607431768211455")! + XCTAssertEqual(x.ulp, Decimal(string: "0.00000000000000000000000000000000000001")!) + XCTAssertEqual(x.nextUp, Decimal(string: "3.4028236692093846346337460743176821146")!) + x = Decimal(string: "3.4028236692093846346337460743176821146")! + XCTAssertEqual(x.ulp, Decimal(string: "0.0000000000000000000000000000000000001")!) + XCTAssertEqual(x.nextDown, Decimal(string: "3.40282366920938463463374607431768211455")!) + x = 1 XCTAssertEqual(x.ulp, Decimal(string: "1e-38")!) XCTAssertEqual(x.nextDown, x - Decimal(string: "1e-38")!)