[Review] SE-0067: Enhanced Floating Point Protocols

[Proposal:
https://github.com/apple/swift-evolution/blob/master/proposals/0067-floating-point-protocols.md\]

This is super impressive. I do have several bits I’m uncomfortable with,
however. I’ll try to separate that into “semantic” and “naming” sections.

Semantic

static var radix: Int { get }

Does it ever make sense to have a model type that allows different instances
to have different radices?

No.

Is there an algorithm that makes use of a model’s radix, or is this just in
here for “completeness”?

If you know ulp, radix, and exponent range, you can infer basically all the
other numerical details of the type. One good example would be the constant
that Dave mentioned, “maxResultOfAdding1”. You can compute this if you know
radix and ulp. The radix is also the bound on how large the relative
spacing between consecutive numbers can get, which is sometimes important
for computing accurate bounds. These are all somewhat niche, but the
problem is that there’s no good way to get this value if you don’t have it,
and it imposes “zero” implementation burden.

  /// A signaling NaN (not-a-number).
  @warn_unused_result
  static func signalingNaN: Self { get }

I’m not sure it really makes sense for a Bignum / APFloat type to support
such a value. But really I think this is just underspecified. What does it
mean, in terms of this protocol and its uses, for a NaN to be signaling? Is
it just a specific “color" of NaN, with no semantic requirements other than
being distinguishable?

There are a variety of means that a softfloat type could use to implement
signaling NaNs. Here are two of the simpler ones:

(a) if running on HW with hard-float support, use native-precision
hard-float instructions to set flags as needed.
(b) provide operation variants that take an inout flags / parameter:

mutating func add(rhs: Self, inout flags: Flags)

(Also, is ‘signalingNan.isNan’ true? I assume so but since ’nan’ is implied
to be a non-signaling NaN I’m not sure anymore.)

Yup, that should be clarified.

  var signBit: Bool { get }

Unlike Chris, I’m strongly against this property as it stands. You should
not be able to write “if someValue.signBit”; a bit is not a boolean value.
(Citation: "Uses of Boolean methods and properties should read as assertions
about the receiver.”)

I’d be okay with Greg’s idea of changing the type to an enum. I’d also be
okay with renaming this to a predicate, whatever the name ends up being.
(“isSignBitSet”, “isSignNegative”, etc.)

Making it a predicate is weird, because then the three properties making up
the number become `isSignBitSet`, `exponent`, and `significand`; one of
these things is not like the other ones. If `signBit: Bool` were ruled out,
I would rather go with Greg’s enum proposal.

  var exponent: Int { get }

Nitpick: it’s probably worth noting in the doc comment that this is the
unbiased exponent value.

Also, does it matter that this is insufficient for bignums, which may have
an exponent of greater than `sizeof(Int.self)` bits? (This is also a concern
for a number of members of BinaryFloatingPoint, like ‘significantBitCount’.)

An exponent of Int.max encodes a number >= 2**Int.max. This is a
staggeringly huge quantity, even when Int is 32 bits (it’s approximately
1e646456992). There are a few extremely niche applications that require
numbers with greater magnitude, but they are *extremely* rare. To a good
approximation, `Int` is more than enough bits, and a reasonable tradeoff.

Naive question: is it necessary to make a trade-off here? Why not an
associated type Exponent that's Int for Float, Double, and Float80,
allowing for something else for bignums?

···

On Fri, Apr 22, 2016 at 9:13 AM, Stephen Canon via swift-evolution <swift-evolution@swift.org> wrote:

On Apr 21, 2016, at 9:13 PM, Jordan Rose <jordan_rose@apple.com> wrote:

Ditto `significandBitCount`. I haven’t seen usage of floating-point types
with more than a few thousand significand bits; billions of bits is enough.
It is plausible that one could build a type that runs into this limit on a
32-bit system, but it wouldn’t be very useful; on a 64-bit system, you can’t
allocate the storage for even one such value.

Naming

On “NaN” vs. “Nan”: I’m not convinced that ignoring the case is the right
way to go here. IMHO the clearest lowercase form is “nan” and the clearest
capitalized form is “NaN”.

The current draft API guidelines don’t cover this case, but if I were to add
something for this, I’d say “when a word is normally written with mixed
case, the lowercase form should be fully-lowercased if the first letter is
naturally uppercase, and the capitalized form should have the first letter
uppercased only.” That rule produces “iPhone/IPhone”, “next/NeXT”, and
“nan/NaN”. (The “if the first letter is naturally uppercase” could be thrown
out as well.)

Yup, this seems like a sensible rule to me.

On 'isLessThanOrEqual(to:)’: I agree with Xiaodi that the argument label is
problematic here. I think the problem is that we have two prepositions that
apply to the argument, and “pick the second one” leaves the base name
feeling unbalanced. (Remember that we allow referring to a method by its
basename alone when using it as a function value.)

On 'isTotallyOrdered(with:)’: I lost track of who said it, but I agree that
this sounds like it’s “!isUnordered(with: other)”. The only name that’s
coming to mind is ‘isTotallyOrderedBefore(_:)’, which isn’t great.

On ‘binade’: At first I thought this was a confusing term and there had to
be a better one, but now I think it’s an “if you don’t know what this is,
you don’t need to use it” case. :-)

Yup.

– Steve

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Two stray thoughts:

I agree with previous comments that `ulpOfOne` may not really be necessary.

Of the following--

  init(signBit: Bool, exponent: Int, significand: Self)
  init(magnitudeOf other: Self, signOf: Self)

--would it be more elegant to have the latter be `init(signOf: Self,
magnitudeOf other: Self)`?

···

On Fri, Apr 22, 2016 at 9:54 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Fri, Apr 22, 2016 at 9:13 AM, Stephen Canon via swift-evolution > <swift-evolution@swift.org> wrote:

On Apr 21, 2016, at 9:13 PM, Jordan Rose <jordan_rose@apple.com> wrote:

[Proposal:
https://github.com/apple/swift-evolution/blob/master/proposals/0067-floating-point-protocols.md\]

This is super impressive. I do have several bits I’m uncomfortable with,
however. I’ll try to separate that into “semantic” and “naming” sections.

Semantic

static var radix: Int { get }

Does it ever make sense to have a model type that allows different instances
to have different radices?

No.

Is there an algorithm that makes use of a model’s radix, or is this just in
here for “completeness”?

If you know ulp, radix, and exponent range, you can infer basically all the
other numerical details of the type. One good example would be the constant
that Dave mentioned, “maxResultOfAdding1”. You can compute this if you know
radix and ulp. The radix is also the bound on how large the relative
spacing between consecutive numbers can get, which is sometimes important
for computing accurate bounds. These are all somewhat niche, but the
problem is that there’s no good way to get this value if you don’t have it,
and it imposes “zero” implementation burden.

  /// A signaling NaN (not-a-number).
  @warn_unused_result
  static func signalingNaN: Self { get }

I’m not sure it really makes sense for a Bignum / APFloat type to support
such a value. But really I think this is just underspecified. What does it
mean, in terms of this protocol and its uses, for a NaN to be signaling? Is
it just a specific “color" of NaN, with no semantic requirements other than
being distinguishable?

There are a variety of means that a softfloat type could use to implement
signaling NaNs. Here are two of the simpler ones:

(a) if running on HW with hard-float support, use native-precision
hard-float instructions to set flags as needed.
(b) provide operation variants that take an inout flags / parameter:

mutating func add(rhs: Self, inout flags: Flags)

(Also, is ‘signalingNan.isNan’ true? I assume so but since ’nan’ is implied
to be a non-signaling NaN I’m not sure anymore.)

Yup, that should be clarified.

  var signBit: Bool { get }

Unlike Chris, I’m strongly against this property as it stands. You should
not be able to write “if someValue.signBit”; a bit is not a boolean value.
(Citation: "Uses of Boolean methods and properties should read as assertions
about the receiver.”)

I’d be okay with Greg’s idea of changing the type to an enum. I’d also be
okay with renaming this to a predicate, whatever the name ends up being.
(“isSignBitSet”, “isSignNegative”, etc.)

Making it a predicate is weird, because then the three properties making up
the number become `isSignBitSet`, `exponent`, and `significand`; one of
these things is not like the other ones. If `signBit: Bool` were ruled out,
I would rather go with Greg’s enum proposal.

  var exponent: Int { get }

Nitpick: it’s probably worth noting in the doc comment that this is the
unbiased exponent value.

Also, does it matter that this is insufficient for bignums, which may have
an exponent of greater than `sizeof(Int.self)` bits? (This is also a concern
for a number of members of BinaryFloatingPoint, like ‘significantBitCount’.)

An exponent of Int.max encodes a number >= 2**Int.max. This is a
staggeringly huge quantity, even when Int is 32 bits (it’s approximately
1e646456992). There are a few extremely niche applications that require
numbers with greater magnitude, but they are *extremely* rare. To a good
approximation, `Int` is more than enough bits, and a reasonable tradeoff.

Naive question: is it necessary to make a trade-off here? Why not an
associated type Exponent that's Int for Float, Double, and Float80,
allowing for something else for bignums?

Ditto `significandBitCount`. I haven’t seen usage of floating-point types
with more than a few thousand significand bits; billions of bits is enough.
It is plausible that one could build a type that runs into this limit on a
32-bit system, but it wouldn’t be very useful; on a 64-bit system, you can’t
allocate the storage for even one such value.

Naming

On “NaN” vs. “Nan”: I’m not convinced that ignoring the case is the right
way to go here. IMHO the clearest lowercase form is “nan” and the clearest
capitalized form is “NaN”.

The current draft API guidelines don’t cover this case, but if I were to add
something for this, I’d say “when a word is normally written with mixed
case, the lowercase form should be fully-lowercased if the first letter is
naturally uppercase, and the capitalized form should have the first letter
uppercased only.” That rule produces “iPhone/IPhone”, “next/NeXT”, and
“nan/NaN”. (The “if the first letter is naturally uppercase” could be thrown
out as well.)

Yup, this seems like a sensible rule to me.

On 'isLessThanOrEqual(to:)’: I agree with Xiaodi that the argument label is
problematic here. I think the problem is that we have two prepositions that
apply to the argument, and “pick the second one” leaves the base name
feeling unbalanced. (Remember that we allow referring to a method by its
basename alone when using it as a function value.)

On 'isTotallyOrdered(with:)’: I lost track of who said it, but I agree that
this sounds like it’s “!isUnordered(with: other)”. The only name that’s
coming to mind is ‘isTotallyOrderedBefore(_:)’, which isn’t great.

On ‘binade’: At first I thought this was a confusing term and there had to
be a better one, but now I think it’s an “if you don’t know what this is,
you don’t need to use it” case. :-)

Yup.

– Steve

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

One more stray thought:
Is there a reason RawSignificand is declared in FloatingPoint but used
only in BinaryFloatingPoint?

···

On Fri, Apr 22, 2016 at 10:02 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Two stray thoughts:

I agree with previous comments that `ulpOfOne` may not really be necessary.

Of the following--

  init(signBit: Bool, exponent: Int, significand: Self)
  init(magnitudeOf other: Self, signOf: Self)

--would it be more elegant to have the latter be `init(signOf: Self,
magnitudeOf other: Self)`?

On Fri, Apr 22, 2016 at 9:54 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Fri, Apr 22, 2016 at 9:13 AM, Stephen Canon via swift-evolution >> <swift-evolution@swift.org> wrote:

On Apr 21, 2016, at 9:13 PM, Jordan Rose <jordan_rose@apple.com> wrote:

[Proposal:
https://github.com/apple/swift-evolution/blob/master/proposals/0067-floating-point-protocols.md\]

This is super impressive. I do have several bits I’m uncomfortable with,
however. I’ll try to separate that into “semantic” and “naming” sections.

Semantic

static var radix: Int { get }

Does it ever make sense to have a model type that allows different instances
to have different radices?

No.

Is there an algorithm that makes use of a model’s radix, or is this just in
here for “completeness”?

If you know ulp, radix, and exponent range, you can infer basically all the
other numerical details of the type. One good example would be the constant
that Dave mentioned, “maxResultOfAdding1”. You can compute this if you know
radix and ulp. The radix is also the bound on how large the relative
spacing between consecutive numbers can get, which is sometimes important
for computing accurate bounds. These are all somewhat niche, but the
problem is that there’s no good way to get this value if you don’t have it,
and it imposes “zero” implementation burden.

  /// A signaling NaN (not-a-number).
  @warn_unused_result
  static func signalingNaN: Self { get }

I’m not sure it really makes sense for a Bignum / APFloat type to support
such a value. But really I think this is just underspecified. What does it
mean, in terms of this protocol and its uses, for a NaN to be signaling? Is
it just a specific “color" of NaN, with no semantic requirements other than
being distinguishable?

There are a variety of means that a softfloat type could use to implement
signaling NaNs. Here are two of the simpler ones:

(a) if running on HW with hard-float support, use native-precision
hard-float instructions to set flags as needed.
(b) provide operation variants that take an inout flags / parameter:

mutating func add(rhs: Self, inout flags: Flags)

(Also, is ‘signalingNan.isNan’ true? I assume so but since ’nan’ is implied
to be a non-signaling NaN I’m not sure anymore.)

Yup, that should be clarified.

  var signBit: Bool { get }

Unlike Chris, I’m strongly against this property as it stands. You should
not be able to write “if someValue.signBit”; a bit is not a boolean value.
(Citation: "Uses of Boolean methods and properties should read as assertions
about the receiver.”)

I’d be okay with Greg’s idea of changing the type to an enum. I’d also be
okay with renaming this to a predicate, whatever the name ends up being.
(“isSignBitSet”, “isSignNegative”, etc.)

Making it a predicate is weird, because then the three properties making up
the number become `isSignBitSet`, `exponent`, and `significand`; one of
these things is not like the other ones. If `signBit: Bool` were ruled out,
I would rather go with Greg’s enum proposal.

  var exponent: Int { get }

Nitpick: it’s probably worth noting in the doc comment that this is the
unbiased exponent value.

Also, does it matter that this is insufficient for bignums, which may have
an exponent of greater than `sizeof(Int.self)` bits? (This is also a concern
for a number of members of BinaryFloatingPoint, like ‘significantBitCount’.)

An exponent of Int.max encodes a number >= 2**Int.max. This is a
staggeringly huge quantity, even when Int is 32 bits (it’s approximately
1e646456992). There are a few extremely niche applications that require
numbers with greater magnitude, but they are *extremely* rare. To a good
approximation, `Int` is more than enough bits, and a reasonable tradeoff.

Naive question: is it necessary to make a trade-off here? Why not an
associated type Exponent that's Int for Float, Double, and Float80,
allowing for something else for bignums?

Ditto `significandBitCount`. I haven’t seen usage of floating-point types
with more than a few thousand significand bits; billions of bits is enough.
It is plausible that one could build a type that runs into this limit on a
32-bit system, but it wouldn’t be very useful; on a 64-bit system, you can’t
allocate the storage for even one such value.

Naming

On “NaN” vs. “Nan”: I’m not convinced that ignoring the case is the right
way to go here. IMHO the clearest lowercase form is “nan” and the clearest
capitalized form is “NaN”.

The current draft API guidelines don’t cover this case, but if I were to add
something for this, I’d say “when a word is normally written with mixed
case, the lowercase form should be fully-lowercased if the first letter is
naturally uppercase, and the capitalized form should have the first letter
uppercased only.” That rule produces “iPhone/IPhone”, “next/NeXT”, and
“nan/NaN”. (The “if the first letter is naturally uppercase” could be thrown
out as well.)

Yup, this seems like a sensible rule to me.

On 'isLessThanOrEqual(to:)’: I agree with Xiaodi that the argument label is
problematic here. I think the problem is that we have two prepositions that
apply to the argument, and “pick the second one” leaves the base name
feeling unbalanced. (Remember that we allow referring to a method by its
basename alone when using it as a function value.)

On 'isTotallyOrdered(with:)’: I lost track of who said it, but I agree that
this sounds like it’s “!isUnordered(with: other)”. The only name that’s
coming to mind is ‘isTotallyOrderedBefore(_:)’, which isn’t great.

On ‘binade’: At first I thought this was a confusing term and there had to
be a better one, but now I think it’s an “if you don’t know what this is,
you don’t need to use it” case. :-)

Yup.

– Steve

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

It’s an added (fairly minor) complexity to the API surface that confers approximately zero benefit.

– Steve

···

On Apr 22, 2016, at 10:54 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Naive question: is it necessary to make a trade-off here? Why not an
associated type Exponent that's Int for Float, Double, and Float80,
allowing for something else for bignums?

I’m confused in that I haven’t really had exposure to signaling NaNs before, and wasn’t really able to find hints on how they are used. Do math operations create signaling NaNs, or is it an explicitly set value?

My first expectation was that you would either have overflow operators where a NaN result would immediately raise an exception.

My second expectation if NaN was a valid value but use created exceptions is that floating point numbers are monadic. Specifically, floating point operations with soft NaN seem similar to optional, while Floats with signaling NaN seem similar to implicitly unwrapped optionals - but without the use of separate types to represent numbers and possibly-not-numbers.

That said, would use of an attribute on a floating point type, similar to what is planned for IUOs, be appropriate to indicate that the type should be treated as signaling?

-DW

···

On Apr 22, 2016, at 8:13 AM, Stephen Canon via swift-evolution <swift-evolution@swift.org> wrote:

  /// A signaling NaN (not-a-number).
  @warn_unused_result
  static func signalingNaN: Self { get }

I’m not sure it really makes sense for a Bignum / APFloat type to support such a value. But really I think this is just underspecified. What does it mean, in terms of this protocol and its uses, for a NaN to be signaling? Is it just a specific “color" of NaN, with no semantic requirements other than being distinguishable?

(-1)^sign * significand * radix^exponent

Significand is not like the other two, I guess, since nothing is raised to
it as a power...

···

On Fri, Apr 22, 2016 at 11:42 AM, Dave Abrahams via swift-evolution < swift-evolution@swift.org> wrote:

A floating point number is made up
of three parts, and one part *is* not like the other ones. Furthermore,
using an enum doesn't change that one (ahem) bit.

In what way is it different?

···

On Apr 22, 2016, at 12:42 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:

This argument makes no sense to me. A floating point number is made up
of three parts, and one part *is* not like the other ones.

Sorry, I think my question is more general than this. "Signaling" is a term of art in IEEE 754, but it doesn't actually mean anything in Swift, and none of the operations in the protocol have defined semantics for "signaling". Maybe that's the real question: what must each of the operations of FloatingPoint and BinaryFloatingPoint do when operating on an SNaN? a QNaN?

(I know what IEEE 754 says. We need the same rules in Swift semantics: "throws", "traps", "returns an unspecified result".)

Jordan

···

On Apr 22, 2016, at 7:13, Stephen Canon <scanon@apple.com> wrote:

  /// A signaling NaN (not-a-number).
  @warn_unused_result
  static func signalingNaN: Self { get }

I’m not sure it really makes sense for a Bignum / APFloat type to support such a value. But really I think this is just underspecified. What does it mean, in terms of this protocol and its uses, for a NaN to be signaling? Is it just a specific “color" of NaN, with no semantic requirements other than being distinguishable?

There are a variety of means that a softfloat type could use to implement signaling NaNs. Here are two of the simpler ones:

(a) if running on HW with hard-float support, use native-precision hard-float instructions to set flags as needed.
(b) provide operation variants that take an inout flags / parameter:

  mutating func add(rhs: Self, inout flags: Flags)

IIUC, what's being suggested is

  init<T: FloatingPoint>(_ x: T)

···

on Mon Apr 25 2016, Stephen Canon <swift-evolution@swift.org> wrote:

On Apr 23, 2016, at 8:53 PM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

A few things…

First, something absent: I'm a little bit concerned by the act that
there's no means to convert between different concrete FloatingPoint
types. Something like the IntMax mechanism in Swift 2's IntegerType
might be useful, though there may be good reasons not to do that.

There are concrete conversions between all built in float types.
Those aren’t going away. Are you saying that you want init(_ value:
Float) etc to be protocol requirements?

--
Dave

My vote is with using operators, and perhaps actually introducing `<=>` for
total order as suggested elsewhere. Why invent method names for standard
operators?

···

On Mon, Apr 25, 2016 at 1:32 PM, Stephen Canon via swift-evolution < swift-evolution@swift.org> wrote:

> On Apr 23, 2016, at 8:53 PM, Brent Royal-Gordon via swift-evolution < > swift-evolution@swift.org> wrote:
>> func isEqual(to other: Self) -> Bool
>> func isLess(than other: Self) -> Bool
>> func isLessThanOrEqual(to other: Self) -> Bool
>
> I'm still not sure why these are methods instead of operators.
>
> (I also share others' concern about the argument label on
`isLessThanOrEqual`.)

There’s lots of concern, and very few suggestions of anything better. The
only thing that seems workable is maybe `isLessThanOrEqualTo(_:)`, which is
inconsistent with the other comparisons, but maybe that’s the right thing
to do anyway.

> > func isEqual(to other: Self) ->Bool
> > func isLess(than other: Self) ->Bool
> > func isLessThanOrEqual(to other: Self) ->Bool
>
> I'm still not sure why these are methods instead of operators.

I think this is an *excellent* choice, and I hope it is the first step to
completely removing operators from protocols.

IMHO throwing operators into protocols is inconsistent and confusing.
Having regular methods and a single generic version of the operator that
calls down on the type’s methods is clearer and guarantees that generic
code can avoid ambiguities by calling the methods directly, instead of
having to rely only on heavily overloaded global operators.

I personally disagree on this point. To me, a protocol describes a set of
requirements for a type to fulfill, which includes things other than
methods. Just as a protocol can define initializers, properties, and
associated types that a type must define in order to conform, it makes
sense that a protocol would also define which operators a conforming type
must support.

Introducing a mapping between names and operators poses a few problems:

– IMO, they are overly verbose and add noise to the definition. This makes
the language look less clean (I'm getting visions of NSDecimalNumber).
– They expose two ways to accomplish the same thing (writing `x.isEqual(to:
y)` and `x == y`).
– Do certain operators automatically get mapped to method names with
appropriate signatures across all types, or does a conforming type still
have to provide that mapping by implementing the operators separately? If
it's the latter, that's extra work for the author of the type writing the
protocol. If it's the former, does it make sense to automatically push
these operators for all types? Should any type that has an `add` method
automatically get `+` as a synonym as well? That may not be desirable.

I'm very supportive of the floating-point protocol proposal in general, but
I feel the arithmetic and comparison operations should be exposed by
operators alone and not by methods, where there is a suitable operator that
has the intended meaning.

···

On Sun, Apr 24, 2016 at 2:57 AM Nicola Salmoria via swift-evolution < swift-evolution@swift.org> wrote:


Nicola

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Comments inline.

Hi Xiaodi —

Thanks for the detailed comments. Some thoughts inline.

* What is your evaluation of the proposal?

+1 in intent. Specifics require further refinement. For example:

Internal inconsistencies in capitalization:
* `signalingNaN` but `isSignalingNan` and `isNan`

This is a typo. Should be signalingNan.

Parameter labels, or whatever they're called now, do not reflect newly
adopted Swift syntax in SE-0046:
* `static func maximum(x: Self, _ y: Self) -> Self` should be `static
func maximum(_ x: Self, _ y: Self) -> Self`, etc.

Infelicitous use of prepositions to conform superficially to new
naming guidelines:
* `isEqual(to:)` is fine, but for consistency there's
`isLessThanOrEqual(to:)`, which is not fine, because the preposition
"to" applies only to "equal" and not to "less than”

Since `adding(_:)` is instead now an operator in the current version
of the proposal, could comparison functions also be operators only?

They could, but you still need isUnordered(with: ) and isTotallyOrdered(with: ), as they don’t have operator equivalents.

My two cents are that the comparison functions that have operators
should stick with those operators only instead of the names.
Stylistically, I think `isLessThanOrEqual(to:)` is less than
cromulent.

Some thoughts on `isUnordered(with:)` and `isTotallyOrdered(with:)`--

1. As you mention below, implementations are free to expose
IEEE754-mandated operations with names that differ from the
specification. Looking to Java and C# as examples of how other C-style
languages implement floating point types, C-style languages have taken
a free hand in how they deal with comparison with NaN.

So long as we're not going down the road of having methods that expose
distinct unordered-quiet and unordered-signaling comparison predicates
as defined in IEEE754 (do any C-style languages do that?), there's
probably some freedom to determine whether `isUnordered(with:)` is
necessary as a standalone method or whether it's already sufficiently
provided for by other methods. In the end, `x.isUnordered(with: y)` is
really equivalent to `x.isNan || y.isNan`, which can be immediately
grokked by any reader of code.

2. `isTotallyOrdered(with:)` isn't quite apt as a name. As I
understand it, totalOrder(x, y) "reflects" a total ordering such that,
when x is ordered "below" y, the return value is true. (Both quoted
words reflect the terminology used in the specification.) So perhaps
`isTotallyOrdered(below:)` or even just `isOrdered(below:)` would be
more apt. Certainly `with` is not the correct label for the parameter,
as any two values that don't compare unordered should be "totally
ordered with" each other. This is especially problematic if we're
going to keep `isUnordered(with:)`, since those two methods would
suggest a complementary logical relationship that just isn't there.

Yes, if we drop isUnordered(with:), then renaming this isOrdered(below:) would work. I’m OK with this approach.

Incorrect nomenclature in an attempt to correct previously misleading
nomenclature:
* `leastMagnitude` should be `leastPositive` or `minPositive`, because
magnitudes can be zero: it's bonkers that
`Double.minimumMagnitude(0.0, Double.leastMagnitude) <
Double.leastMagnitude`!
(Likewise, `leastNormalMagnitude` should be `leastNormalPositive` or
`minPositive`, and `greatestFiniteMagnitude` should be
`greatestFinite` or `maxFinite`)

Inconsistencies with Integer protocols (at least, as they are currently):
* properties named "least..." or "greatest..." are inconsistent with
conceptually similar properties such as `Int.min` and `Int.max`

`min` and `max` were deliberately avoided based on a discussion with the Apple standard library team; these properties don’t really behave like the integer bounds properties, so naming them similarly may be confusing.

I agree absolutely that `min` and `max`, standalone, are misleading
and shouldn't be used. That said, I would argue that `maxFinite` is
demonstrably the correct name because it's the most succinct one
that's both accurate and consistent with other usage. Adding
"magnitude" clarifies nothing here and in fact made me do a
double-take on first reading: I had to think for a split second
whether there exists a greater floating point value that's a finite
non-magnitude: it's of course absurd, but you have to think about it
the first time. Meanwhile, `greatestFinite` means exactly the same
thing as `maxFinite`, but now you're introducing a different word
where it's not necessary to clarify the meaning or differentiate from
standalone `max`.

My biggest concern with `maxFinite` is that it would seem to also require a `minFinite`, which would then be easily confused with e.g. DBL_MIN (which means something radically different), and also implies that they might not be symmetric after all. To my ear, “magnitude” more readily suggests the existing symmetry: namely that I can get the finite bounds as +/-.greatestFiniteMagnitude.

FWIW, 754 actually explicitly describes least[Positive]Magnitude as “the positive number of least magnitude” (the definition of nextUp, 5.3.1).

Your point about magnitudes being non-zero is reasonable, but I think you’ve taken it a step to far; it could be corrected by simply changing `leastMagnitude` to either `leastPositiveMagnitude` or `leastNonzeroMagnitude`.

By the same reasoning here, `minPositive` (instead of
`leastMagnitude`) is equally accurate, more consistent with usage
elsewhere, and probably more accessible to non-native English
speakers. Throughout the IEEE specification, "magnitude" is used in
relation to absolute values, which is not really in play here.

In any case, we agree that `leastMagnitude` must at minimum be renamed
to exclude zero.

`leastNormalMagnitude` and `greatestFiniteMagnitude` are accurate as is. (`minPositive`, on the other hand, would be exceedingly misleading). Can you expand on why you want to change them? It seems like you simply prefer “positive” to “magnitude”?

I made a typo here (I blame jetlag): `minPositiveNormal` was what I
meant to type. Again, my rationale is that it is the least deviation
from `min` that accurately describes what's going on using the
simplest words. Here, the distinction between normal and subnormal is
unavoidable, but we don't need to talk about magnitudes, and there
really is no difference between "min" and "least”.

One is a word, the other is an abbreviation. The swift guidelines, for better or worse, counsel us to “avoid abbreviations”. The fact that “min" is a term of art makes it plausible, but I don’t think we’re desperate to save two characters in the name.

Also potentially useful (actually, definitely useful in implementing
floating point strides) would be the properties `maxExactInteger`
(and, I suppose, a corresponding `minExactInteger`).

An early draft of the protocol had these. Naming this property is *hard*, because every floating-point value larger than `maxExactInteger` is … an exact integer. If you want to be unambiguously precise, you end up with something horrible. Ultimately I punted on this issue, but I would definitely support adding it in the future if an appropriate name can be found, or if a compelling use case arises (I don’t think it’s actually needed for implementing strides).

Use of one term-of-art (`ulp`), as Swift naming guidelines allow and
even encourage, but failure to use more widely understood terms of
art:
* `squareRoot()` should be `sqrt()`
* something really ought to be done about
`truncatingRemainder(dividingBy:)`--the fact that the comments tell
the reader that `truncatingRemainder(dividingBy:)` is equivalent to C
`fmod` suggests to me that `fmod` may be a widely understood
term-of-art
I argue strongly that Swift's naming guidelines about terms-of-art
should be understood to encourage terms used widely in other languages
for basic mathematical functions instead of written-out English
equivalents--e.g. `asinh` instead of`inverseHyperbolicSine`. Looking
to other C-style languages, all seem to accept these shorter terms
as-is without writing them out.

sqrt( ) I could support, but fmod( ) is an absolutely terrible name for a relatively rarely-used function. If there were a good term-of-art, I would want to use it, but AFAIK there isn’t.

I should note that the free functions sqrt( ) and fmod( ) won’t go away with this proposal. They will continue to be supplied by the math overlay for Float, Double, CGFloat, just not for the FloatingPoint protocol. So why do we need them in the FloatingPoint protocol at all?

The squareRoot( ) and remainder( ) methods are distinct from most of the other <math.h> functions in that IEEE 754 considers them to be "basic operations” as defined by clause 5 of the standard (IEEE 754 spells out the name “squareRoot” FWIW, though there’s no requirement that we follow that).

Even IEEE754 is interestingly inconsistent here. Throughout, it's
written as "squareRoot", but when it comes to "recommended" functions,
the reciprocal of the square root is written "rSqrt" (see table 9.1).
I'd highly recommend setting a good example of when things are
terms-of-art by going with "sqrt”.

Clause 9, being non-normative, didn't receive nearly as much editorial attention.

While we're on the topic of IEEE754 section 5 functions, we're missing
abs(x) in the protocol.

abs() would have come from SignedArithmetic, but you’re right that it’s now missing and should be added to FloatingPoint.

And speaking of absolute value functions, IEEE754 calls it "minNumMag"
and "maxNumMag" when comparing two values but, when it comes to
summing, recommends a function called "sumAbs". A missed opportunity
for consistency, IMO. When implementing in Swift, then, was there a
rationale for having `minimumMagnitude` instead of the much shorter
but equally grokkable `minAbs` (abs being, IMO, a term-of-art like
sqrt)?

maxAbs is unclear (to me); I could easily imagine that it returns max(abs(x), abs(y)). Given that these methods have no precedent in C-family languages, there isn’t an actual term of art to follow, and the meaning of maxAbs isn’t particularly obvious.

And then speaking of implementations of IEEE minNum and maxNum--are
those static methods necessary in the protocol? What is gained over
having just the comparison operators?

While one can write these operations in terms of the comparison operators, it’s a bit unwieldy to do so, and requires some heroics from the optimizer to turn such an expanded definition into e.g. the arm64 FMAXNM instruction or the AVX-512 VRANGE instruction. It’s somewhat easier to wire up those optimizations this way.

Similarly, having these methods directly available is a useful optimization hook for user-defined types that conform to the protocol (though the protocol *will* provide default implementations, so you’re not obligated to do so).

Because of this it makes sense to require them as methods in the FloatingPoint protocol, rather than only as free functions. [truncatingRemainder( ) is not required by IEEE 754, but it doesn’t impose a significant implementation burden and eases the transition for folks currently using operator %.]

I'm not sure I buy the "ease" argument. Surely, if folks are using
operator %, it's equivalently difficult to transition either to the
free function or to `truncatingRemainder()`? Suppose you deprecate %
and recommend fmod(), then just leave `truncatingRemainder()` out of
the protocol. After all, the rationale for deprecation is that people
aren't using it correctly...

fmod() wraps the C stdlib fmod(), so only exists for types supported by the C stdlib. The truncatingRemainder() method is available for all FloatingPoint types (these two sets of types are equivalent for Swift today, but if someone wants to write a library type that conforms to FloatingPoint, it would provide truncatingRemainder).

···

On Apr 20, 2016, at 12:04 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Wed, Apr 20, 2016 at 5:38 AM, Stephen Canon <scanon@apple.com> wrote:

On Apr 19, 2016, at 6:34 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:

I'm saying something else. The preposition that goes with "less" is "than,"
while "to" goes only with "equal." By making "to" a parameter label you've got
{less than or equal} to, which is inelegant because the label cannot be
distributed to both parts--i.e. one cannot say "less than to or equal to."

Put another way, I could just as well rewrite the method as `equalToOrLess
(than:)`. Now, the parameter is labeled "than" instead of "to," yet the
parameter serves the same purpose. Thus, I argue that the proposed method name
may meet the letter of the Swift guidelines but is awkward.

I see, thanks.

···

on Wed Apr 20 2016, Xiaodi Wu <xiaodi.wu-AT-gmail.com> wrote:

On Wed, Apr 20, 2016 at 14:44 Dave Abrahams via swift-evolution > <swift-evolution@swift.org> wrote:

    on Tue Apr 19 2016, Xiaodi Wu <swift-evolution@swift.org> wrote:

    > * What is your evaluation of the proposal?
    >
    > +1 in intent. Specifics require further refinement. For example:
    >
    > Internal inconsistencies in capitalization:
    > * `signalingNaN` but `isSignalingNan` and `isNan`
    >
    > Parameter labels, or whatever they're called now, do not reflect newly
    > adopted Swift syntax in SE-0046:
    > * `static func maximum(x: Self, _ y: Self) -> Self` should be `static
    > func maximum(_ x: Self, _ y: Self) -> Self`, etc.
    >
    > Infelicitous use of prepositions to conform superficially to new
    > naming guidelines:
    > * `isEqual(to:)` is fine, but for consistency there's
    > `isLessThanOrEqual(to:)`, which is not fine, because the preposition
    > "to" applies only to "equal" and not to "less than"

    That seems like a huge stretch to me. Are you claiming it's wrong to
    say “x is less than or equal to y,” or are you saying something else?

    --
    Dave

    _______________________________________________
    swift-evolution mailing list
    swift-evolution@swift.org
    https://lists.swift.org/mailman/listinfo/swift-evolution

--
Dave

FWIW, you can avoid the multiple-preposition issue it differently:

- isLessThanOrEqual(to:) => isNotGreaterThan(_:), isAtOrBelow(_:), etc.

…neither of which I can claim to really like, but such possibilities exist.

Offering in case there's a better phrasing along similar lines.

···

On Apr 20, 2016, at 3:08 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:

I'm saying something else. The preposition that goes with "less" is "than," while "to" goes only with "equal." By making "to" a parameter label you've got {less than or equal} to, which is inelegant because the label cannot be distributed to both parts--i.e. one cannot say "less than to or equal to."

Put another way, I could just as well rewrite the method as `equalToOrLess(than:)`. Now, the parameter is labeled "than" instead of "to," yet the parameter serves the same purpose. Thus, I argue that the proposed method name may meet the letter of the Swift guidelines but is awkward.

On Wed, Apr 20, 2016 at 14:44 Dave Abrahams via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

on Tue Apr 19 2016, Xiaodi Wu <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

> * What is your evaluation of the proposal?
>
> +1 in intent. Specifics require further refinement. For example:
>
> Internal inconsistencies in capitalization:
> * `signalingNaN` but `isSignalingNan` and `isNan`
>
> Parameter labels, or whatever they're called now, do not reflect newly
> adopted Swift syntax in SE-0046:
> * `static func maximum(x: Self, _ y: Self) -> Self` should be `static
> func maximum(_ x: Self, _ y: Self) -> Self`, etc.
>
> Infelicitous use of prepositions to conform superficially to new
> naming guidelines:
> * `isEqual(to:)` is fine, but for consistency there's
> `isLessThanOrEqual(to:)`, which is not fine, because the preposition
> "to" applies only to "equal" and not to "less than"

That seems like a huge stretch to me. Are you claiming it's wrong to
say “x is less than or equal to y,” or are you saying something else?

--
Dave

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Less than or equal to is not the same as not greater than, because two
unordered values are not less than or equal to and also not greater than.

···

On Wed, Apr 20, 2016 at 15:51 plx via swift-evolution < swift-evolution@swift.org> wrote:

FWIW, you can avoid the multiple-preposition issue it differently:

- isLessThanOrEqual(to:) => isNotGreaterThan(_:), isAtOrBelow(_:), etc.

…neither of which I can claim to really like, but such possibilities exist.

Offering in case there's a better phrasing along similar lines.

On Apr 20, 2016, at 3:08 PM, Xiaodi Wu via swift-evolution < > swift-evolution@swift.org> wrote:

I'm saying something else. The preposition that goes with "less" is
"than," while "to" goes only with "equal." By making "to" a parameter label
you've got {less than or equal} to, which is inelegant because the label
cannot be distributed to both parts--i.e. one cannot say "less than to or
equal to."

Put another way, I could just as well rewrite the method as
`equalToOrLess(than:)`. Now, the parameter is labeled "than" instead of
"to," yet the parameter serves the same purpose. Thus, I argue that the
proposed method name may meet the letter of the Swift guidelines but is
awkward.

On Wed, Apr 20, 2016 at 14:44 Dave Abrahams via swift-evolution < > swift-evolution@swift.org> wrote:

on Tue Apr 19 2016, Xiaodi Wu <swift-evolution@swift.org> wrote:

> * What is your evaluation of the proposal?
>
> +1 in intent. Specifics require further refinement. For example:
>
> Internal inconsistencies in capitalization:
> * `signalingNaN` but `isSignalingNan` and `isNan`
>
> Parameter labels, or whatever they're called now, do not reflect newly
> adopted Swift syntax in SE-0046:
> * `static func maximum(x: Self, _ y: Self) -> Self` should be `static
> func maximum(_ x: Self, _ y: Self) -> Self`, etc.
>
> Infelicitous use of prepositions to conform superficially to new
> naming guidelines:
> * `isEqual(to:)` is fine, but for consistency there's
> `isLessThanOrEqual(to:)`, which is not fine, because the preposition
> "to" applies only to "equal" and not to "less than"

That seems like a huge stretch to me. Are you claiming it's wrong to
say “x is less than or equal to y,” or are you saying something else?

--
Dave

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Naive question: is it necessary to make a trade-off here? Why not an
associated type Exponent that's Int for Float, Double, and Float80,
allowing for something else for bignums?

It’s an added (fairly minor) complexity to the API surface that confers
approximately zero benefit.

Fair. I know it's silly, but it feels a little inelegant that for a
binary floating point type both `exponent` and `exponentBitCount` are
of type Int.

···

On Fri, Apr 22, 2016 at 9:56 AM, Stephen Canon <scanon@apple.com> wrote:

On Apr 22, 2016, at 10:54 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

– Steve

Naive question: is it necessary to make a trade-off here? Why not an
associated type Exponent that's Int for Float, Double, and Float80,
allowing for something else for bignums?

It’s an added (fairly minor) complexity to the API surface that confers
approximately zero benefit.

Alternatively, could `exponent` could be of type Self, just as `significand` is?

···

On Fri, Apr 22, 2016 at 9:56 AM, Stephen Canon <scanon@apple.com> wrote:

On Apr 22, 2016, at 10:54 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

– Steve

So you admit the benefit isn’t *actually* zero? :-)

Yes, it does add a small bit of complexity to the API’s surface, but it's on a part of the surface that I don’t think people look at very much. How often are people extracting a float’s exponent? Plus, I suspect there’s a fair bit of overlap between the group of people who even know what `.exponent` would get used for, and those who’d get a warm fuzzy feeling from seeing that the standard library has baked-in, low-level support for bignum / arbitrary precision types.

Embrace the warm fuzzies… make exponent be an associated type.

- Dave Sweeris (who, despite the light-hearted tone, thinks this is actually pretty important)

···

On Apr 22, 2016, at 9:56 AM, Stephen Canon via swift-evolution <swift-evolution@swift.org> wrote:

On Apr 22, 2016, at 10:54 AM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:

Naive question: is it necessary to make a trade-off here? Why not an
associated type Exponent that's Int for Float, Double, and Float80,
allowing for something else for bignums?

It’s an added (fairly minor) complexity to the API surface that confers approximately zero benefit.

It was originally used for nan static function payload argument, but as that function has been removed from the FloatingPoint protocol, the associatedtype could also be moved down.

– Steve

···

On Apr 22, 2016, at 11:16 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

One more stray thought:
Is there a reason RawSignificand is declared in FloatingPoint but used
only in BinaryFloatingPoint?

On Fri, Apr 22, 2016 at 10:02 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Two stray thoughts:

I agree with previous comments that `ulpOfOne` may not really be necessary.

Of the following--

 init(signBit: Bool, exponent: Int, significand: Self)
 init(magnitudeOf other: Self, signOf: Self)

--would it be more elegant to have the latter be `init(signOf: Self,
magnitudeOf other: Self)`?

On Fri, Apr 22, 2016 at 9:54 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Fri, Apr 22, 2016 at 9:13 AM, Stephen Canon via swift-evolution >>> <swift-evolution@swift.org> wrote:

On Apr 21, 2016, at 9:13 PM, Jordan Rose <jordan_rose@apple.com> wrote:

[Proposal:
https://github.com/apple/swift-evolution/blob/master/proposals/0067-floating-point-protocols.md\]

This is super impressive. I do have several bits I’m uncomfortable with,
however. I’ll try to separate that into “semantic” and “naming” sections.

Semantic

static var radix: Int { get }

Does it ever make sense to have a model type that allows different instances
to have different radices?

No.

Is there an algorithm that makes use of a model’s radix, or is this just in
here for “completeness”?

If you know ulp, radix, and exponent range, you can infer basically all the
other numerical details of the type. One good example would be the constant
that Dave mentioned, “maxResultOfAdding1”. You can compute this if you know
radix and ulp. The radix is also the bound on how large the relative
spacing between consecutive numbers can get, which is sometimes important
for computing accurate bounds. These are all somewhat niche, but the
problem is that there’s no good way to get this value if you don’t have it,
and it imposes “zero” implementation burden.

/// A signaling NaN (not-a-number).
@warn_unused_result
static func signalingNaN: Self { get }

I’m not sure it really makes sense for a Bignum / APFloat type to support
such a value. But really I think this is just underspecified. What does it
mean, in terms of this protocol and its uses, for a NaN to be signaling? Is
it just a specific “color" of NaN, with no semantic requirements other than
being distinguishable?

There are a variety of means that a softfloat type could use to implement
signaling NaNs. Here are two of the simpler ones:

(a) if running on HW with hard-float support, use native-precision
hard-float instructions to set flags as needed.
(b) provide operation variants that take an inout flags / parameter:

mutating func add(rhs: Self, inout flags: Flags)

(Also, is ‘signalingNan.isNan’ true? I assume so but since ’nan’ is implied
to be a non-signaling NaN I’m not sure anymore.)

Yup, that should be clarified.

var signBit: Bool { get }

Unlike Chris, I’m strongly against this property as it stands. You should
not be able to write “if someValue.signBit”; a bit is not a boolean value.
(Citation: "Uses of Boolean methods and properties should read as assertions
about the receiver.”)

I’d be okay with Greg’s idea of changing the type to an enum. I’d also be
okay with renaming this to a predicate, whatever the name ends up being.
(“isSignBitSet”, “isSignNegative”, etc.)

Making it a predicate is weird, because then the three properties making up
the number become `isSignBitSet`, `exponent`, and `significand`; one of
these things is not like the other ones. If `signBit: Bool` were ruled out,
I would rather go with Greg’s enum proposal.

var exponent: Int { get }

Nitpick: it’s probably worth noting in the doc comment that this is the
unbiased exponent value.

Also, does it matter that this is insufficient for bignums, which may have
an exponent of greater than `sizeof(Int.self)` bits? (This is also a concern
for a number of members of BinaryFloatingPoint, like ‘significantBitCount’.)

An exponent of Int.max encodes a number >= 2**Int.max. This is a
staggeringly huge quantity, even when Int is 32 bits (it’s approximately
1e646456992). There are a few extremely niche applications that require
numbers with greater magnitude, but they are *extremely* rare. To a good
approximation, `Int` is more than enough bits, and a reasonable tradeoff.

Naive question: is it necessary to make a trade-off here? Why not an
associated type Exponent that's Int for Float, Double, and Float80,
allowing for something else for bignums?

Ditto `significandBitCount`. I haven’t seen usage of floating-point types
with more than a few thousand significand bits; billions of bits is enough.
It is plausible that one could build a type that runs into this limit on a
32-bit system, but it wouldn’t be very useful; on a 64-bit system, you can’t
allocate the storage for even one such value.

Naming

On “NaN” vs. “Nan”: I’m not convinced that ignoring the case is the right
way to go here. IMHO the clearest lowercase form is “nan” and the clearest
capitalized form is “NaN”.

The current draft API guidelines don’t cover this case, but if I were to add
something for this, I’d say “when a word is normally written with mixed
case, the lowercase form should be fully-lowercased if the first letter is
naturally uppercase, and the capitalized form should have the first letter
uppercased only.” That rule produces “iPhone/IPhone”, “next/NeXT”, and
“nan/NaN”. (The “if the first letter is naturally uppercase” could be thrown
out as well.)

Yup, this seems like a sensible rule to me.

On 'isLessThanOrEqual(to:)’: I agree with Xiaodi that the argument label is
problematic here. I think the problem is that we have two prepositions that
apply to the argument, and “pick the second one” leaves the base name
feeling unbalanced. (Remember that we allow referring to a method by its
basename alone when using it as a function value.)

On 'isTotallyOrdered(with:)’: I lost track of who said it, but I agree that
this sounds like it’s “!isUnordered(with: other)”. The only name that’s
coming to mind is ‘isTotallyOrderedBefore(_:)’, which isn’t great.

On ‘binade’: At first I thought this was a confusing term and there had to
be a better one, but now I think it’s an “if you don’t know what this is,
you don’t need to use it” case. :-)

Yup.

– Steve

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

IEEE 754 allows this (“If logBFormat is a floating-point format, then the following operations are homogeneous), but IMO this makes the property more awkward to use in practice.

– Steve

···

On Apr 22, 2016, at 11:26 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Fri, Apr 22, 2016 at 9:56 AM, Stephen Canon <scanon@apple.com> wrote:

On Apr 22, 2016, at 10:54 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Naive question: is it necessary to make a trade-off here? Why not an
associated type Exponent that's Int for Float, Double, and Float80,
allowing for something else for bignums?

It’s an added (fairly minor) complexity to the API surface that confers
approximately zero benefit.

Alternatively, could `exponent` could be of type Self, just as `significand` is?