Skip to content

Commit 430bdf0

Browse files
authored
Merge pull request #1900 from spevans/pr_decimal_doublevalue_fix_42
[4.2] Decimal: Fix .doubleValue to use _length to only index valid mantissa elements.
2 parents 6a0c9af + 1aa970f commit 430bdf0

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

@@ -697,4 +698,88 @@ class TestDecimal: XCTestCase {
697698
XCTAssertEqual(1, negativeSix.raising(toPower: 0))
698699
}
699700

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

0 commit comments

Comments
 (0)