On Sat, Jan 13, 2018 at 9:07 PM, Jonathan Hull via swift-evolution < swift-evolution@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@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@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