Skip to content

SE-0080 (2/5) - Failable initializers for Float->Float #2977

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 2 commits into from
Jun 30, 2016

Conversation

ultramiraculous
Copy link
Contributor

What's in this pull request?

Breaking out from #2742. Adds init?(exactly:) initializers for float types from other float types.

Resolved bug number: (SR-1491)


Before merging this pull request to apple/swift repository:

  • Test pull request on Swift continuous integration.

Triggering Swift CI

The swift-ci is triggered by writing a comment on this PR addressed to the GitHub user @swift-ci. Different tests will run depending on the specific comment that you use. The currently available comments are:

Smoke Testing

Platform Comment
All supported platforms @swift-ci Please smoke test
All supported platforms @swift-ci Please smoke test and merge
OS X platform @swift-ci Please smoke test OS X platform
Linux platform @swift-ci Please smoke test Linux platform

Validation Testing

Platform Comment
All supported platforms @swift-ci Please test
All supported platforms @swift-ci Please test and merge
OS X platform @swift-ci Please test OS X platform
OS X platform @swift-ci Please benchmark
Linux platform @swift-ci Please test Linux platform

Lint Testing

Language Comment
Python @swift-ci Please Python lint

Note: Only members of the Apple organization can trigger swift-ci.

@ultramiraculous ultramiraculous changed the title Failable float float SE-0067 (2/5) - Failable initializers for Float->Float Jun 10, 2016
@gribozavr
Copy link
Contributor

@swift-ci Please test

#if !os(Windows) && (arch(i386) || arch(x86_64))
% end
% end

/// Construct an instance that approximates `other`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please restore the old comment for the case when Self is That (Create an instance initialized to ...).

@gribozavr
Copy link
Contributor

@stephentyrone Could you review, please?

I'm not 100% sure what is the desired behavior for NaN. I can see it argued both ways:

  • Since NaNs are not equal to each other, this initializer can't return any value that can possibly round-trip and compare equal to the original value. So FloatXX(exactly: NaN) should return nil.
  • Conversions between floating point formats should propagate NaNs if the target format supports them, so FloatXX(exactly: NaN) should return a NaN.

#if arch(i386) || arch(x86_64)
% end

% for OtherFloat, otherSignificandBits in floatNameToSignificandBits.iteritems():
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indentation of the second-level %if and %for is inconsistent, would you mind fixing it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Think I got this right. You're right, it was all over the place from moving things around over time.

@ultramiraculous
Copy link
Contributor Author

Yeah, so @gribozavr / @stephentyrone on the NaN deal: I went back and forth on it and meant to leave a note asking for guidance. It seemed like not propagating the NaN is the right move, but I got stuck going back and forth on it. Maybe it should propagate, like the -Inf/+Inf special cases? It's at the very least not the same case as truncation, where we're advertising that we'll return nil.

@ultramiraculous
Copy link
Contributor Author

(Also I guess the change I just made to srcBits <= bits is about to cause my local test run to fail, because the NaN case isn't going to be handled the same in every case...)

@stephentyrone
Copy link
Contributor

There are two competing considerations WRT how NaN is handled:

  • IEEE 754: "For an operation with quiet NaN inputs, other than maximum and minimum operations, if a floating-point result is to be delivered the result shall be a quiet NaN"
  • We would like the have the axiom that either result == source or result is nil.

General practice in floating-point is that it's OK to "lose" NaNs if the exceptional condition is tracked by some other means (here, returning nil). So formally, I think we can justify either behavior. There's not a huge performance difference either way (though preserving NaN will be slightly faster for concrete types on x86).

  • If we preserve NaNs, we preserve some information about what went wrong, which is usually good.
  • If we return nil for NaNs, we make it more likely that an exceptional case will be detected and handled promptly, which is also good.

I come down weakly in favor of returning nil for NaN, as I believe that it's slightly friendlier to inexperienced users, and experts can always get the other behavior with a one-line workaround.

if ${That}(self) != other {
return nil
}
% endif
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

endif vs end?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I've got that fixed locally. I was waiting on ideas on NaN handling before I pushed changes again.

/// Create an ${That} initialized to `value`, if `value` can be represented without rounding.
/// - note: This is effectively checking that `inputValue == outputValue`, meaning `NaN` inputs will always return `nil`.
@_transparent
public init?(exactly other: ${That}) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this look ok as a revision?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you change the function to be @inline(__always) instead of @_transparent?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To elaborate, @_transparent affects compile-time diagnostics, and I don't think we have any special diagnostics support in the compiler for this new initializer.

@gribozavr
Copy link
Contributor

@ultramiraculous Thank you! LGTM modulo comments. Let's run it in CI:

@swift-ci Please test

@gribozavr
Copy link
Contributor

@swift-ci Please test

}

/// Create an ${That} initialized to `value`, if `value` can be represented without rounding.
/// - note: This is effectively checking that `inputValue == outputValue`, meaning `NaN` inputs will always return `nil`.
Copy link
Contributor

@gribozavr gribozavr Jun 29, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should "an ${That}" be "a ${That}"? The substitutions are Float, Double, and Float80.

Also, could you wrap to 80 columns and add an empty line before "- note"?

@gribozavr
Copy link
Contributor

@ultramiraculous Thank you very much for working on this PR!

@ultramiraculous ultramiraculous force-pushed the failable-float-float branch 2 times, most recently from d5ed380 to e7a5e67 Compare June 29, 2016 22:30
@ultramiraculous ultramiraculous changed the title SE-0067 (2/5) - Failable initializers for Float->Float SE-0080 (2/5) - Failable initializers for Float->Float Jun 29, 2016
@gribozavr
Copy link
Contributor

@swift-ci Please test and merge

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants