Skip to content

Commit 8abc4a0

Browse files
committed
Validate the exactly pattern of Double from NSNumber to ensure proper IEEE 754 non lossy conversions
1 parent c17f16b commit 8abc4a0

File tree

2 files changed

+93
-2
lines changed

2 files changed

+93
-2
lines changed

stdlib/public/SDK/Foundation/NSNumber.swift

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,16 @@ extension Double : _ObjectiveCBridgeable {
481481
}
482482

483483
public init?(exactly number: NSNumber) {
484-
self = number.doubleValue
484+
let type = number.objCType.pointee
485+
if type == 0x51 {
486+
guard let result = Double(exactly: number.uint64Value) else { return nil }
487+
self = result
488+
} else if type == 0x71 {
489+
guard let result = Double(exactly: number.int64Value) else { return nil }
490+
self = result
491+
} else {
492+
self = number.doubleValue
493+
}
485494
}
486495

487496
@_semantics("convertToObjectiveC")

test/stdlib/NSNumberBridging.swift

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -727,6 +727,88 @@ func testNSNumberBridgeFromCGFloat() {
727727
}
728728
}
729729

730+
func test_numericBitPatterns_to_floatingPointTypes() {
731+
let signed_numbers: [NSNumber] = [
732+
NSNumber(value: Int64(bitPattern: 6)),
733+
NSNumber(value: Int64(bitPattern: 1 << 56)),
734+
NSNumber(value: Int64(bitPattern: 1 << 53)),
735+
NSNumber(value: Int64(bitPattern: 1 << 52)),
736+
NSNumber(value: Int64(bitPattern: 1 << 25)),
737+
NSNumber(value: Int64(bitPattern: 1 << 24)),
738+
NSNumber(value: Int64(bitPattern: 1 << 23)),
739+
NSNumber(value: -Int64(bitPattern: 1 << 53)),
740+
NSNumber(value: -Int64(bitPattern: 1 << 52)),
741+
NSNumber(value: -Int64(bitPattern: 6)),
742+
NSNumber(value: -Int64(bitPattern: 1 << 56)),
743+
NSNumber(value: -Int64(bitPattern: 1 << 25)),
744+
NSNumber(value: -Int64(bitPattern: 1 << 24)),
745+
NSNumber(value: -Int64(bitPattern: 1 << 23)),
746+
]
747+
748+
let signed_values: [Int64] = [
749+
Int64(bitPattern: 6),
750+
Int64(bitPattern: 1 << 56),
751+
Int64(bitPattern: 1 << 53),
752+
Int64(bitPattern: 1 << 52),
753+
Int64(bitPattern: 1 << 25),
754+
Int64(bitPattern: 1 << 24),
755+
Int64(bitPattern: 1 << 23),
756+
-Int64(bitPattern: 1 << 53),
757+
-Int64(bitPattern: 1 << 52),
758+
-Int64(bitPattern: 6),
759+
-Int64(bitPattern: 1 << 56),
760+
-Int64(bitPattern: 1 << 25),
761+
-Int64(bitPattern: 1 << 24),
762+
-Int64(bitPattern: 1 << 23),
763+
]
764+
765+
let unsigned_numbers: [NSNumber] = [
766+
NSNumber(value: UInt64(bitPattern: 6)),
767+
NSNumber(value: UInt64(bitPattern: 1 << 56)),
768+
NSNumber(value: UInt64(bitPattern: 1 << 63)),
769+
NSNumber(value: UInt64(bitPattern: 1 << 53)),
770+
NSNumber(value: UInt64(bitPattern: 1 << 52)),
771+
NSNumber(value: UInt64(bitPattern: 1 << 25)),
772+
NSNumber(value: UInt64(bitPattern: 1 << 24)),
773+
NSNumber(value: UInt64(bitPattern: 1 << 23)),
774+
]
775+
776+
let unsigned_values: [UInt64] = [
777+
UInt64(bitPattern: 6),
778+
UInt64(bitPattern: 1 << 56),
779+
UInt64(bitPattern: 1 << 63),
780+
UInt64(bitPattern: 1 << 53),
781+
UInt64(bitPattern: 1 << 52),
782+
UInt64(bitPattern: 1 << 25),
783+
UInt64(bitPattern: 1 << 24),
784+
UInt64(bitPattern: 1 << 23)
785+
]
786+
787+
for (number, value) in zip(signed_numbers, signed_values) {
788+
let numberCast = Double(exactly: number)
789+
let valueCast = Double(exactly: value)
790+
expectEqual(numberCast, valueCast)
791+
}
792+
793+
for (number, value) in zip(unsigned_numbers, unsigned_values) {
794+
let numberCast = Double(exactly: number)
795+
let valueCast = Double(exactly: value)
796+
expectEqual(numberCast, valueCast)
797+
}
798+
799+
for (number, value) in zip(signed_numbers, signed_values) {
800+
let numberCast = Float(exactly: number)
801+
let valueCast = Float(exactly: value)
802+
expectEqual(numberCast, valueCast)
803+
}
804+
805+
for (number, value) in zip(unsigned_numbers, unsigned_values) {
806+
let numberCast = Float(exactly: number)
807+
let valueCast = Float(exactly: value)
808+
expectEqual(numberCast, valueCast)
809+
}
810+
}
811+
730812
nsNumberBridging.test("Bridge Int8") { testNSNumberBridgeFromInt8() }
731813
nsNumberBridging.test("Bridge UInt8") { testNSNumberBridgeFromUInt8() }
732814
nsNumberBridging.test("Bridge Int16") { testNSNumberBridgeFromInt16() }
@@ -740,5 +822,5 @@ nsNumberBridging.test("Bridge UInt") { testNSNumberBridgeFromUInt() }
740822
nsNumberBridging.test("Bridge Float") { testNSNumberBridgeFromFloat() }
741823
nsNumberBridging.test("Bridge Double") { testNSNumberBridgeFromDouble() }
742824
nsNumberBridging.test("Bridge CGFloat") { testNSNumberBridgeFromCGFloat() }
743-
825+
nsNumberBridging.test("bitPattern to exactly") { test_numericBitPatterns_to_floatingPointTypes() }
744826
runAllTests()

0 commit comments

Comments
 (0)