DecimalFloatingPoint Protocol Review

In another post, @scanon outlined some basic requirements before we can define fixed-width Decimal numbers:

There is now a preliminary DecimalFloatingPoint protocol that could be reviewed by anyone who is interested:

public protocol DecimalFloatingPoint:FloatingPoint, ExpressibleByFloatLiteral {
  
  /// A type that represents the encoded significand of a value.
  associatedtype RawSignificand: UnsignedInteger
  
  /// A type that represents the encoded exponent of a value.
  associatedtype RawExponent : UnsignedInteger
  
  /// Creates a new instance from the specified sign and bit patterns.
  ///
  /// The values passed as `exponentBitPattern` is interpreted in the
  /// decimal interchange format defined by the [IEEE 754 specification][spec].
  ///
  /// [spec]: http://ieeexplore.ieee.org/servlet/opac?punumber=4610933
  ///
  /// The `significandBitPattern` are the big-endian, integer decimal digits
  /// of the number.  For example, the integer number `314` represents a
  /// significand of `314`.
  ///
  /// - Parameters:
  ///   - sign: The sign of the new value.
  ///   - exponentBitPattern: The bit pattern to use for the exponent field of
  ///     the new value.
  ///   - significandBitPattern: Bit pattern to use for the mantissa field of
  ///     the new value.
  init(sign: FloatingPointSign, exponentBitPattern: RawExponent,
       significandBitPattern: RawSignificand)
  
  /// Initialize from raw Binary Integer Decimal (BID) or
  /// Densely Packed Decimal (DPD) encoded integers.
  /// - Parameters:
  ///   - bits: The bit pattern to use for the new value.
  ///   - bidEncoding: When `true`, the `bits` are assumed to have a BID
  ///     encoding; otherwise, a DPD encoding is assumed, which is translated
  ///     to BID format during initialization.
  init(bitPattern bits: RawSignificand, bidEncoding: Bool)
  
  /// Creates a new instance from the given value, rounded to the closest
  /// possible representation.
  ///
  /// If two representable values are equally close, the result is the value
  /// with more trailing zeros in its significand bit pattern.
  ///
  /// - Parameter value: A floating-point value to be converted.
  init<Source:DecimalFloatingPoint>(_ value: Source)
  
  /// Creates a new instance from the given value, if it can be represented
  /// exactly.
  ///
  /// If the given floating-point value cannot be represented exactly, the
  /// result is `nil`. A value that is NaN ("not a number") cannot be
  /// represented exactly if its payload cannot be encoded exactly.
  ///
  /// - Parameter value: A floating-point value to be converted.
  init?<Source:DecimalFloatingPoint>(exactly value: Source)
  
  /// A decimal floating-point type's `exponentBias` imposes a limit on the
  /// range of the exponent for normal, finite values. The unbiased exponent of
  /// a type `F` can be obtained with this attribute. The unbiased exponent
  /// can be calculated as:
  ///         `exponent = exponentBitPattern - exponentBias`
  static var exponentBias: Int { get }
  
  /// The number of bits used to represent the type's exponent.
  static var exponentBitCount: Int { get }
  
  /// The raw encoding of the value's exponent field.
  ///
  /// This value is unadjusted by the type's exponent bias.
  var exponentBitPattern: Self.RawExponent { get }
  
  /// The raw encoding of the value's significand field.
  var significandBitPattern: RawSignificand { get }
  
  /// The floating-point value with the same sign and exponent as this value,
  /// but with a significand of 1.0.
  ///
  /// A *decade* is a set of decimal floating-point values that all have the
  /// same sign and exponent. The `decade` property is a member of the same
  /// decade as this value, but with a unit significand.
  ///
  /// In this example, `x` has a value of `21.5`, which is stored as
  /// `2.15 * 10**1`, where `**` is exponentiation. Therefore, `x.decade` is
  /// equal to `1.0 * 10**1`, or `10.0`.
  ///```
  /// let x = 21.5
  /// // x.significand == 2.15
  /// // x.exponent == 1
  ///
  /// let y = x.decade
  /// // y == 10.0
  /// // y.significand == 1.0
  /// // y.exponent == 1
  ///```
  var decade: Self { get }
  
  /// True if the internal number encoding is binary integer decimal or BID.
  /// The alternative allowed by the IEEE standard is densely packed decimal
  /// or DPD.
  ///
  /// The initializer `init(bitPattern:bidEncoding)` works from both DPD and
  /// BID. Converters to both formats are also available.
  static var isBIDFormat: Bool { get }
  
  // FIXME: - Discussion on how to do rounding and state tracking
  /// Class-wide setting for how numbers will be rounded in calculations.
  static var rounding: FloatingPointRoundingRule { get set }
  
  /// The number of digits required to represent the value's significand.
  ///
  /// If this value is a finite nonzero number, `significandDigitCount` is the
  /// number of decimal digits required to represent the value of
  /// `significand`; otherwise, `significandDigitCount` is -1. The value of
  /// `significandDigitCount` is always -1 or from one to the
  /// `significandMaxDigitCount`. For example:
  ///
  /// - For any representable power of ten, `significandDigitCount` is one,
  ///   because significand` is `1`.
  /// - If `x` is 10, `x.significand` is `10` in decimal, so
  ///   `x.significandDigitCount` is 2.
  /// - If `x` is Decimal32.pi, `x.significand` is `3.141593` in
  ///   decimal, and `x.significandDigitCount` is 7.
  var significandDigitCount: Int { get }
  
}
3 Likes