Skip to content

Commit 0df0449

Browse files
authored
Merge pull request #1929 from spevans/pr_sr_1464_42
[4.2] SR-1464: NSNumber.description is not compatible between OS X and linux
2 parents 82e63f8 + a8de83b commit 0df0449

File tree

6 files changed

+277
-44
lines changed

6 files changed

+277
-44
lines changed

CoreFoundation/String.subproj/CFString.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5996,7 +5996,7 @@ enum {
59965996
CFFormatDummyPointerType = 42 /* special case for %n */
59975997
};
59985998

5999-
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS
5999+
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX
60006000
/* Only come in here if spec->type is CFFormatLongType or CFFormatDoubleType. Pass in 0 for width or precision if not specified. Returns false if couldn't do the format (with the assumption the caller falls back to unlocalized).
60016001
*/
60026002
static Boolean __CFStringFormatLocalizedNumber(CFMutableStringRef output, CFLocaleRef locale, const CFPrintValue *values, const CFFormatSpec *spec, SInt32 width, SInt32 precision, Boolean hasPrecision) {
@@ -6980,7 +6980,7 @@ static void __CFStringAppendFormatCore(CFMutableStringRef outputString, CFString
69806980
switch (specs[curSpec].type) {
69816981
case CFFormatLongType:
69826982
case CFFormatDoubleType:
6983-
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS
6983+
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX
69846984
if (localizedFormatting && (specs[curSpec].flags & kCFStringFormatLocalizable)) { // We have a locale, so we do localized formatting
69856985
if (__CFStringFormatLocalizedNumber(outputString, (CFLocaleRef)formatOptions, values, &specs[curSpec], width, precision, hasPrecision)) break;
69866986
}

Foundation/NSDecimalNumber.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,11 @@ open class NSDecimalNumber : NSNumber {
147147
public required convenience init(bytes buffer: UnsafeRawPointer, objCType type: UnsafePointer<Int8>) {
148148
NSRequiresConcreteImplementation()
149149
}
150-
150+
151+
open override var description: String {
152+
return self.decimal.description
153+
}
154+
151155
open override func description(withLocale locale: Locale?) -> String {
152156
guard locale == nil else {
153157
fatalError("Locale not supported: \(locale!)")

Foundation/NSNumber.swift

Lines changed: 70 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -915,7 +915,7 @@ open class NSNumber : NSValue {
915915
}
916916

917917
open var stringValue: String {
918-
return description(withLocale: nil)
918+
return self.description
919919
}
920920

921921
/// Create an instance initialized to `value`.
@@ -958,39 +958,86 @@ open class NSNumber : NSValue {
958958
}
959959
}
960960

961-
private static let _numberFormatterForNilLocale: CFNumberFormatter = {
962-
let formatter: CFNumberFormatter
963-
formatter = CFNumberFormatterCreate(nil, CFLocaleCopyCurrent(), kCFNumberFormatterNoStyle)
964-
CFNumberFormatterSetProperty(formatter, kCFNumberFormatterMaxFractionDigits, 15._bridgeToObjectiveC())
965-
return formatter
966-
}()
967-
968961
open func description(withLocale locale: Locale?) -> String {
969-
// CFNumberFormatterCreateStringWithNumber() does not like numbers of type
970-
// SInt128Type, as it loses the type when looking it up and treats it as
971-
// an SInt64Type, so special case them.
972-
if _CFNumberGetType2(_cfObject) == kCFNumberSInt128Type {
973-
return String(format: "%@", unsafeBitCast(_cfObject, to: UnsafePointer<CFNumber>.self))
974-
}
962+
guard let locale = locale else { return self.description }
963+
switch _CFNumberGetType2(_cfObject) {
964+
case kCFNumberSInt8Type, kCFNumberCharType:
965+
return String(format: "%d", locale: locale, self.int8Value)
975966

976-
let aLocale = locale
977-
let formatter: CFNumberFormatter
978-
if (aLocale == nil) {
979-
formatter = NSNumber._numberFormatterForNilLocale
980-
} else {
981-
formatter = CFNumberFormatterCreate(nil, aLocale?._cfObject, kCFNumberFormatterDecimalStyle)
967+
case kCFNumberSInt16Type, kCFNumberShortType:
968+
return String(format: "%hi", locale: locale, self.int16Value)
969+
970+
case kCFNumberSInt32Type:
971+
return String(format: "%d", locale: locale, self.int32Value)
972+
973+
case kCFNumberIntType, kCFNumberLongType, kCFNumberNSIntegerType, kCFNumberCFIndexType:
974+
return String(format: "%ld", locale: locale, self.intValue)
975+
976+
case kCFNumberSInt64Type, kCFNumberLongLongType:
977+
return String(format: "%lld", locale: locale, self.int64Value)
978+
979+
case kCFNumberSInt128Type:
980+
let value = self.int128Value
981+
if value.high == 0 {
982+
return value.low.description // BUG: "%llu" doesnt work correctly and treats number as signed
983+
} else {
984+
// BUG: Note the locale is actually ignored here as this is converted using CFNumber.c:emit128()
985+
return String(format: "%@", locale: locale, unsafeBitCast(_cfObject, to: UnsafePointer<CFNumber>.self))
986+
}
987+
988+
case kCFNumberFloatType, kCFNumberFloat32Type:
989+
return String(format: "%0.7g", locale: locale, self.floatValue)
990+
991+
case kCFNumberFloat64Type, kCFNumberDoubleType:
992+
return String(format: "%0.16g", locale: locale, self.doubleValue)
993+
994+
case kCFNumberCGFloatType:
995+
if Int.max == Int32.max {
996+
return String(format: "%0.7g", locale: locale, self.floatValue)
997+
} else {
998+
return String(format: "%0.16g", locale: locale, self.doubleValue)
999+
}
1000+
default: fatalError("Unknown NSNumber Type")
9821001
}
983-
return CFNumberFormatterCreateStringWithNumber(nil, formatter, self._cfObject)._swiftObject
9841002
}
9851003

9861004
override open var _cfTypeID: CFTypeID {
9871005
return CFNumberGetTypeID()
9881006
}
9891007

9901008
open override var description: String {
991-
return description(withLocale: nil)
1009+
switch _CFNumberGetType2(_cfObject) {
1010+
case kCFNumberSInt8Type, kCFNumberCharType, kCFNumberSInt16Type, kCFNumberShortType,
1011+
kCFNumberSInt32Type, kCFNumberIntType, kCFNumberLongType, kCFNumberNSIntegerType, kCFNumberCFIndexType:
1012+
return self.intValue.description
1013+
1014+
case kCFNumberSInt64Type, kCFNumberLongLongType:
1015+
return self.int64Value.description
1016+
1017+
case kCFNumberSInt128Type:
1018+
let value = self.int128Value
1019+
if value.high == 0 {
1020+
return value.low.description
1021+
} else {
1022+
return String(format: "%@", locale: nil, unsafeBitCast(_cfObject, to: UnsafePointer<CFNumber>.self))
1023+
}
1024+
1025+
case kCFNumberFloatType, kCFNumberFloat32Type:
1026+
return self.floatValue.description
1027+
1028+
case kCFNumberFloat64Type, kCFNumberDoubleType:
1029+
return self.doubleValue.description
1030+
1031+
case kCFNumberCGFloatType:
1032+
if Int.max == Int32.max {
1033+
return self.floatValue.description
1034+
} else {
1035+
return self.doubleValue.description
1036+
}
1037+
default: fatalError("Unknown NSNumber Type")
1038+
}
9921039
}
993-
1040+
9941041
internal func _cfNumberType() -> CFNumberType {
9951042
switch objCType.pointee {
9961043
case 0x42: return kCFNumberCharType

TestFoundation/TestDecimal.swift

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -504,7 +504,7 @@ class TestDecimal: XCTestCase {
504504
XCTAssertEqual(1, ten._length)
505505
}
506506

507-
func test_NSDecimal() {
507+
func test_NSDecimal() throws {
508508
var nan = Decimal.nan
509509
XCTAssertTrue(NSDecimalIsNotANumber(&nan))
510510
var zero = Decimal()
@@ -525,6 +525,31 @@ class TestDecimal: XCTestCase {
525525
let nsd1 = NSDecimalNumber(decimal: Decimal(2657.6))
526526
let nsd2 = NSDecimalNumber(floatLiteral: 2657.6)
527527
XCTAssertEqual(nsd1, nsd2)
528+
529+
XCTAssertEqual(NSDecimalNumber(decimal: Decimal(Int8.min)).description, Int8.min.description)
530+
XCTAssertEqual(NSDecimalNumber(decimal: Decimal(Int8.max)).description, Int8.max.description)
531+
XCTAssertEqual(NSDecimalNumber(decimal: Decimal(UInt8.min)).description, UInt8.min.description)
532+
XCTAssertEqual(NSDecimalNumber(decimal: Decimal(UInt8.max)).description, UInt8.max.description)
533+
534+
XCTAssertEqual(NSDecimalNumber(decimal: Decimal(Int16.min)).description, Int16.min.description)
535+
XCTAssertEqual(NSDecimalNumber(decimal: Decimal(Int16.max)).description, Int16.max.description)
536+
XCTAssertEqual(NSDecimalNumber(decimal: Decimal(UInt16.min)).description, UInt16.min.description)
537+
XCTAssertEqual(NSDecimalNumber(decimal: Decimal(UInt16.max)).description, UInt16.max.description)
538+
539+
XCTAssertEqual(NSDecimalNumber(decimal: Decimal(Int32.min)).description, Int32.min.description)
540+
XCTAssertEqual(NSDecimalNumber(decimal: Decimal(Int32.max)).description, Int32.max.description)
541+
XCTAssertEqual(NSDecimalNumber(decimal: Decimal(UInt32.min)).description, UInt32.min.description)
542+
XCTAssertEqual(NSDecimalNumber(decimal: Decimal(UInt32.max)).description, UInt32.max.description)
543+
544+
XCTAssertEqual(NSDecimalNumber(decimal: Decimal(Int64.min)).description, Int64.min.description)
545+
XCTAssertEqual(NSDecimalNumber(decimal: Decimal(Int64.max)).description, Int64.max.description)
546+
XCTAssertEqual(NSDecimalNumber(decimal: Decimal(UInt64.min)).description, UInt64.min.description)
547+
XCTAssertEqual(NSDecimalNumber(decimal: Decimal(UInt64.max)).description, UInt64.max.description)
548+
549+
XCTAssertEqual(try NSDecimalNumber(decimal: Decimal(string: "12.34").unwrapped()).description, "12.34")
550+
XCTAssertEqual(try NSDecimalNumber(decimal: Decimal(string: "0.0001").unwrapped()).description, "0.0001")
551+
XCTAssertEqual(try NSDecimalNumber(decimal: Decimal(string: "-1.0002").unwrapped()).description, "-1.0002")
552+
XCTAssertEqual(try NSDecimalNumber(decimal: Decimal(string: "0.0").unwrapped()).description, "0")
528553
}
529554

530555
func test_PositivePowers() {

0 commit comments

Comments
 (0)