Skip to content

SE-0080 (3/5) - Failable initializers for Float->Int #3356

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Aug 10, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions stdlib/public/core/FixedPoint.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
%{

from SwiftIntTypes import all_integer_types, int_max_bits, should_define_truncating_bit_pattern_init
from SwiftFloatingPointTypes import all_floating_point_types, getFtoIBounds

#
# Utility code for later in this template
Expand Down Expand Up @@ -524,6 +525,46 @@ extension ${Self} {
% end

extension ${Self} {
// Construction of integers from floating point numbers.
% for src_type in all_floating_point_types():
% Src = src_type.stdlib_name
% srcBits = src_type.bits
% (lower, upper) = getFtoIBounds(floatBits=srcBits, intBits=int(bits), signed=signed)

% if srcBits == 80:
#if !os(Windows) && (arch(i386) || arch(x86_64))
% end

/// Creates a new instance by rounding the given floating-point value toward
/// zero.
///
/// - Parameter other: A floating-point value. When `other` is rounded toward
/// zero, the result must be within the range `${Self}.min...${Self}.max`.
@_transparent
public init(_ value: ${Src}) {
_precondition(value.isFinite,
"${Src} value cannot be converted to ${Self} because it is either infinite or NaN")
_precondition(value > ${str(lower)}.0,
"${Src} value cannot be converted to ${Self} because the result would be less than ${Self}.min")
_precondition(value < ${str(upper)}.0,
"${Src} value cannot be converted to ${Self} because the result would be greater than ${Self}.max")
self._value = Builtin.fpto${sign}i_FPIEEE${srcBits}_${BuiltinName}(value._value)
}

/// Creates a ${Self} whose value is `value`
/// if no rounding is necessary, nil otherwise.
@inline(__always)
public init?(exactly value: ${Src}) {
self._value = Builtin.fpto${sign}i_FPIEEE${srcBits}_${BuiltinName}(value._value)
if ${Src}(self) != value {
return nil
}
}
% if srcBits == 80:
#endif
% end
% end

/// Construct a `${Self}` having the same memory representation as
/// the `${OtherSelf}` `bitPattern`. No range or overflow checking
/// occurs, and the resulting `${Self}` may not have the same numeric
Expand Down
113 changes: 22 additions & 91 deletions stdlib/public/core/FloatingPointTypes.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,12 @@
import SwiftShims

%{
from SwiftFloatingPointTypes import all_floating_point_types

#
# Utility code for later in this template
#

# // Bit counts for all floating point types.
# // 80-bit floating point types are only permitted on x86 architectures. This
# // restriction is handled via #if's in the generated code.
allFloatBits = [32, 64, 80]

# Bit counts for all int types
allIntBits = [8, 16, 32, 64, 'Int']

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

# Mapping from float bits to significand bits
explicitSignificandBits = { 32:24, 64:53, 80:64 }
SignificandSizes = { 32:32, 64:64, 80:64 }
SignificandBitCounts = { 32:23, 64:52, 80:63 }
ExponentBitCounts = { 32:8, 64:11, 80:15 }

def allInts():
for bits in allIntBits:
for signed in False, True:
Expand All @@ -50,21 +41,6 @@ def builtinIntName(name):

def intName(name, signed):
return ('' if signed else 'U') + baseIntName(name)

floatName = { 32:'Float', 64:'Double', 80:'Float80' }

def cFuncSuffix(bits):
if bits == 32:
return 'f'
if bits == 64:
return ''
if bits == 80:
return 'l'

def intFormatFix(bits):
if bits == 'Int':
return int(CMAKE_SIZEOF_VOID_P) * 8
return bits
}%

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

% for bits in allFloatBits:
% for self_type in all_floating_point_types():
%{
Self = floatName[bits]
SignificandSize = SignificandSizes[bits]
SignificandBitCount = SignificandBitCounts[bits]
ExponentBitCount = ExponentBitCounts[bits]
Self = self_type.stdlib_name
bits = self_type.bits
cFuncSuffix = self_type.cFuncSuffix
SignificandSize = self_type.significand_size
SignificandBitCount = self_type.significand_bits
ExponentBitCount = self_type.exponent_bits
RawSignificand = 'UInt' + str(SignificandSize)

if Self == 'Float':
Expand Down Expand Up @@ -601,7 +579,7 @@ extension ${Self}: BinaryFloatingPoint {
var other = other
_swift_stdlib_remainderl(&self, &other)
%else:
self = _swift_stdlib_remainder${cFuncSuffix(bits)}(self, other)
self = _swift_stdlib_remainder${cFuncSuffix}(self, other)
%end
}

Expand All @@ -615,7 +593,7 @@ extension ${Self}: BinaryFloatingPoint {
%if bits == 80:
_swift_stdlib_squareRootl(&self)
%else:
self = _swift_stdlib_squareRoot${cFuncSuffix(bits)}(self)
self = _swift_stdlib_squareRoot${cFuncSuffix}(self)
%end
}

Expand Down Expand Up @@ -867,8 +845,9 @@ extension ${Self} {
// Construction from other floating point numbers.
@_transparent
extension ${Self} {
% for srcBits in allFloatBits:
% That = floatName[srcBits]
% for src_type in all_floating_point_types():
% srcBits = src_type.bits
% That = src_type.stdlib_name

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

extension ${Self} {
@available(*, unavailable, message: "Please use the `abs(_:)` free function")
public static func abs(_ x: ${Self}) -> ${Self} {
fatalError("unavailable")
}
}

% if bits == 80:
#endif
% end
% end # for bits in allFloatBits

// Construction of integers from floating point numbers.
%{
def getFtoIBounds(floatBits, intBits, signed):
if not signed:
return (-1, 1 << intBits)
upper = 1 << intBits - 1
if intBits <= explicitSignificandBits[floatBits]:
return (-upper - 1, upper)
ulp = 1 << intBits - explicitSignificandBits[floatBits]
return (-upper - ulp, upper)
}%
% for (bits, signed) in allInts():
% sign = 's' if signed else 'u'
% Self = intName(bits, signed)
% BuiltinName = builtinIntName(bits)
% intBits = intFormatFix(bits)
@_transparent
extension ${Self} {
% for srcBits in allFloatBits:
% That = floatName[srcBits]

% if srcBits == 80:
#if !os(Windows) && (arch(i386) || arch(x86_64))
% end
/// Creates a new instance by rounding the given floating-point value toward
/// zero.
///
/// - Parameter other: A floating-point value. When `other` is rounded toward
/// zero, the result must be within the range `${Self}.min...${Self}.max`.
public init(_ other: ${That}) {
_precondition(other.isFinite,
"${That} value cannot be converted to ${Self} because it is either infinite or NaN")
% (lower, upper) = getFtoIBounds(srcBits, int(intBits), signed)
_precondition(other > ${str(lower)}.0,
"${That} value cannot be converted to ${Self} because the result would be less than ${Self}.min")
_precondition(other < ${str(upper)}.0,
"${That} value cannot be converted to ${Self} because the result would be greater than ${Self}.max")
self._value = Builtin.fpto${sign}i_FPIEEE${srcBits}_${BuiltinName}(other._value)
}
% if srcBits == 80:
#endif
% end
% end
}


extension ${Self} {
@available(*, unavailable, message: "Please use the `abs(_:)` free function")
public static func abs(_ x: ${Self}) -> ${Self} {
fatalError("unavailable")
}
}

% end # for (bits, signed) in allInts()
% end # for bits in all_floating_point_types
48 changes: 48 additions & 0 deletions utils/SwiftFloatingPointTypes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# ===--- SwiftFloatingPointTypes.py ----------------------------*- coding: utf-8 -*-===//
#
# This source file is part of the Swift.org open source project
#
# Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
# Licensed under Apache License v2.0 with Runtime Library Exception
#
# See http://swift.org/LICENSE.txt for license information
# See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors

# returns (lower, upper) exclusive bounds for the integer values
# that can be stored into a float
def getFtoIBounds(floatBits, intBits, signed):
sigBits = floating_point_bits_to_type()[floatBits].explicit_significand_bits
if not signed:
return (-1, 1 << intBits)
upper = 1 << (intBits - 1)
if intBits <= sigBits:
return (-upper - 1, upper)
ulp = 1 << (intBits - sigBits)
return (-upper - ulp, upper)

class SwiftFloatType(object):

def __init__(self, name, cFuncSuffix, significandBits, exponentBits, significandSize, totalBits):
self.stdlib_name = name
self.cFuncSuffix = cFuncSuffix
self.significand_bits = significandBits
self.significand_size = significandSize
self.exponent_bits = exponentBits
self.explicit_significand_bits = significandBits + 1
self.bits = totalBits

def floating_point_bits_to_type():
return {
32: SwiftFloatType(name="Float", cFuncSuffix="f", significandBits=23, exponentBits=8, significandSize=32, totalBits=32),
64: SwiftFloatType(name="Double", cFuncSuffix="", significandBits=52, exponentBits=11, significandSize=64, totalBits=64),
80: SwiftFloatType(name="Float80", cFuncSuffix="l", significandBits=63, exponentBits=15, significandSize=64, totalBits=80),
}

def all_floating_point_types():
return floating_point_bits_to_type().values()

# // Bit counts for all floating point types.
# // 80-bit floating point types are only permitted on x86 architectures. This
# // restriction is handled via #if's in the generated code.
def all_floating_point_bits():
return floating_point_bits_to_type().keys()
11 changes: 11 additions & 0 deletions utils/SwiftIntTypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@
# Number of bits in the biggest int type
int_max_bits = max(_all_integer_type_bitwidths)

def int_max(bits, signed):
bits = bits - 1 if signed else bits
bits = max(bits, 0)
return (1 << bits) - 1

def int_min(bits, signed):
return (-1 * int_max(bits, signed) - 1) if signed else 0

class SwiftIntegerType(object):

Expand All @@ -26,6 +33,9 @@ def __init__(self, is_word, bits, is_signed):
self.possible_bitwidths = [32, 64]
else:
self.possible_bitwidths = [bits]

self.min = int_min(bits, is_signed)
self.max = int_max(bits, is_signed)

# Derived properties
self.stdlib_name = \
Expand Down Expand Up @@ -118,3 +128,4 @@ def all_integer_assignment_operator_names():

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

4 changes: 3 additions & 1 deletion utils/gyb.py
Original file line number Diff line number Diff line change
Expand Up @@ -710,8 +710,10 @@ def execute(self, context):
# If we got a result, the code was an expression, so append
# its value
if result is not None and result != '':
from numbers import Number, Integral
result_string = repr(result) if isinstance(result, Number) and not isinstance(result, Integral) else str(result)
context.append_text(
str(result), self.filename, self.start_line_number)
result_string, self.filename, self.start_line_number)

def __str__(self, indent=''):
source_lines = re.sub(r'^\n', '', strip_trailing_nl(
Expand Down
Loading