Skip to content

Commit 17f150f

Browse files
authored
Merge pull request #1759 from spevans/pr_decimal_doublevalue_fix
Decimal: Fix .doubleValue to use _length to only index valid mantissa elements.
2 parents c3d8ef9 + 54a29e2 commit 17f150f

File tree

2 files changed

+98
-12
lines changed

2 files changed

+98
-12
lines changed

Foundation/Decimal.swift

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -201,20 +201,19 @@ extension Decimal {
201201

202202
extension Decimal : Hashable, Comparable {
203203
internal var doubleValue: Double {
204+
if _length == 0 {
205+
if _isNegative == 1 {
206+
return Double.nan
207+
} else {
208+
return 0
209+
}
210+
}
211+
204212
var d = 0.0
205-
if _length == 0 && _isNegative == 1 {
206-
return Double.nan
213+
for idx in stride(from: min(_length, 8), to: 0, by: -1) {
214+
d = d * 65536 + Double(self[idx - 1])
207215
}
208-
209-
d = d * 65536 + Double(_mantissa.7)
210-
d = d * 65536 + Double(_mantissa.6)
211-
d = d * 65536 + Double(_mantissa.5)
212-
d = d * 65536 + Double(_mantissa.4)
213-
d = d * 65536 + Double(_mantissa.3)
214-
d = d * 65536 + Double(_mantissa.2)
215-
d = d * 65536 + Double(_mantissa.1)
216-
d = d * 65536 + Double(_mantissa.0)
217-
216+
218217
if _exponent < 0 {
219218
for _ in _exponent..<0 {
220219
d /= 10.0
@@ -226,9 +225,11 @@ extension Decimal : Hashable, Comparable {
226225
}
227226
return _isNegative != 0 ? -d : d
228227
}
228+
229229
public var hashValue: Int {
230230
return Int(bitPattern: __CFHashDouble(doubleValue))
231231
}
232+
232233
public static func ==(lhs: Decimal, rhs: Decimal) -> Bool {
233234
if lhs.isNaN {
234235
return rhs.isNaN

TestFoundation/TestDecimal.swift

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ class TestDecimal: XCTestCase {
3131
("test_SimpleMultiplication", test_SimpleMultiplication),
3232
("test_SmallerNumbers", test_SmallerNumbers),
3333
("test_ZeroPower", test_ZeroPower),
34+
("test_doubleValue", test_doubleValue)
3435
]
3536
}
3637

@@ -712,4 +713,88 @@ class TestDecimal: XCTestCase {
712713
XCTAssertEqual(1, negativeSix.raising(toPower: 0))
713714
}
714715

716+
func test_doubleValue() {
717+
XCTAssertEqual(NSDecimalNumber(decimal:Decimal(0)).doubleValue, 0)
718+
XCTAssertEqual(NSDecimalNumber(decimal:Decimal(1)).doubleValue, 1)
719+
XCTAssertEqual(NSDecimalNumber(decimal:Decimal(-1)).doubleValue, -1)
720+
XCTAssertTrue(NSDecimalNumber(decimal:Decimal.nan).doubleValue.isNaN)
721+
XCTAssertEqual(NSDecimalNumber(decimal:Decimal(UInt64.max)).doubleValue, Double(1.8446744073709552e+19))
722+
XCTAssertEqual(NSDecimalNumber(decimal:Decimal(string: "1234567890123456789012345678901234567890")!).doubleValue, Double(1.2345678901234568e+39))
723+
724+
var d = Decimal()
725+
d._mantissa.0 = 1
726+
d._mantissa.1 = 2
727+
d._mantissa.2 = 3
728+
d._mantissa.3 = 4
729+
d._mantissa.4 = 5
730+
d._mantissa.5 = 6
731+
d._mantissa.6 = 7
732+
d._mantissa.7 = 8
733+
734+
XCTAssertEqual(NSDecimalNumber(decimal: d).doubleValue, 0)
735+
XCTAssertEqual(d, Decimal(0))
736+
737+
d._length = 1
738+
XCTAssertEqual(NSDecimalNumber(decimal: d).doubleValue, 1)
739+
XCTAssertEqual(d, Decimal(1))
740+
741+
d._length = 2
742+
XCTAssertEqual(NSDecimalNumber(decimal: d).doubleValue, 131073)
743+
XCTAssertEqual(d, Decimal(131073))
744+
745+
d._length = 3
746+
XCTAssertEqual(NSDecimalNumber(decimal: d).doubleValue, 12885032961)
747+
XCTAssertEqual(d, Decimal(12885032961))
748+
749+
d._length = 4
750+
XCTAssertEqual(NSDecimalNumber(decimal: d).doubleValue, 1125912791875585)
751+
XCTAssertEqual(d, Decimal(1125912791875585))
752+
753+
d._length = 5
754+
XCTAssertEqual(NSDecimalNumber(decimal: d).doubleValue, 9.223484628133963e+19)
755+
XCTAssertEqual(d, Decimal(string: "92234846281339633665")!)
756+
757+
d._length = 6
758+
XCTAssertEqual(NSDecimalNumber(decimal: d).doubleValue, 7.253647152534056e+24)
759+
XCTAssertEqual(d, Decimal(string: "7253647152534056387870721")!)
760+
761+
d._length = 7
762+
XCTAssertEqual(NSDecimalNumber(decimal: d).doubleValue, 5.546043912470029e+29)
763+
XCTAssertEqual(d, Decimal(string: "554604391247002897211195523073")!)
764+
765+
d._length = 8
766+
XCTAssertEqual(NSDecimalNumber(decimal: d).doubleValue, 4.153892947266987e+34)
767+
XCTAssertEqual(d, Decimal(string: "41538929472669868031141181829283841")!)
768+
769+
// The result of the subtractions can leave values in the internal mantissa of a and b,
770+
// although _length = 0 which is correct.
771+
let x = Decimal(10.5)
772+
let y = Decimal(9.0)
773+
let z = Decimal(1.5)
774+
let a = x - y - z
775+
let b = x - z - y
776+
777+
XCTAssertEqual(x.description, "10.5")
778+
XCTAssertEqual(y.description, "9")
779+
XCTAssertEqual(z.description, "1.5")
780+
XCTAssertEqual(a.description, "0")
781+
XCTAssertEqual(b.description, "0")
782+
XCTAssertEqual(NSDecimalNumber(decimal: x).doubleValue, 10.5)
783+
XCTAssertEqual(NSDecimalNumber(decimal: y).doubleValue, 9.0)
784+
XCTAssertEqual(NSDecimalNumber(decimal: z).doubleValue, 1.5)
785+
XCTAssertEqual(NSDecimalNumber(decimal: a).doubleValue, 0.0)
786+
XCTAssertEqual(NSDecimalNumber(decimal: b).doubleValue, 0.0)
787+
788+
let nf = NumberFormatter()
789+
nf.locale = Locale(identifier: "en_US")
790+
nf.numberStyle = .decimal
791+
nf.minimumFractionDigits = 2
792+
nf.maximumFractionDigits = 2
793+
794+
XCTAssertEqual(nf.string(from: NSDecimalNumber(decimal: x)), "10.50")
795+
XCTAssertEqual(nf.string(from: NSDecimalNumber(decimal: y)), "9.00")
796+
XCTAssertEqual(nf.string(from: NSDecimalNumber(decimal: z)), "1.50")
797+
XCTAssertEqual(nf.string(from: NSDecimalNumber(decimal: a)), "0.00")
798+
XCTAssertEqual(nf.string(from: NSDecimalNumber(decimal: b)), "0.00")
799+
}
715800
}

0 commit comments

Comments
 (0)