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
|
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
|
@_transparent
|
||||||
public func signum() -> Self {
|
public func signum() -> Self {
|
||||||
return (self > (0 as Self) ? 1 : 0) - (self < (0 as Self) ? 1 : 0)
|
return (self > (0 as Self) ? 1 : 0) - (self < (0 as Self) ? 1 : 0)
|
||||||
@@ -2290,6 +2271,93 @@ ${operatorComment(x.nonMaskingOperator, True)}
|
|||||||
}
|
}
|
||||||
|
|
||||||
extension FixedWidthInteger {
|
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
|
/// Creates a new instance with the representable value that's closest to the
|
||||||
/// given integer.
|
/// given integer.
|
||||||
///
|
///
|
||||||
@@ -2311,7 +2379,7 @@ extension FixedWidthInteger {
|
|||||||
///
|
///
|
||||||
/// - Parameter source: An integer to convert to this type.
|
/// - Parameter source: An integer to convert to this type.
|
||||||
@_semantics("optimize.sil.specialize.generic.partial.never")
|
@_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) {
|
if _slowPath(source < Self.min) {
|
||||||
self = Self.min
|
self = Self.min
|
||||||
}
|
}
|
||||||
@@ -3219,47 +3287,6 @@ ${assignmentOperatorComment(x.operator, True)}
|
|||||||
}
|
}
|
||||||
%# end of concrete type: ${Self}
|
%# 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 {
|
extension ${Self} : Hashable {
|
||||||
/// The integer's hash value.
|
/// The integer's hash value.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -40,3 +40,89 @@ print(tentwenty)
|
|||||||
// CHECK: 4294967295
|
// CHECK: 4294967295
|
||||||
// CHECK: 15
|
// CHECK: 15
|
||||||
// CHECK: 1020
|
// 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