[swift-evolution] [Pitch] Percentage Type
Xiaodi Wu
xiaodi.wu at gmail.com
Sat Jan 13 23:18:51 CST 2018
As Erica mentioned about Angle, this seems to be a perfect fit for an
appropriately focused third-party library, but I'm not sure I appreciate
why this should be part of the standard library. In large part, you seem to
have reinvented a decimal type, which is already available in the form of
Foundation.Decimal on all supported platforms.
On Sat, Jan 13, 2018 at 9:07 PM, Jonathan Hull via swift-evolution <
swift-evolution at swift.org> wrote:
> Here is the code I use for percentage myself. (I incorrectly said I use a
> UInt64… I use a UInt32):
>
> ///Represents a percentage with the precision of millionths of 1 (i.e. 4
> decimal places: XX.XXXX%). The value is always positive (or zero), but may
> be greater than 100%
> struct Percentage {
> fileprivate(set) var millionths:UInt32
>
>
> fileprivate init(storage:UInt32){
> millionths = storage
> }
>
>
> static var quarter = Percentage(storage: 250_000)
> static var half = Percentage(storage: 500_000)
> static var threeQuarters = Percentage(storage: 750_000)
> static var full = Percentage(storage: 1_000_000)
>
>
> init(millionths:Int) {
> self.millionths = UInt32(millionths)
> }
>
>
> init(_ double:Double, range:ClosedRange<Double> = 0...1) {
> if range == 0...1 {
> self.millionths = UInt32(max(double * 1_000_000, 0))
> }else if range == 0...100{
> self.millionths = UInt32(max(double * 10_000, 0))
> }else{
> self.millionths = UInt32(max((double - range.lowerBound
> )/(range.upperBound - range.lowerBound),0))
> }
> }
>
>
> init(_ num:Num, range:ClosedRange<Num> = 0...1) {
> if range == 0...1 {
> self.millionths = UInt32(max(num * 1_000_000, 0).integerValue)
> }else if range == 0...100{
> self.millionths = UInt32(max(num * 10_000, 0).integerValue)
> }else{
> self.millionths = UInt32(max((num - range.lowerBound)/(range.
> upperBound - range.lowerBound),0).integerValue)
> }
> }
>
>
> init(_ decimal:Decimal, range:ClosedRange<Decimal> = 0...1) {
> if range == 0...1 {
> self.millionths = NSDecimalNumber(decimal: max(decimal *
> 1_000_000, 0)).uint32Value
> }else if range == 0...100{
> self.millionths = NSDecimalNumber(decimal: max(decimal *
> 10_000, 0)).uint32Value
> }else{
> let shifted = max((decimal - range.lowerBound)/(range.upper
> Bound - range.lowerBound),0)
> self.millionths = NSDecimalNumber(decimal: shifted).
> uint32Value
> }
> }
>
>
> init(hundredths:Int) {
> self.millionths = UInt32(max(hundredths * 10_000, 0))
> }
>
> init(thousandths:Int) {
> self.millionths = UInt32(max(thousandths * 1_000, 0))
> }
>
>
> var isFull:Bool {
> return self.millionths >= 1_000_000
> }
>
>
> var doubleValue:Double{
> return Double(self.millionths)/1_000_000
> }
>
>
> var cgfloatValue:CGFloat{
> return CGFloat(self.millionths)/1_000_000
> }
>
>
> var decimalValue:Decimal {
> return Decimal(self.millionths)/1_000_000
> }
>
>
> var numValue:Num {
> return Num(numerator: Int32(self.millionths), denominator:
> 1_000_000)
> }
>
>
> var hundredths:Int {
> return Int(self.millionths/10_000)
> }
>
>
> var thousandths:Int {
> return Int(self.millionths/1_000)
> }
>
>
> var tenThousandths:Int {
> return Int(self.millionths/100)
> }
>
>
> func map(to range:ClosedRange<Num>) -> Num {
> return self.numValue * (range.upperBound - range.lowerBound) +
> range.lowerBound
> }
>
>
> mutating func clip() {
> if self.millionths > 1_000_000 {
> self.millionths = 1_000_000
> }
> }
>
>
> func clipped()->Percentage {
> if self.millionths > 1_000_000 {
> return Percentage.full
> }
> return self
> }
>
>
>
>
> }
>
> extension Percentage:CustomStringConvertible {
> var description: String {
> let num = self.numValue * 100
> if num.isInteger{
> return "\(num)%"
> }
> return "\(num.decimalValue)%"
> }
> }
>
> extension Percentage:ExpressibleByIntegerLiteral {
> init(integerLiteral value: IntegerLiteralType) {
> self.millionths = UInt32(max(value * 10_000, 0))
> }
> }
>
> extension Percentage:Hashable {
> var hashValue: Int {
> return self.millionths.hashValue
> }
>
>
> static func == (lhs:Percentage, rhs:Percentage)->Bool {
> return lhs.millionths == rhs.millionths
> }
> }
>
> extension Percentage:Comparable {
> static func < (lhs:Percentage, rhs:Percentage) -> Bool {
> return lhs.millionths < rhs.millionths
> }
> }
>
> extension Percentage {
> static func + (lhs:Percentage, rhs:Percentage)->Percentage {
> return Percentage(storage: lhs.millionths + rhs.millionths)
> }
>
>
> static func - (lhs:Percentage, rhs:Percentage)->Percentage {
> if rhs > lhs {return 0}
> return Percentage(storage: lhs.millionths - rhs.millionths)
> }
>
>
> static func * (lhs:Percentage, rhs:Double)->Double {
> return lhs.doubleValue * rhs
> }
>
>
> static func * (lhs:Double, rhs:Percentage)->Double {
> return lhs * rhs.doubleValue
> }
>
>
> static func * (lhs:Percentage, rhs:CGFloat)->CGFloat {
> return lhs.cgfloatValue * rhs
> }
>
>
> static func * (lhs:CGFloat, rhs:Percentage)->CGFloat {
> return lhs * rhs.cgfloatValue
> }
>
>
> static func * (lhs:Percentage, rhs:Num)->Num {
> return lhs.numValue * rhs
> }
>
>
> static func * (lhs:Num, rhs:Percentage)->Num {
> return lhs * rhs.numValue
> }
>
> static func * (lhs:Percentage, rhs:Percentage)->Percentage {
> return Percentage(lhs.decimalValue * rhs.decimalValue)
> }
>
>
> }
>
>
>
>
> On Jan 13, 2018, at 6:26 PM, Jonathan Hull via swift-evolution <
> swift-evolution at swift.org> wrote:
>
> Hi Evolution,
>
> I was wondering if we would consider adding a percentage type to Swift.
> This would be a type with a value between 0 & 1.
>
> I know we can and do use doubles or floats for this now, but there really
> is a semantic difference between most parameters that take a value between
> 0 & 1 and those that take any floating point value. It would be nice to
> have a type that semantically means that the value is from 0 to 1.
>
> It could even just wrap a Double for speed (in my own code I wrap a UInt64
> for decimal accuracy… and to avoid issues around comparisons).
>
> Thanks,
> Jon
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20180113/5597369b/attachment.html>
More information about the swift-evolution
mailing list