Skip to content

Commit 420088f

Browse files
authored
Merge pull request #3356 from ultramiraculous/failable-float-to-int
SE-0080 (3/5) - Failable initializers for Float->Int
2 parents 22b18b2 + 636c5aa commit 420088f

File tree

8 files changed

+279
-241
lines changed

8 files changed

+279
-241
lines changed

stdlib/public/core/FixedPoint.swift.gyb

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
%{
1414

1515
from SwiftIntTypes import all_integer_types, int_max_bits, should_define_truncating_bit_pattern_init
16+
from SwiftFloatingPointTypes import all_floating_point_types, getFtoIBounds
1617

1718
#
1819
# Utility code for later in this template
@@ -524,6 +525,46 @@ extension ${Self} {
524525
% end
525526

526527
extension ${Self} {
528+
// Construction of integers from floating point numbers.
529+
% for src_type in all_floating_point_types():
530+
% Src = src_type.stdlib_name
531+
% srcBits = src_type.bits
532+
% (lower, upper) = getFtoIBounds(floatBits=srcBits, intBits=int(bits), signed=signed)
533+
534+
% if srcBits == 80:
535+
#if !os(Windows) && (arch(i386) || arch(x86_64))
536+
% end
537+
538+
/// Creates a new instance by rounding the given floating-point value toward
539+
/// zero.
540+
///
541+
/// - Parameter other: A floating-point value. When `other` is rounded toward
542+
/// zero, the result must be within the range `${Self}.min...${Self}.max`.
543+
@_transparent
544+
public init(_ value: ${Src}) {
545+
_precondition(value.isFinite,
546+
"${Src} value cannot be converted to ${Self} because it is either infinite or NaN")
547+
_precondition(value > ${str(lower)}.0,
548+
"${Src} value cannot be converted to ${Self} because the result would be less than ${Self}.min")
549+
_precondition(value < ${str(upper)}.0,
550+
"${Src} value cannot be converted to ${Self} because the result would be greater than ${Self}.max")
551+
self._value = Builtin.fpto${sign}i_FPIEEE${srcBits}_${BuiltinName}(value._value)
552+
}
553+
554+
/// Creates a ${Self} whose value is `value`
555+
/// if no rounding is necessary, nil otherwise.
556+
@inline(__always)
557+
public init?(exactly value: ${Src}) {
558+
self._value = Builtin.fpto${sign}i_FPIEEE${srcBits}_${BuiltinName}(value._value)
559+
if ${Src}(self) != value {
560+
return nil
561+
}
562+
}
563+
% if srcBits == 80:
564+
#endif
565+
% end
566+
% end
567+
527568
/// Construct a `${Self}` having the same memory representation as
528569
/// the `${OtherSelf}` `bitPattern`. No range or overflow checking
529570
/// occurs, and the resulting `${Self}` may not have the same numeric

stdlib/public/core/FloatingPointTypes.swift.gyb

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

1515
%{
16+
from SwiftFloatingPointTypes import all_floating_point_types
17+
1618
#
1719
# Utility code for later in this template
1820
#
1921

20-
# // Bit counts for all floating point types.
21-
# // 80-bit floating point types are only permitted on x86 architectures. This
22-
# // restriction is handled via #if's in the generated code.
23-
allFloatBits = [32, 64, 80]
24-
2522
# Bit counts for all int types
2623
allIntBits = [8, 16, 32, 64, 'Int']
2724

@@ -31,12 +28,6 @@ word_bits = int(CMAKE_SIZEOF_VOID_P) * 8
3128
# Number of bits in integer literals.
3229
builtinIntLiteralBits = 2048
3330

34-
# Mapping from float bits to significand bits
35-
explicitSignificandBits = { 32:24, 64:53, 80:64 }
36-
SignificandSizes = { 32:32, 64:64, 80:64 }
37-
SignificandBitCounts = { 32:23, 64:52, 80:63 }
38-
ExponentBitCounts = { 32:8, 64:11, 80:15 }
39-
4031
def allInts():
4132
for bits in allIntBits:
4233
for signed in False, True:
@@ -50,21 +41,6 @@ def builtinIntName(name):
5041

5142
def intName(name, signed):
5243
return ('' if signed else 'U') + baseIntName(name)
53-
54-
floatName = { 32:'Float', 64:'Double', 80:'Float80' }
55-
56-
def cFuncSuffix(bits):
57-
if bits == 32:
58-
return 'f'
59-
if bits == 64:
60-
return ''
61-
if bits == 80:
62-
return 'l'
63-
64-
def intFormatFix(bits):
65-
if bits == 'Int':
66-
return int(CMAKE_SIZEOF_VOID_P) * 8
67-
return bits
6844
}%
6945

7046
// TODO: remove once integer proposal is available ----------------------------
@@ -79,12 +55,14 @@ extension UInt${bits} {
7955
}
8056
%end
8157

82-
% for bits in allFloatBits:
58+
% for self_type in all_floating_point_types():
8359
%{
84-
Self = floatName[bits]
85-
SignificandSize = SignificandSizes[bits]
86-
SignificandBitCount = SignificandBitCounts[bits]
87-
ExponentBitCount = ExponentBitCounts[bits]
60+
Self = self_type.stdlib_name
61+
bits = self_type.bits
62+
cFuncSuffix = self_type.cFuncSuffix
63+
SignificandSize = self_type.significand_size
64+
SignificandBitCount = self_type.significand_bits
65+
ExponentBitCount = self_type.exponent_bits
8866
RawSignificand = 'UInt' + str(SignificandSize)
8967

9068
if Self == 'Float':
@@ -601,7 +579,7 @@ extension ${Self}: BinaryFloatingPoint {
601579
var other = other
602580
_swift_stdlib_remainderl(&self, &other)
603581
%else:
604-
self = _swift_stdlib_remainder${cFuncSuffix(bits)}(self, other)
582+
self = _swift_stdlib_remainder${cFuncSuffix}(self, other)
605583
%end
606584
}
607585

@@ -615,7 +593,7 @@ extension ${Self}: BinaryFloatingPoint {
615593
%if bits == 80:
616594
_swift_stdlib_squareRootl(&self)
617595
%else:
618-
self = _swift_stdlib_squareRoot${cFuncSuffix(bits)}(self)
596+
self = _swift_stdlib_squareRoot${cFuncSuffix}(self)
619597
%end
620598
}
621599

@@ -867,8 +845,9 @@ extension ${Self} {
867845
// Construction from other floating point numbers.
868846
@_transparent
869847
extension ${Self} {
870-
% for srcBits in allFloatBits:
871-
% That = floatName[srcBits]
848+
% for src_type in all_floating_point_types():
849+
% srcBits = src_type.bits
850+
% That = src_type.stdlib_name
872851

873852
% if srcBits == 80:
874853
#if !os(Windows) && (arch(i386) || arch(x86_64))
@@ -1077,62 +1056,14 @@ public postfix func -- (lhs: inout ${Self}) -> ${Self} {
10771056
fatalError("-- is not available")
10781057
}
10791058

1059+
extension ${Self} {
1060+
@available(*, unavailable, message: "Please use the `abs(_:)` free function")
1061+
public static func abs(_ x: ${Self}) -> ${Self} {
1062+
fatalError("unavailable")
1063+
}
1064+
}
1065+
10801066
% if bits == 80:
10811067
#endif
10821068
% end
1083-
% end # for bits in allFloatBits
1084-
1085-
// Construction of integers from floating point numbers.
1086-
%{
1087-
def getFtoIBounds(floatBits, intBits, signed):
1088-
if not signed:
1089-
return (-1, 1 << intBits)
1090-
upper = 1 << intBits - 1
1091-
if intBits <= explicitSignificandBits[floatBits]:
1092-
return (-upper - 1, upper)
1093-
ulp = 1 << intBits - explicitSignificandBits[floatBits]
1094-
return (-upper - ulp, upper)
1095-
}%
1096-
% for (bits, signed) in allInts():
1097-
% sign = 's' if signed else 'u'
1098-
% Self = intName(bits, signed)
1099-
% BuiltinName = builtinIntName(bits)
1100-
% intBits = intFormatFix(bits)
1101-
@_transparent
1102-
extension ${Self} {
1103-
% for srcBits in allFloatBits:
1104-
% That = floatName[srcBits]
1105-
1106-
% if srcBits == 80:
1107-
#if !os(Windows) && (arch(i386) || arch(x86_64))
1108-
% end
1109-
/// Creates a new instance by rounding the given floating-point value toward
1110-
/// zero.
1111-
///
1112-
/// - Parameter other: A floating-point value. When `other` is rounded toward
1113-
/// zero, the result must be within the range `${Self}.min...${Self}.max`.
1114-
public init(_ other: ${That}) {
1115-
_precondition(other.isFinite,
1116-
"${That} value cannot be converted to ${Self} because it is either infinite or NaN")
1117-
% (lower, upper) = getFtoIBounds(srcBits, int(intBits), signed)
1118-
_precondition(other > ${str(lower)}.0,
1119-
"${That} value cannot be converted to ${Self} because the result would be less than ${Self}.min")
1120-
_precondition(other < ${str(upper)}.0,
1121-
"${That} value cannot be converted to ${Self} because the result would be greater than ${Self}.max")
1122-
self._value = Builtin.fpto${sign}i_FPIEEE${srcBits}_${BuiltinName}(other._value)
1123-
}
1124-
% if srcBits == 80:
1125-
#endif
1126-
% end
1127-
% end
1128-
}
1129-
1130-
1131-
extension ${Self} {
1132-
@available(*, unavailable, message: "Please use the `abs(_:)` free function")
1133-
public static func abs(_ x: ${Self}) -> ${Self} {
1134-
fatalError("unavailable")
1135-
}
1136-
}
1137-
1138-
% end # for (bits, signed) in allInts()
1069+
% end # for bits in all_floating_point_types

utils/SwiftFloatingPointTypes.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# ===--- SwiftFloatingPointTypes.py ----------------------------*- coding: utf-8 -*-===//
2+
#
3+
# This source file is part of the Swift.org open source project
4+
#
5+
# Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
6+
# Licensed under Apache License v2.0 with Runtime Library Exception
7+
#
8+
# See http://swift.org/LICENSE.txt for license information
9+
# See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
11+
# returns (lower, upper) exclusive bounds for the integer values
12+
# that can be stored into a float
13+
def getFtoIBounds(floatBits, intBits, signed):
14+
sigBits = floating_point_bits_to_type()[floatBits].explicit_significand_bits
15+
if not signed:
16+
return (-1, 1 << intBits)
17+
upper = 1 << (intBits - 1)
18+
if intBits <= sigBits:
19+
return (-upper - 1, upper)
20+
ulp = 1 << (intBits - sigBits)
21+
return (-upper - ulp, upper)
22+
23+
class SwiftFloatType(object):
24+
25+
def __init__(self, name, cFuncSuffix, significandBits, exponentBits, significandSize, totalBits):
26+
self.stdlib_name = name
27+
self.cFuncSuffix = cFuncSuffix
28+
self.significand_bits = significandBits
29+
self.significand_size = significandSize
30+
self.exponent_bits = exponentBits
31+
self.explicit_significand_bits = significandBits + 1
32+
self.bits = totalBits
33+
34+
def floating_point_bits_to_type():
35+
return {
36+
32: SwiftFloatType(name="Float", cFuncSuffix="f", significandBits=23, exponentBits=8, significandSize=32, totalBits=32),
37+
64: SwiftFloatType(name="Double", cFuncSuffix="", significandBits=52, exponentBits=11, significandSize=64, totalBits=64),
38+
80: SwiftFloatType(name="Float80", cFuncSuffix="l", significandBits=63, exponentBits=15, significandSize=64, totalBits=80),
39+
}
40+
41+
def all_floating_point_types():
42+
return floating_point_bits_to_type().values()
43+
44+
# // Bit counts for all floating point types.
45+
# // 80-bit floating point types are only permitted on x86 architectures. This
46+
# // restriction is handled via #if's in the generated code.
47+
def all_floating_point_bits():
48+
return floating_point_bits_to_type().keys()

utils/SwiftIntTypes.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@
1414
# Number of bits in the biggest int type
1515
int_max_bits = max(_all_integer_type_bitwidths)
1616

17+
def int_max(bits, signed):
18+
bits = bits - 1 if signed else bits
19+
bits = max(bits, 0)
20+
return (1 << bits) - 1
21+
22+
def int_min(bits, signed):
23+
return (-1 * int_max(bits, signed) - 1) if signed else 0
1724

1825
class SwiftIntegerType(object):
1926

@@ -26,6 +33,9 @@ def __init__(self, is_word, bits, is_signed):
2633
self.possible_bitwidths = [32, 64]
2734
else:
2835
self.possible_bitwidths = [bits]
36+
37+
self.min = int_min(bits, is_signed)
38+
self.max = int_max(bits, is_signed)
2939

3040
# Derived properties
3141
self.stdlib_name = \
@@ -118,3 +128,4 @@ def all_integer_assignment_operator_names():
118128

119129
def all_integer_or_real_assignment_operator_names():
120130
return ['=', '*=', '/=', '+=', '-=']
131+

utils/gyb.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -710,8 +710,10 @@ def execute(self, context):
710710
# If we got a result, the code was an expression, so append
711711
# its value
712712
if result is not None and result != '':
713+
from numbers import Number, Integral
714+
result_string = repr(result) if isinstance(result, Number) and not isinstance(result, Integral) else str(result)
713715
context.append_text(
714-
str(result), self.filename, self.start_line_number)
716+
result_string, self.filename, self.start_line_number)
715717

716718
def __str__(self, indent=''):
717719
source_lines = re.sub(r'^\n', '', strip_trailing_nl(

0 commit comments

Comments
 (0)