Skip to content

Commit 883ca16

Browse files
authored
Merge pull request #4314 from ultramiraculous/failable-int-to-float
SE-0080 (4/4) - Failable initializers for Float->Int
2 parents 8fff45d + d5a6731 commit 883ca16

File tree

2 files changed

+97
-23
lines changed

2 files changed

+97
-23
lines changed

stdlib/public/core/FloatingPointTypes.swift.gyb

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -13,34 +13,18 @@
1313
import SwiftShims
1414

1515
%{
16+
from SwiftIntTypes import all_integer_types
1617
from SwiftFloatingPointTypes import all_floating_point_types
1718

1819
#
1920
# Utility code for later in this template
2021
#
2122

22-
# Bit counts for all int types
23-
allIntBits = [8, 16, 32, 64, 'Int']
24-
2523
# Number of bits in the Builtin.Word type
2624
word_bits = int(CMAKE_SIZEOF_VOID_P) * 8
2725

2826
# Number of bits in integer literals.
2927
builtinIntLiteralBits = 2048
30-
31-
def allInts():
32-
for bits in allIntBits:
33-
for signed in False, True:
34-
yield bits, signed
35-
36-
def baseIntName(name):
37-
return 'Int' if name == 'Int' else 'Int' + str(name)
38-
39-
def builtinIntName(name):
40-
return 'Int' + str(word_bits) if name == 'Int' else 'Int' + str(name)
41-
42-
def intName(name, signed):
43-
return ('' if signed else 'U') + baseIntName(name)
4428
}%
4529

4630
// TODO: remove once integer proposal is available ----------------------------
@@ -833,15 +817,31 @@ public prefix func - (x: ${Self}) -> ${Self} {
833817

834818
// Construction from integers.
835819
extension ${Self} {
836-
% for (srcBits, srcSigned) in allInts():
837-
% That = intName(srcBits, srcSigned)
838-
% ThatBuiltinName = builtinIntName(srcBits)
839-
% sign = 's' if srcSigned else 'u'
820+
821+
% for self_ty in all_integer_types(word_bits):
822+
% That = self_ty.stdlib_name
823+
% ThatBuiltinName = self_ty.builtin_name
824+
% srcBits = self_ty.bits
825+
% sign = 's' if self_ty.is_signed else 'u'
840826
@_transparent
841827
public init(_ v: ${That}) {
842828
_value = Builtin.${sign}itofp_${ThatBuiltinName}_FPIEEE${bits}(v._value)
843829
}
844-
% end
830+
831+
% if srcBits < SignificandBitCount:
832+
@available(*, message: "Converting ${That} to ${Self} will always succeed.")
833+
% end
834+
@inline(__always)
835+
public init?(exactly value: ${That}) {
836+
_value = Builtin.${sign}itofp_${ThatBuiltinName}_FPIEEE${bits}(value._value)
837+
838+
% if srcBits < SignificandBitCount:
839+
if ${That}(self) != value {
840+
return nil
841+
}
842+
% end
843+
}
844+
% end # all_integer_types
845845
}
846846

847847
// Construction from other floating point numbers.

validation-test/stdlib/FloatingPointConversion.swift.gyb

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,14 @@
1111
import StdlibUnittest
1212

1313
%{
14-
from SwiftFloatingPointTypes import all_floating_point_types
14+
import gyb
15+
from SwiftFloatingPointTypes import all_floating_point_types, getFtoIBounds
16+
from SwiftIntTypes import all_integer_types
1517
}%
1618

19+
var FixedPointConversionTruncations = TestSuite("FixedPointToFloatingPointConversionTruncations")
20+
var FixedPointConversionFailures = TestSuite("FixedPointToFloatingPointConversionFailures")
21+
1722
var FloatingPointConversionTruncations = TestSuite("FloatingPointToFloatingPointConversionTruncations")
1823
var FloatingPointConversionFailures = TestSuite("FloatingPointToFloatingPointConversionFailures")
1924

@@ -111,6 +116,75 @@ FloatingPointConversionFailures.test("${OtherFloat}To${Self}Conversion/AlwaysSuc
111116

112117
% end # for in all_floating_point_types (Other)
113118

119+
%{
120+
121+
float_to_int_conversion_template = gyb.parse_template("float_to_int_conversion",
122+
"""
123+
% for int_ty in all_integer_types(word_bits):
124+
% OtherInt = int_ty.stdlib_name
125+
% OtherMin = int_ty.min
126+
% OtherMax = int_ty.max
127+
% (FloatMin, FloatMax) = getFtoIBounds(self_type.bits, int_ty.bits, int_ty.is_signed)
128+
129+
% for testValue in [0, FloatMin, FloatMax, FloatMin - 1, FloatMax + 1, OtherMin, OtherMax]:
130+
131+
% if testValue < OtherMin or testValue > OtherMax:
132+
% # Can't construct `other` value, do nothing and continue.
133+
134+
% elif testValue >= FloatMin and testValue <= FloatMax:
135+
136+
FixedPointConversionTruncations.test("${OtherInt}to${Self}Conversion/${testValue}") {
137+
expectEqual(${Self}(${testValue} as ${OtherInt}), ${testValue})
138+
}
139+
140+
FixedPointConversionFailures.test("${OtherInt}to${Self}FailableConversion/${testValue}") {
141+
expectEqual(${Self}(exactly: ${testValue} as ${OtherInt}), ${testValue})
142+
}
143+
144+
% else:
145+
146+
FixedPointConversionTruncations.test("${OtherInt}to${Self}Truncation/${testValue}") {
147+
let value: ${OtherInt} = ${testValue}
148+
let result = ${Self}(value)
149+
expectNotEqual(${OtherInt}(result), value)
150+
}
151+
152+
FixedPointConversionFailures.test("${OtherInt}to${Self}Failure/${testValue}") {
153+
let value: ${OtherInt} = ${testValue}
154+
let result = ${Self}(exactly: value)
155+
expectEqual(result, ${OtherMin} as ${Self})
156+
expectEqual(${OtherInt}(result!), value)
157+
}
158+
159+
% end
160+
161+
% end # testValue in testValues
162+
% end # for in all_integer_types (Other)
163+
""")
164+
}%
165+
166+
#if arch(i386) || arch(arm)
167+
168+
${gyb.execute_template(
169+
float_to_int_conversion_template,
170+
word_bits=32,
171+
**locals()
172+
)}
173+
174+
#elseif arch(x86_64) || arch(arm64) || arch(powerpc64) || arch(powerpc64le) || arch(s390x)
175+
176+
${gyb.execute_template(
177+
float_to_int_conversion_template,
178+
word_bits=64,
179+
**locals()
180+
)}
181+
182+
#else
183+
184+
_UnimplementedError()
185+
186+
#endif
187+
114188
% if Self == 'Float80':
115189
#endif
116190
% end

0 commit comments

Comments
 (0)