mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Implement fixed-width integer conversions from binary floating point
Make internal stdlib function public because it is called from stdlib tests Add some first-thought optimizations
This commit is contained in:
@@ -1581,25 +1581,6 @@ extension BinaryInteger {
|
||||
self = 0
|
||||
}
|
||||
|
||||
/// Creates an integer from the given floating-point value, if it can be
|
||||
/// represented exactly.
|
||||
///
|
||||
/// If the value passed as `source` is not representable exactly, the result
|
||||
/// is `nil`. In the following example, the constant `x` is successfully
|
||||
/// created from a value of `21.0`, while the attempt to initialize the
|
||||
/// constant `y` from `21.5` fails:
|
||||
///
|
||||
/// let x = Int(exactly: 21.0)
|
||||
/// // x == Optional(21)
|
||||
/// let y = Int(exactly: 21.5)
|
||||
/// // y == nil
|
||||
///
|
||||
/// - Parameter source: A floating-point value to convert to an integer.
|
||||
public init?<T : BinaryFloatingPoint>(exactly source: T) {
|
||||
// FIXME(integers): implement
|
||||
fatalError()
|
||||
}
|
||||
|
||||
@_transparent
|
||||
public func signum() -> Self {
|
||||
return (self > (0 as Self) ? 1 : 0) - (self < (0 as Self) ? 1 : 0)
|
||||
@@ -2290,6 +2271,93 @@ ${operatorComment(x.nonMaskingOperator, True)}
|
||||
}
|
||||
|
||||
extension FixedWidthInteger {
|
||||
@_semantics("optimize.sil.specialize.generic.partial.never")
|
||||
public // @testable
|
||||
static func _convert<Source : BinaryFloatingPoint>(
|
||||
from source: Source
|
||||
) -> (value: Self?, exact: Bool) {
|
||||
guard _fastPath(!source.isZero) else { return (0, true) }
|
||||
guard _fastPath(source.isFinite) else { return (nil, false) }
|
||||
guard Self.isSigned || source > 0 else { return (nil, false) }
|
||||
let exponent = source.exponent
|
||||
if _slowPath(Self.bitWidth <= exponent) { return (nil, false) }
|
||||
let minBitWidth = source.significandWidth
|
||||
let isExact = (minBitWidth <= exponent)
|
||||
let bitPattern = source.significandBitPattern
|
||||
// `RawSignificand.bitWidth` is not available if `RawSignificand` does not
|
||||
// conform to `FixedWidthInteger`; we can compute this value as follows if
|
||||
// `source` is finite:
|
||||
let bitWidth = minBitWidth &+ bitPattern.trailingZeroBitCount
|
||||
let shift = exponent - Source.Exponent(bitWidth)
|
||||
// Use `Self.Magnitude` to prevent sign extension if `shift < 0`.
|
||||
let shiftedBitPattern = Self.Magnitude.bitWidth > bitWidth
|
||||
? Self.Magnitude(truncatingIfNeeded: bitPattern) << shift
|
||||
: Self.Magnitude(truncatingIfNeeded: bitPattern << shift)
|
||||
if _slowPath(Self.isSigned && Self.bitWidth &- 1 == exponent) {
|
||||
return source < 0 && shiftedBitPattern == 0
|
||||
? (Self.min, isExact)
|
||||
: (nil, false)
|
||||
}
|
||||
let magnitude = ((1 as Self.Magnitude) << exponent) | shiftedBitPattern
|
||||
return (
|
||||
Self.isSigned && source < 0 ? 0 &- Self(magnitude) : Self(magnitude),
|
||||
isExact)
|
||||
}
|
||||
|
||||
/// Creates an integer from the given floating-point value, rounding toward
|
||||
/// zero. Any fractional part of the value passed as `source` is removed.
|
||||
///
|
||||
/// let x = Int(21.5)
|
||||
/// // x == 21
|
||||
/// let y = Int(-21.5)
|
||||
/// // y == -21
|
||||
///
|
||||
/// If `source` is outside the bounds of this type after rounding toward
|
||||
/// zero, a runtime error may occur.
|
||||
///
|
||||
/// let z = UInt(-21.5)
|
||||
/// // Error: ...the result would be less than UInt.min
|
||||
///
|
||||
/// - Parameter source: A floating-point value to convert to an integer.
|
||||
/// `source` must be representable in this type after rounding toward
|
||||
/// zero.
|
||||
@_semantics("optimize.sil.specialize.generic.partial.never")
|
||||
@inline(__always)
|
||||
public init<T : BinaryFloatingPoint>(_ source: T) {
|
||||
guard let value = Self._convert(from: source).value else {
|
||||
fatalError("""
|
||||
\(T.self) value cannot be converted to \(Self.self) because it is \
|
||||
infinite or NaN, or because the result would be greater than \
|
||||
\(Self.self).max or less than \(Self.self).min
|
||||
""")
|
||||
}
|
||||
self = value
|
||||
}
|
||||
|
||||
/// Creates an integer from the given floating-point value, if it can be
|
||||
/// represented exactly.
|
||||
///
|
||||
/// If the value passed as `source` is not representable exactly, the result
|
||||
/// is `nil`. In the following example, the constant `x` is successfully
|
||||
/// created from a value of `21.0`, while the attempt to initialize the
|
||||
/// constant `y` from `21.5` fails:
|
||||
///
|
||||
/// let x = Int(exactly: 21.0)
|
||||
/// // x == Optional(21)
|
||||
/// let y = Int(exactly: 21.5)
|
||||
/// // y == nil
|
||||
///
|
||||
/// - Parameter source: A floating-point value to convert to an integer.
|
||||
@_semantics("optimize.sil.specialize.generic.partial.never")
|
||||
@inline(__always)
|
||||
public init?<T : BinaryFloatingPoint>(exactly source: T) {
|
||||
let (temporary, exact) = Self._convert(from: source)
|
||||
guard exact, let value = temporary else {
|
||||
return nil
|
||||
}
|
||||
self = value
|
||||
}
|
||||
|
||||
/// Creates a new instance with the representable value that's closest to the
|
||||
/// given integer.
|
||||
///
|
||||
@@ -2311,7 +2379,7 @@ extension FixedWidthInteger {
|
||||
///
|
||||
/// - Parameter source: An integer to convert to this type.
|
||||
@_semantics("optimize.sil.specialize.generic.partial.never")
|
||||
public init<Other: BinaryInteger>(clamping source: Other) {
|
||||
public init<Other : BinaryInteger>(clamping source: Other) {
|
||||
if _slowPath(source < Self.min) {
|
||||
self = Self.min
|
||||
}
|
||||
@@ -3219,47 +3287,6 @@ ${assignmentOperatorComment(x.operator, True)}
|
||||
}
|
||||
%# end of concrete type: ${Self}
|
||||
|
||||
extension ${Self} {
|
||||
/// Creates an integer from the given floating-point value, rounding toward
|
||||
/// zero.
|
||||
///
|
||||
/// Any fractional part of the value passed as `source` is removed, rounding
|
||||
/// the value toward zero.
|
||||
///
|
||||
/// let x = Int(21.5)
|
||||
/// // x == 21
|
||||
/// let y = Int(-21.5)
|
||||
/// // y == -21
|
||||
///
|
||||
/// If `source` is outside the bounds of this type after rounding toward
|
||||
/// zero, a runtime error may occur.
|
||||
///
|
||||
/// let z = UInt(-21.5)
|
||||
/// // Error: ...the result would be less than UInt.min
|
||||
///
|
||||
/// - Parameter source: A floating-point value to convert to an integer.
|
||||
/// `source` must be representable in this type after rounding toward
|
||||
/// zero.
|
||||
// FIXME(integers): implement me in a less terrible way
|
||||
public init<T : BinaryFloatingPoint>(_ source: T) {
|
||||
% for (FloatType, FloatBits) in [
|
||||
% ('Float', 32), ('Double', 64), ('Float80', 80)]:
|
||||
% if FloatType == 'Float80':
|
||||
#if !os(Windows) && (arch(i386) || arch(x86_64))
|
||||
% end
|
||||
if source is ${FloatType} {
|
||||
self.init(source as! ${FloatType})
|
||||
return
|
||||
}
|
||||
% if FloatType == 'Float80':
|
||||
#endif
|
||||
% end
|
||||
% end
|
||||
_preconditionFailure("Conversion is not supported")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension ${Self} : Hashable {
|
||||
/// The integer's hash value.
|
||||
///
|
||||
|
||||
@@ -40,3 +40,89 @@ print(tentwenty)
|
||||
// CHECK: 4294967295
|
||||
// CHECK: 15
|
||||
// CHECK: 1020
|
||||
|
||||
|
||||
// Test generic conversions from floating point
|
||||
|
||||
print(Int8._convert(from: -128))
|
||||
print(Int8._convert(from: 128))
|
||||
print(Int8._convert(from: -127))
|
||||
print(Int8._convert(from: 127))
|
||||
|
||||
// CHECK: (value: Optional(-128), exact: true)
|
||||
// CHECK: (value: nil, exact: false)
|
||||
// CHECK: (value: Optional(-127), exact: true)
|
||||
// CHECK: (value: Optional(127), exact: true)
|
||||
|
||||
print(Int8._convert(from: -129))
|
||||
print(Int8._convert(from: -128.999))
|
||||
print(Int8._convert(from: -128.001))
|
||||
print(Int8._convert(from: -127.999))
|
||||
print(Int8._convert(from: -127.001))
|
||||
print(Int8._convert(from: 127.001))
|
||||
print(Int8._convert(from: 127.999))
|
||||
|
||||
// CHECK: (value: nil, exact: false)
|
||||
// CHECK: (value: Optional(-128), exact: false)
|
||||
// CHECK: (value: Optional(-128), exact: false)
|
||||
// CHECK: (value: Optional(-127), exact: false)
|
||||
// CHECK: (value: Optional(-127), exact: false)
|
||||
// CHECK: (value: Optional(127), exact: false)
|
||||
// CHECK: (value: Optional(127), exact: false)
|
||||
|
||||
print(Int8._convert(from: 0))
|
||||
print(Int8._convert(from: -0.0))
|
||||
print(Int8._convert(from: 0.001))
|
||||
print(Int8._convert(from: -0.001))
|
||||
|
||||
// CHECK: (value: Optional(0), exact: true)
|
||||
// CHECK: (value: Optional(0), exact: true)
|
||||
// CHECK: (value: Optional(0), exact: false)
|
||||
// CHECK: (value: Optional(0), exact: false)
|
||||
|
||||
print(Int8._convert(from: Double.leastNonzeroMagnitude))
|
||||
print(Int8._convert(from: -Double.leastNonzeroMagnitude))
|
||||
print(Int8._convert(from: Double.leastNormalMagnitude))
|
||||
print(Int8._convert(from: -Double.leastNormalMagnitude))
|
||||
|
||||
// CHECK: (value: Optional(0), exact: false)
|
||||
// CHECK: (value: Optional(0), exact: false)
|
||||
// CHECK: (value: Optional(0), exact: false)
|
||||
// CHECK: (value: Optional(0), exact: false)
|
||||
|
||||
print(UInt8._convert(from: -1))
|
||||
print(UInt8._convert(from: 255))
|
||||
print(UInt8._convert(from: 256))
|
||||
print(UInt8._convert(from: Double.infinity))
|
||||
print(UInt8._convert(from: -Double.infinity))
|
||||
print(UInt8._convert(from: Double.nan))
|
||||
|
||||
// CHECK: (value: nil, exact: false)
|
||||
// CHECK: (value: Optional(255), exact: true)
|
||||
// CHECK: (value: nil, exact: false)
|
||||
// CHECK: (value: nil, exact: false)
|
||||
// CHECK: (value: nil, exact: false)
|
||||
// CHECK: (value: nil, exact: false)
|
||||
|
||||
let f = Float(Int64.min)
|
||||
let ff = Int64._convert(from: f)
|
||||
let fff = Int64(f)
|
||||
print(fff == ff.value!)
|
||||
// CHECK: true
|
||||
|
||||
let g = f.nextUp
|
||||
let gg = Int64._convert(from: g)
|
||||
let ggg = Int64(g)
|
||||
print(ggg == gg.value!)
|
||||
// CHECK: true
|
||||
|
||||
let h = Float(Int64.max)
|
||||
let hh = Int64._convert(from: h)
|
||||
print(hh)
|
||||
// CHECK: (value: nil, exact: false)
|
||||
|
||||
let i = h.nextDown
|
||||
let ii = Int64._convert(from: i)
|
||||
let iii = Int64(i)
|
||||
print(iii == ii.value!)
|
||||
// CHECK: true
|
||||
|
||||
Reference in New Issue
Block a user