Skip to content

Commit dcf6e2b

Browse files
natecook1000Greg Parker
authored andcommitted
[stdlib] Fix FloatingPoint.init(exactly:) (#12739)
* [stdlib] Fix FloatingPoint.init(exactly:) This initializer wasn't actually checking the exact conversion. SR-4634, rdar://problem/31836766
1 parent a0483e9 commit dcf6e2b

File tree

4 files changed

+62
-77
lines changed

4 files changed

+62
-77
lines changed

stdlib/public/core/FloatingPointTypes.swift.gyb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1503,8 +1503,9 @@ extension ${Self} {
15031503
public init?(exactly value: ${That}) {
15041504
_value = Builtin.${sign}itofp_${ThatBuiltinName}_FPIEEE${bits}(value._value)
15051505

1506-
% if srcBits < SignificandBitCount:
1507-
if ${That}(self) != value {
1506+
% if srcBits >= SignificandBitCount:
1507+
guard let roundTrip = ${That}(exactly: self),
1508+
roundTrip == value else {
15081509
return nil
15091510
}
15101511
% end

test/stdlib/TestNSNumberBridging.swift

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -658,16 +658,16 @@ func testNSNumberBridgeFromUInt() {
658658
let uint = (number!) as? UInt
659659
expectEqual(UInt(exactly: interestingValue), uint)
660660

661-
// these are disabled because of https://bugs.swift.org/browse/SR-4634
662-
if uint! != UInt(UInt32.max) && uint! != UInt(UInt32.max - 1) {
663-
let float = (number!) as? Float
664-
let expectedFloat = Float(uint!)
665-
testFloat(expectedFloat, float)
661+
let float = (number!) as? Float
662+
let expectedFloat = Float(exactly: uint!)
663+
if UInt.bitWidth == 32 && uint! >= UInt.max - 1 {
664+
expectNil(expectedFloat)
665+
} else {
666+
testFloat(expectedFloat, float)
666667
}
667-
668-
668+
669669
let double = (number!) as? Double
670-
let expectedDouble = Double(uint!)
670+
let expectedDouble = Double(exactly: uint!)
671671
testDouble(expectedDouble, double)
672672
}
673673
let bridged = interestingValue as NSNumber

validation-test/stdlib/FloatingPointConversion.swift.gyb

Lines changed: 43 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -115,74 +115,58 @@ FloatingPointConversionFailures.test("${OtherFloat}To${Self}Conversion/AlwaysSuc
115115

116116
% end # for in all_floating_point_types (Other)
117117

118-
%{
118+
#if arch(i386) || arch(arm)
119+
% int_types = all_integer_types(32)
120+
#elseif arch(x86_64) || arch(arm64) || arch(powerpc64) || arch(powerpc64le) || arch(s390x)
121+
% int_types = all_integer_types(64)
122+
#else
123+
_UnimplementedError()
124+
#endif
119125

120-
float_to_int_conversion_template = gyb.parse_template("float_to_int_conversion",
121-
"""
122-
% for int_ty in all_integer_types(word_bits):
126+
% for int_ty in int_types:
123127
% OtherInt = int_ty.stdlib_name
124-
% OtherMin = int_ty.min
125-
% OtherMax = int_ty.max
126-
% (FloatMin, FloatMax) = getFtoIBounds(self_type.bits, int_ty.bits, int_ty.is_signed)
127-
128-
% for testValue in [0, FloatMin, FloatMax, FloatMin - 1, FloatMax + 1, OtherMin, OtherMax]:
129-
130-
% if testValue < OtherMin or testValue > OtherMax:
131-
% # Can't construct `other` value, do nothing and continue.
132128

133-
% elif testValue >= FloatMin and testValue <= FloatMax:
134-
135-
FixedPointConversionTruncations.test("${OtherInt}to${Self}Conversion/${testValue}") {
136-
expectEqual(${Self}(${testValue} as ${OtherInt}), ${testValue})
137-
}
138-
139-
FixedPointConversionFailures.test("${OtherInt}to${Self}FailableConversion/${testValue}") {
140-
expectEqual(${Self}(exactly: ${testValue} as ${OtherInt}), ${testValue})
129+
extension ${OtherInt} {
130+
static var _test${Self}Conversion: [(${OtherInt}, ${OtherInt}, ${OtherInt}?)] {
131+
if bitWidth > ${Self}.significandBitCount + 1 {
132+
let bitOffset = ${Self}.significandBitCount + 1
133+
let limit: ${OtherInt} = ~(~0 << bitOffset)
134+
let over: ${OtherInt} = 1 + limit << 1
135+
return [
136+
(0, 0, 0),
137+
(limit, limit, limit),
138+
(over, over + 1, nil),
139+
% if int_ty.is_signed:
140+
(-limit, -limit, -limit),
141+
(-over, -(over + 1), nil),
142+
% end
143+
]
144+
} else {
145+
return [
146+
(0, 0, 0),
147+
(.min, .min, .min),
148+
(.max, .max, .max),
149+
]
150+
}
151+
}
141152
}
142153

143-
% else:
154+
FixedPointConversionTruncations.test("${OtherInt}to${Self}")
155+
.forEach(in: ${OtherInt}._test${Self}Conversion) {
156+
value, roundedExpectation, exactExpectation in
144157

145-
FixedPointConversionTruncations.test("${OtherInt}to${Self}Truncation/${testValue}") {
146-
let value: ${OtherInt} = ${testValue}
147-
let result = ${Self}(value)
148-
expectNotEqual(${OtherInt}(result), value)
149-
}
158+
let roundedResult = ${Self}(value)
159+
expectEqual(roundedResult, ${Self}(roundedExpectation))
150160

151-
FixedPointConversionFailures.test("${OtherInt}to${Self}Failure/${testValue}") {
152-
let value: ${OtherInt} = ${testValue}
153-
let result = ${Self}(exactly: value)
154-
expectEqual(result, ${OtherMin} as ${Self})
155-
expectEqual(${OtherInt}(result!), value)
161+
let exactResult = ${Self}(exactly: value)
162+
if let expectation = exactExpectation {
163+
expectEqual(exactResult!, ${Self}(expectation))
164+
} else {
165+
expectNil(exactResult)
166+
}
156167
}
157168

158-
% end
159-
160-
% end # testValue in testValues
161-
% end # for in all_integer_types (Other)
162-
""")
163-
}%
164-
165-
#if arch(i386) || arch(arm)
166-
167-
${gyb.execute_template(
168-
float_to_int_conversion_template,
169-
word_bits=32,
170-
**locals()
171-
)}
172-
173-
#elseif arch(x86_64) || arch(arm64) || arch(powerpc64) || arch(powerpc64le) || arch(s390x)
174-
175-
${gyb.execute_template(
176-
float_to_int_conversion_template,
177-
word_bits=64,
178-
**locals()
179-
)}
180-
181-
#else
182-
183-
_UnimplementedError()
184-
185-
#endif
169+
% end # for in int_types
186170

187171
% if Self == 'Float80':
188172
#endif

validation-test/stdlib/ValidationNSNumberBridging.swift

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -652,16 +652,16 @@ func testNSNumberBridgeFromUInt() {
652652
let uint = (number!) as? UInt
653653
expectEqual(UInt(exactly: interestingValue), uint)
654654

655-
// these are disabled because of https://bugs.swift.org/browse/SR-4634
656-
if uint! != UInt(UInt32.max) && uint! != UInt(UInt32.max - 1) {
657-
let float = (number!) as? Float
658-
let expectedFloat = Float(uint!)
659-
testFloat(expectedFloat, float)
655+
let float = (number!) as? Float
656+
let expectedFloat = Float(exactly: uint!)
657+
if UInt.bitWidth == 32 && uint! >= UInt.max - 1 {
658+
expectNil(expectedFloat)
659+
} else {
660+
testFloat(expectedFloat, float)
660661
}
661-
662-
662+
663663
let double = (number!) as? Double
664-
let expectedDouble = Double(uint!)
664+
let expectedDouble = Double(exactly: uint!)
665665
testDouble(expectedDouble, double)
666666
}
667667
let bridged = interestingValue as NSNumber

0 commit comments

Comments
 (0)