[swift-evolution] [Proposal] Add floor() and ceiling() functions to FloatingPoint

Stephen Canon scanon at apple.com
Mon Jun 27 14:23:15 CDT 2016


> On Jun 27, 2016, at 12:34 PM, Karl <razielim at gmail.com> wrote:
> 
> 
>> On 27 Jun 2016, at 16:23, Stephen Canon <scanon at apple.com <mailto:scanon at apple.com>> wrote:
>> 
>> 
>>> On Jun 25, 2016, at 05:06, Karl via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>> 
>>>> Proposal: https://gist.github.com/karwa/273db66cd8a5fe2c388ccc7de9c4cf31 <https://gist.github.com/karwa/273db66cd8a5fe2c388ccc7de9c4cf31>
>> Karl, thanks for writing this up.  It should be extended to include not only floor( ) and ceiling( ), but also:
>> 
>> 	/// Returns the integral value closest to `self` whose magnitude is not greater than that of `self`.
>> 	func truncate( ) -> Self
>> 
>> 	/// Returns the integral value closest to `self`.  If two integrers are equally close, the even one
>> 	/// is returned.
>> 	//  NOTE: The name of this function requires bike-shedding.  I’ve chosen a deliberately poor
>> 	//  name as a straw-man.
>> 	func roundToNearestTiesToEven( ) -> Self
>> 
>> 	/// Returns the integral value closest to `self`.  If two integrers are equally close, the one with
>> 	/// greater magnitude is returned.
>> 	//  NOTE: The name of this function requires bike-shedding.  I’ve chosen a deliberately poor
>> 	//  name as a straw-man.
>> 	func roundToNearestTiesAway( ) -> Self
>> 
>> and mutating versions of those.
>> 
> 
> I was trying to add these, but working out the names of the mutating functions is difficult. How is truncate different to floor if it returns an integral value and can never round up?
> 
> Perhaps for the other functions, we could have a general `round` function with a tiebreak-enum parameter (it would be great if we could embed enums in protocols, but I’m not sure if that’s even on the roadmap):
> 
> enum FloatingPointRoundingStrategy {   // or something to that effect
>     case preferEven
>     case preferGreatest
> }
> 
> func rounded(inTiebreak: FloatingPointRoundingStrategy) -> Self
> 
> I think `(4.5).rounded(inTiebreak: .preferGreatest) == 5.0` looks quite nice.

Yes, something along these lines might work, though `FloatingPointRoundingStrategy` isn’t quite right; after all, round-towards-infinity (aka ceiling) is also a rounding strategy.

One option (which I don’t totally love, but which simplifies the API surface quite a bit, and avoids adding more formXXXX constructions) would be to fold all the rounding rules into a single member function (very strawman):

	/// Describes a rule for rounding to an integral value.
	enum RoundingRule {
		/// The result is the closest representable value greater than or equal to the source.
		case upwards
		/// The result is the closest representable value less than or equal to the source.
		case downwards
		/// The result is the closest representable value whose magnitude is less than or equal to that of the source.
		case towardZero
		/// The result is the closest representable value; if two values are equally close, the even one is chosen.
		case toNearestTiesToEven
		/// The result is the closest representable value; if two values are equally close, the one with greater magnitude is chosen.
		case toNearestTiesAway
	}

	/// Rounds to an integral value according to the specified rounding rule.
	mutating func round(_ rule: RoundingRule = toNearestTiesAway)

	func rounded(_ rule: RoundingRule = toNearestTiesAway) -> Self

That gives us e.g.:

	let x = -2.5
	x.round(.upwards)	// -2
	x.round(.downwards)	// -3
	x.round(.towardZero)	// -2
	x.round(.toNearestTiesToEven)	// -2
	x.round()	// -3

We might also provide free functions that implement the most common operations under the familiar libc names (I realize that free-functions are not broadly considered “swifty”, but they may be appropriate here; we would effectively simply be moving these free functions from Darwin/Glibc into the stdlib, and making them available for all FloatingPoint types).

	func ceil<T: FloatingPoint>(_ x: T) -> T {
		return x.rounded(.upwards)
	}

	func floor<T: FloatingPoint>(_ x: T) -> T {
		return x.rounded(.downwards)
	}

	func trunc<T: FloatingPoint>(_ x: T) -> T {
		return x.rounded(.towardZero)
	}


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160627/c0bdab18/attachment.html>


More information about the swift-evolution mailing list