Can anyone tell me how to round a double value to x number of decimal places in Swift?
I have:
var totalWorkTimeInHours = (totalWorkTime/60/60)
With totalWorkTime being an NSTimeInterval (double) in second.
totalWorkTimeInHours will give me the hours, but it gives me the amount of time in such a long precise number e.g. 1.543240952039......
How do I round this down to, say, 1.543 when I print totalWorkTimeInHours?
You can use Swift's round function to accomplish this.
To round a Double with 3 digits precision, first multiply it by 1000, round it and divide the rounded result by 1000:
let x = 1.23556789
let y = Double(round(1000 * x) / 1000)
print(y) /// 1.236
Unlike any kind of printf(...) or String(format: ...) solutions, the result of this operation is still of type Double.
EDIT:
Regarding the comments that it sometimes does not work, please read this: What Every Programmer Should Know About Floating-Point Arithmetic
Extension for Swift 2
A more general solution is the following extension, which works with Swift 2 & iOS 9:
extension Double {
/// Rounds the double to decimal places value
func roundToPlaces(places:Int) -> Double {
let divisor = pow(10.0, Double(places))
return round(self * divisor) / divisor
}
}
Extension for Swift 3
In Swift 3 round is replaced by rounded:
extension Double {
/// Rounds the double to decimal places value
func rounded(toPlaces places:Int) -> Double {
let divisor = pow(10.0, Double(places))
return (self * divisor).rounded() / divisor
}
}
Example which returns Double rounded to 4 decimal places:
let x = Double(0.123456789).roundToPlaces(4) // x becomes 0.1235 under Swift 2
let x = Double(0.123456789).rounded(toPlaces: 4) // Swift 3 version
How do I round this down to, say, 1.543 when I print totalWorkTimeInHours?
To round totalWorkTimeInHours to 3 digits for printing, use the String constructor which takes a format string:
print(String(format: "%.3f", totalWorkTimeInHours))
With Swift 5, according to your needs, you can choose one of the 9 following styles in order to have a rounded result from a Double.
#1. Using FloatingPoint rounded() method
In the simplest case, you may use the Double rounded() method.
let roundedValue1 = (0.6844 * 1000).rounded() / 1000
let roundedValue2 = (0.6849 * 1000).rounded() / 1000
print(roundedValue1) // returns 0.684
print(roundedValue2) // returns 0.685
#2. Using FloatingPoint rounded(_:) method
let roundedValue1 = (0.6844 * 1000).rounded(.toNearestOrEven) / 1000
let roundedValue2 = (0.6849 * 1000).rounded(.toNearestOrEven) / 1000
print(roundedValue1) // returns 0.684
print(roundedValue2) // returns 0.685
#3. Using Darwin round function
Foundation offers a round function via Darwin.
import Foundation
let roundedValue1 = round(0.6844 * 1000) / 1000
let roundedValue2 = round(0.6849 * 1000) / 1000
print(roundedValue1) // returns 0.684
print(roundedValue2) // returns 0.685
#4. Using a Double extension custom method built with Darwin round and pow functions
If you want to repeat the previous operation many times, refactoring your code can be a good idea.
import Foundation
extension Double {
func roundToDecimal(_ fractionDigits: Int) -> Double {
let multiplier = pow(10, Double(fractionDigits))
return Darwin.round(self * multiplier) / multiplier
}
}
let roundedValue1 = 0.6844.roundToDecimal(3)
let roundedValue2 = 0.6849.roundToDecimal(3)
print(roundedValue1) // returns 0.684
print(roundedValue2) // returns 0.685
#5. Using NSDecimalNumber rounding(accordingToBehavior:) method
If needed, NSDecimalNumber offers a verbose but powerful solution for rounding decimal numbers.
import Foundation
let scale: Int16 = 3
let behavior = NSDecimalNumberHandler(roundingMode: .plain, scale: scale, raiseOnExactness: false, raiseOnOverflow: false, raiseOnUnderflow: false, raiseOnDivideByZero: true)
let roundedValue1 = NSDecimalNumber(value: 0.6844).rounding(accordingToBehavior: behavior)
let roundedValue2 = NSDecimalNumber(value: 0.6849).rounding(accordingToBehavior: behavior)
print(roundedValue1) // returns 0.684
print(roundedValue2) // returns 0.685
#6. Using NSDecimalRound(_:_:_:_:) function
import Foundation
let scale = 3
var value1 = Decimal(0.6844)
var value2 = Decimal(0.6849)
var roundedValue1 = Decimal()
var roundedValue2 = Decimal()
NSDecimalRound(&roundedValue1, &value1, scale, NSDecimalNumber.RoundingMode.plain)
NSDecimalRound(&roundedValue2, &value2, scale, NSDecimalNumber.RoundingMode.plain)
print(roundedValue1) // returns 0.684
print(roundedValue2) // returns 0.685
#7. Using NSString init(format:arguments:) initializer
If you want to return a NSString from your rounding operation, using NSString initializer is a simple but efficient solution.
import Foundation
let roundedValue1 = NSString(format: "%.3f", 0.6844)
let roundedValue2 = NSString(format: "%.3f", 0.6849)
print(roundedValue1) // prints 0.684
print(roundedValue2) // prints 0.685
#8. Using String init(format:_:) initializer
Swift’s String type is bridged with Foundation’s NSString class. Therefore, you can use the following code in order to return a String from your rounding operation:
import Foundation
let roundedValue1 = String(format: "%.3f", 0.6844)
let roundedValue2 = String(format: "%.3f", 0.6849)
print(roundedValue1) // prints 0.684
print(roundedValue2) // prints 0.685
#9. Using NumberFormatter
If you expect to get a String? from your rounding operation, NumberFormatter offers a highly customizable solution.
import Foundation
let formatter = NumberFormatter()
formatter.numberStyle = NumberFormatter.Style.decimal
formatter.roundingMode = NumberFormatter.RoundingMode.halfUp
formatter.maximumFractionDigits = 3
let roundedValue1 = formatter.string(from: 0.6844)
let roundedValue2 = formatter.string(from: 0.6849)
print(String(describing: roundedValue1)) // prints Optional("0.684")
print(String(describing: roundedValue2)) // prints Optional("0.685")
In Swift 5.5 and Xcode 13.2:
let pi: Double = 3.14159265358979
String(format:"%.2f", pi)
Example:
PS.: It still the same since Swift 2.0 and Xcode 7.2
This is a fully worked code
Swift 3.0/4.0/5.0 , Xcode 9.0 GM/9.2 and above
let doubleValue : Double = 123.32565254455
self.lblValue.text = String(format:"%.f", doubleValue)
print(self.lblValue.text)
output - 123
let doubleValue : Double = 123.32565254455
self.lblValue_1.text = String(format:"%.1f", doubleValue)
print(self.lblValue_1.text)
output - 123.3
let doubleValue : Double = 123.32565254455
self.lblValue_2.text = String(format:"%.2f", doubleValue)
print(self.lblValue_2.text)
output - 123.33
let doubleValue : Double = 123.32565254455
self.lblValue_3.text = String(format:"%.3f", doubleValue)
print(self.lblValue_3.text)
output - 123.326
Building on Yogi's answer, here's a Swift function that does the job:
func roundToPlaces(value:Double, places:Int) -> Double {
let divisor = pow(10.0, Double(places))
return round(value * divisor) / divisor
}
In Swift 3.0 and Xcode 8.0:
extension Double {
func roundTo(places: Int) -> Double {
let divisor = pow(10.0, Double(places))
return (self * divisor).rounded() / divisor
}
}
Use this extension like so:
let doubleValue = 3.567
let roundedValue = doubleValue.roundTo(places: 2)
print(roundedValue) // prints 3.56
Swift 4, Xcode 10
yourLabel.text = String(format:"%.2f", yourDecimalValue)
The code for specific digits after decimals is:
var a = 1.543240952039
var roundedString = String(format: "%.3f", a)
Here the %.3f tells the swift to make this number rounded to 3 decimal places.and if you want double number, you may use this code:
// String to Double
var roundedString = Double(String(format: "%.3f", b))
Use the built in Foundation Darwin library
SWIFT 3
extension Double {
func round(to places: Int) -> Double {
let divisor = pow(10.0, Double(places))
return Darwin.round(self * divisor) / divisor
}
}
Usage:
let number:Double = 12.987654321
print(number.round(to: 3))
Outputs: 12.988
If you want to round Double values, you might want to use Swift Decimal so you don't introduce any errors that can crop up when trying to math with these rounded values. If you use Decimal, it can accurately represent decimal values of that rounded floating point value.
So you can do:
extension Double {
/// Convert `Double` to `Decimal`, rounding it to `scale` decimal places.
///
/// - Parameters:
/// - scale: How many decimal places to round to. Defaults to `0`.
/// - mode: The preferred rounding mode. Defaults to `.plain`.
/// - Returns: The rounded `Decimal` value.
func roundedDecimal(to scale: Int = 0, mode: NSDecimalNumber.RoundingMode = .plain) -> Decimal {
var decimalValue = Decimal(self)
var result = Decimal()
NSDecimalRound(&result, &decimalValue, scale, mode)
return result
}
}
Then, you can get the rounded Decimal value like so:
let foo = 427.3000000002
let value = foo.roundedDecimal(to: 2) // results in 427.30
And if you want to display it with a specified number of decimal places (as well as localize the string for the user's current locale), you can use a NumberFormatter:
let formatter = NumberFormatter()
formatter.maximumFractionDigits = 2
formatter.minimumFractionDigits = 2
if let string = formatter.string(for: value) {
print(string)
}
A handy way can be the use of extension of type Double
extension Double {
var roundTo2f: Double {return Double(round(100 *self)/100) }
var roundTo3f: Double {return Double(round(1000*self)/1000) }
}
Usage:
let regularPie: Double = 3.14159
var smallerPie: Double = regularPie.roundTo3f // results 3.142
var smallestPie: Double = regularPie.roundTo2f // results 3.14
This is a sort of a long workaround, which may come in handy if your needs are a little more complex. You can use a number formatter in Swift.
let numberFormatter: NSNumberFormatter = {
let nf = NSNumberFormatter()
nf.numberStyle = .DecimalStyle
nf.minimumFractionDigits = 0
nf.maximumFractionDigits = 1
return nf
}()
Suppose your variable you want to print is
var printVar = 3.567
This will make sure it is returned in the desired format:
numberFormatter.StringFromNumber(printVar)
The result here will thus be "3.6" (rounded). While this is not the most economic solution, I give it because the OP mentioned printing (in which case a String is not undesirable), and because this class allows for multiple parameters to be set.
Either:
Using String(format:):
Typecast Double to String with %.3f format specifier and then back to Double
Double(String(format: "%.3f", 10.123546789))!
Or extend Double to handle N-Decimal places:
extension Double {
func rounded(toDecimalPlaces n: Int) -> Double {
return Double(String(format: "%.\(n)f", self))!
}
}
By calculation
multiply with 10^3, round it and then divide by 10^3...
(1000 * 10.123546789).rounded()/1000
Or extend Double to handle N-Decimal places:
extension Double {
func rounded(toDecimalPlaces n: Int) -> Double {
let multiplier = pow(10, Double(n))
return (multiplier * self).rounded()/multiplier
}
}
I would use
print(String(format: "%.3f", totalWorkTimeInHours))
and change .3f to any number of decimal numbers you need
This is more flexible algorithm of rounding to N significant digits
Swift 3 solution
extension Double {
// Rounds the double to 'places' significant digits
func roundTo(places:Int) -> Double {
guard self != 0.0 else {
return 0
}
let divisor = pow(10.0, Double(places) - ceil(log10(fabs(self))))
return (self * divisor).rounded() / divisor
}
}
// Double(0.123456789).roundTo(places: 2) = 0.12
// Double(1.23456789).roundTo(places: 2) = 1.2
// Double(1234.56789).roundTo(places: 2) = 1200
The best way to format a double property is to use the Apple predefined methods.
mutating func round(_ rule: FloatingPointRoundingRule)
FloatingPointRoundingRule is a enum which has following possibilities
Enumeration Cases:
case awayFromZero
Round to the closest allowed value whose magnitude is greater than or equal to that of the source.
case down
Round to the closest allowed value that is less than or equal to the source.
case toNearestOrAwayFromZero
Round to the closest allowed value; if two values are equally close, the one with greater magnitude is chosen.
case toNearestOrEven
Round to the closest allowed value; if two values are equally close, the even one is chosen.
case towardZero
Round to the closest allowed value whose magnitude is less than or equal to that of the source.
case up
Round to the closest allowed value that is greater than or equal to the source.
var aNumber : Double = 5.2
aNumber.rounded(.up) // 6.0
round a double value to x number of decimal
NO. of digits after decimal
var x = 1.5657676754
var y = (x*10000).rounded()/10000
print(y) // 1.5658
var x = 1.5657676754
var y = (x*100).rounded()/100
print(y) // 1.57
var x = 1.5657676754
var y = (x*10).rounded()/10
print(y) // 1.6
For ease to use, I created an extension:
extension Double {
var threeDigits: Double {
return (self * 1000).rounded(.toNearestOrEven) / 1000
}
var twoDigits: Double {
return (self * 100).rounded(.toNearestOrEven) / 100
}
var oneDigit: Double {
return (self * 10).rounded(.toNearestOrEven) / 10
}
}
var myDouble = 0.12345
print(myDouble.threeDigits)
print(myDouble.twoDigits)
print(myDouble.oneDigit)
The print results are:
0.123
0.12
0.1
Thanks for the inspiration of other answers!
Not Swift but I'm sure you get the idea.
pow10np = pow(10,num_places);
val = round(val*pow10np) / pow10np;
Swift 5
using String method
var yourDouble = 3.12345
//to round this to 2 decimal spaces i could turn it into string
let roundingString = String(format: "%.2f", myDouble)
let roundedDouble = Double(roundingString) //and than back to double
// result is 3.12
but it's more accepted to use extension
extension Double {
func round(to decimalPlaces: Int) -> Double {
let precisionNumber = pow(10,Double(decimalPlaces))
var n = self // self is a current value of the Double that you will round
n = n * precisionNumber
n.round()
n = n / precisionNumber
return n
}
}
and then you can use:
yourDouble.round(to:2)
This seems to work in Swift 5.
Quite surprised there isn't a standard function for this already.
//Truncation of Double to n-decimal places with rounding
extension Double {
func truncate(to places: Int) -> Double {
return Double(Int((pow(10, Double(places)) * self).rounded())) / pow(10, Double(places))
}
}
To avoid Float imperfections use Decimal
extension Float {
func rounded(rule: NSDecimalNumber.RoundingMode, scale: Int) -> Float {
var result: Decimal = 0
var decimalSelf = NSNumber(value: self).decimalValue
NSDecimalRound(&result, &decimalSelf, scale, rule)
return (result as NSNumber).floatValue
}
}
ex.
1075.58 rounds to 1075.57 when using Float with scale: 2 and .down
1075.58 rounds to 1075.58 when using Decimal with scale: 2 and .down
var n = 123.111222333
n = Double(Int(n * 10.0)) / 10.0
Result: n = 123.1
Change 10.0 (1 decimal place) to any of 100.0 (2 decimal place), 1000.0 (3 decimal place) and so on, for the number of digits you want after decimal..
The solution worked for me. XCode 13.3.1 & Swift 5
extension Double {
func rounded(decimalPoint: Int) -> Double {
let power = pow(10, Double(decimalPoint))
return (self * power).rounded() / power
}
}
Test:
print(-87.7183123123.rounded(decimalPoint: 3))
print(-87.7188123123.rounded(decimalPoint: 3))
print(-87.7128123123.rounded(decimalPoint: 3))
Result:
-87.718
-87.719
-87.713
I found this wondering if it is possible to correct a user's input. That is if they enter three decimals instead of two for a dollar amount. Say 1.111 instead of 1.11 can you fix it by rounding? The answer for many reasons is no! With money anything over i.e. 0.001 would eventually cause problems in a real checkbook.
Here is a function to check the users input for too many values after the period. But which will allow 1., 1.1 and 1.11.
It is assumed that the value has already been checked for successful conversion from a String to a Double.
//func need to be where transactionAmount.text is in scope
func checkDoublesForOnlyTwoDecimalsOrLess()->Bool{
var theTransactionCharacterMinusThree: Character = "A"
var theTransactionCharacterMinusTwo: Character = "A"
var theTransactionCharacterMinusOne: Character = "A"
var result = false
var periodCharacter:Character = "."
var myCopyString = transactionAmount.text!
if myCopyString.containsString(".") {
if( myCopyString.characters.count >= 3){
theTransactionCharacterMinusThree = myCopyString[myCopyString.endIndex.advancedBy(-3)]
}
if( myCopyString.characters.count >= 2){
theTransactionCharacterMinusTwo = myCopyString[myCopyString.endIndex.advancedBy(-2)]
}
if( myCopyString.characters.count > 1){
theTransactionCharacterMinusOne = myCopyString[myCopyString.endIndex.advancedBy(-1)]
}
if theTransactionCharacterMinusThree == periodCharacter {
result = true
}
if theTransactionCharacterMinusTwo == periodCharacter {
result = true
}
if theTransactionCharacterMinusOne == periodCharacter {
result = true
}
}else {
//if there is no period and it is a valid double it is good
result = true
}
return result
}
You can add this extension :
extension Double {
var clean: String {
return self.truncatingRemainder(dividingBy: 1) == 0 ? String(format: "%.0f", self) : String(format: "%.2f", self)
}
}
and call it like this :
let ex: Double = 10.123546789
print(ex.clean) // 10.12
Here's one for SwiftUI if you need a Text element with the number value.
struct RoundedDigitText : View {
let digits : Int
let number : Double
var body : some View {
Text(String(format: "%.\(digits)f", number))
}
}
//find the distance between two points
let coordinateSource = CLLocation(latitude: 30.7717625, longitude:76.5741449 )
let coordinateDestination = CLLocation(latitude: 29.9810859, longitude: 76.5663599)
let distanceInMeters = coordinateSource.distance(from: coordinateDestination)
let valueInKms = distanceInMeters/1000
let preciseValueUptoThreeDigit = Double(round(1000*valueInKms)/1000)
self.lblTotalDistance.text = "Distance is : \(preciseValueUptoThreeDigit) kms"
I'm not sure if there is an issue or not, so i'm just gonna write it down.
I'm developing using swift, xcode 7.2 , on iphone 5s.
And calculating execution time using
NSDate.timeIntervalSinceReferenceDate()
I created 2 arrays, one with 200,000 elements and one with 20.
and try to have random access to their elements. accessing elements on big one is almost 55 times slower! i know its bigger but isn't this O(1) ?
I also tried the same on java and the accessing speed is the same for big and small array.
From CFArrayheader in apple documentation, i found this:
Accessing any value at a particular index in an array is at worst O(log n), but should usually be O(1).
but it think this cant be true based on the numbers i've tested.
I know i didn't make a big test or anything special, but the fact that its not working is really messing with my head!
i kinda need this for what i'm working on. and the algorithm is not working on swift and iOS and its working on java and android.
let bigSize:Int = 200000
var bigArray = [Int](count:bigSize,repeatedValue:0)
let smallSize:Int = 20
var smallArray = [Int](count:smallSize,repeatedValue:0)
for i in 0..<bigSize
{
bigArray[i] = i + 8 * i
}
for i in 0..<smallSize
{
smallArray[i] = i + 9 * i
}
let indexBig = Int(arc4random_uniform(UInt32(bigSize)) % UInt32(bigSize))
let indexSmall = Int(arc4random_uniform(UInt32(smallSize)) % UInt32(smallSize))
var a = NSDate.timeIntervalSinceReferenceDate()
print(bigArray[indexBig])
var b = NSDate.timeIntervalSinceReferenceDate()
print(b-a) \\prints 0.000888049602508545
a = NSDate.timeIntervalSinceReferenceDate()
print(smallArray[indexSmall])
b = NSDate.timeIntervalSinceReferenceDate()
print(b-a) \\prints 6.90221786499023e-05
java :
(accessing one element is so fast on java and its on pc, so i access more elements, but same number on both arrays)
int bigSize = 200000;
int[] bigArray = new int[bigSize];
Random rand = new Random();
int smallSize = 20;
int[] smallArray = new int[smallSize];
for(int i = 0;i < bigSize;i++)
bigArray[i] = i + i * 8;
for(int i = 0;i < smallSize;i++)
smallArray[i] = i + i * 8;
int smallIndex = rand.nextInt(smallSize);
int bigIndex = rand.nextInt(bigSize);
int sum = 0;
long a = System.currentTimeMillis();
for(int i = 0;i < 10000;i++)
{
sum += bigArray[rand.nextInt(bigSize)];
}
System.out.println(sum);
long b = System.currentTimeMillis();
System.out.println(b-a); //prints 2
a = System.currentTimeMillis();
sum = 0;
for(int i = 0; i < 10000;i++)
{
sum += smallArray[rand.nextInt(smallSize)];
}
System.out.println(sum);
b = System.currentTimeMillis();
System.out.println(b - a); //prints 1
If you change the order of your two tests, you'll find that the performance is flipped. In short, the first test runs more slowly than the second one, regardless of whether it's the small array or the big one. This is a result of some dynamics of print. If you do a print before you perform the tests, the delay resulting from the first print is eliminated.
A better way to test this would be to create a unit test, which (a) repeats the subscript operator many times; and (b) uses measureBlock to repeat the test a few times to check for standard deviation and the like.
When I do that, I find the access time is indistinguishable, consistent with O(1). This were my unit tests:
let bigSize: Int = 200_000
let smallSize: Int = 20
func testBigArrayPerformance() {
let size = bigSize
let array = Array(0 ..< size).map { $0 + 8 * $0 }
var value = 0
measureBlock {
let baseIndex = Int(arc4random_uniform(UInt32(size)))
for index in 0 ..< 1_000_000 {
value += array[(baseIndex + index) % size]
}
}
print(value)
print(array.count)
}
func testSmallArrayPerformance() {
let size = smallSize
let array = Array(0 ..< size).map { $0 + 8 * $0 }
var value = 0
measureBlock {
let baseIndex = Int(arc4random_uniform(UInt32(size)))
for index in 0 ..< 1_000_000 {
value += array[(baseIndex + index) % size]
}
}
print(value)
print(array.count)
}
Admittedly, I've added some mathematical operations that change the index (my intent was to make sure the compiler didn't do some radical optimization that removed my attempt to repeat the subscript operation), and the overhead of that mathematical operation will dilute the subscript operator performance difference. But, even when I simplified the index operator, the performance between the two renditions was indistinguishable.
The question is related to calculating an increase in currency.
Loop over this n times, and let's say you start with $50k and your multiplier is 2. Something like b * 2 + a
This is the correct result:
$50,000.00
$100,000.00
$250,000.00
$600,000.00
$1,450,000.00
$3,500,000.00
$8,450,000.00
$20,400,000.00
$49,250,000.00
So just to be clear, the question is about efficiency in swift, not simply how to calculate this. Are there any handy data structures that would make this faster? Basically I was just looping through how many years (n) adding 2 (200%) and incrementing a couple temp variables to keep track of the current and previous values. It feels like there has got to be a much better way of handling this.
$50k base
$50k * 2 + 0 (previous value) = $100k
$100k * 2 + $50k = $250k
$250k * 2 + $100k = $600k
etc.
Code:
let baseAmount = 50000.0
let percentReturn = 200.0
let years = 10
// Calc decimal of percent.
var out: Double = 0.0
var previous: Double = 0.0
let returnPercent = percentReturn * 0.01
// Create tmp array to store values.
var tmpArray = [Double]()
// Loop through years.
for var index = 0; index < years; ++index
{
if index == 0
{
out = baseAmount
tmpArray.append(baseAmount)
}
else if index == 1
{
out = (out * returnPercent)
tmpArray.append(out)
previous = baseAmount
}
else
{
let tmp = (tmpArray.last! * returnPercent) + previous
previous = tmpArray.last!
tmpArray.append(tmp)
}
}
println(tmpArray)
Here are some ideas for improving efficiency:
Initialize your array to the appropriate size (it isn't dynamic; it is always the number of years)
Remove special cases (year 0 and 1 calculations) from the for-loop
Code:
func calculate(baseAmount: Double, percentReturn: Double, years: Int) -> [Double] {
// I prefer to return an empty array instead of nil
// so that you don't have to check for nil later
if years < 1 {
return [Double]()
}
let percentReturnAsDecimal = percentReturn * 0.01
// You know the size of the array, no need to append
var result = [Double](count: years, repeatedValue: 0.0)
result[0] = baseAmount
// No need to do this in the loop
if years > 1 {
result[1] = baseAmount * percentReturnAsDecimal
}
// Loop through years 2+
for year in 2 ..< years {
let lastYear = result[year - 1]
let yearBeforeLast = result[year - 2]
result[year] = (lastYear * percentReturnAsDecimal) + yearBeforeLast
}
return result
}
Efficiency in terms of speed I found this to be the fastest implementation of your algorithm:
let baseAmount = 50000.0
let returnPercent = 2.0
let years = 10
// you know the size of the array so you don't have to append to it and just use the subscript which is much faster
var array = [Double](count: years, repeatedValue: 0)
var previousValue = 0.0
var currentValue = baseAmount
for i in 0..<years {
array[i] = currentValue
let p2 = currentValue
currentValue = currentValue * returnPercent + previousValue
previousValue = p2
}
print(array)
Trying to figure out the 'correct' way to round down decimal numbers in Swift and struggling to set up the C calls correctly (or something) as it is returning a weird result. Here's a snippet from Playground:
import Foundation
func roundTo2(result: UnsafePointer<Double>, number: UnsafePointer<Double>) {
var resultCOP = COpaquePointer(result)
var numberCOP = COpaquePointer(number)
NSDecimalRound(resultCOP, numberCOP, 2, .RoundDown)
}
var from: Double = 1.54762
var to: Double = 0.0
roundTo2(&to, &from)
println("From: \(from), to: \(to)")
Output -> From: 1.54762, to: 1.54761981964356
I was hoping for 1.54. Any pointers would be appreciated.
The rounding process should be pretty straightforward without any wrappers. All we should do -- just call the function NSDecimalRound(_:_:_:_:), described there: https://developer.apple.com/documentation/foundation/1412204-nsdecimalround
import Cocoa
/// For example let's take any value with multiple decimals like this:
var amount: NSDecimalNumber = NSDecimalNumber(value: 453.585879834)
/// The mutable pointer reserves only "one cell" in memory for the
let uMPtr = UnsafeMutablePointer<Decimal>.allocate(capacity: 1)
/// Connect the pointer to the value of amount
uMPtr[0] = amount.decimalValue
/// Let's check the connection between variable/pointee and the poiner
Swift.print(uMPtr.pointee) /// result: 453.5858798339999232
/// One more pointer to the pointer
let uPtr = UnsafePointer<Decimal>.init(uMPtr)
/// Standard function call
NSDecimalRound(uMPtr, uPtr, Int(2), NSDecimalNumber.RoundingMode.bankers)
/// Check the result
Swift.print(uMPtr.pointee as NSDecimalNumber) /// result: 453.59
My solution:
var from: Double = 1.54762
var to: Double = 0.0
let decimalSize = 2.0 //you want to round for 2 digits after decimal point, change to your right value
let k = pow(10.0, decimalSize) //k here is 100
let cent = from*k
/*
get floor (integer) value of this double,
equal or less than 'cent'.You will get 154.
For negative value, it will return-155.
If you want to get -154, you have to use ceil(cent) for cent < 0.
*/
let centRound = floor(cent)
to = centRound/k
println("From: \(from), to: \(to)")
As additional info to HoaParis answer, you can make an extensions for Double so you can call it easily again later:
extension Double{
func roundDown(decimals:Int)->Double{
var from: Double = self
var to: Double = 0.0
let decimalSize = 2.0 //you want to round for 2 digits after decimal point, change to your right value
let k = pow(10.0, Double(decimals)) //k here is 100
var cent = from*k
var centRound = floor(cent) //get floor (integer) value of this double.You will get 154.
to = centRound/k
return to
}
}
var from: Double = 1.54762
from.roundDown(2)// 1.54
from.roundDown(3)// 1.547
Here's another approach (if you just want a fix rounding to 2 digits):
extension Double {
mutating func roundTo2Digits() {
self = NSString(format:"%2.2f", self).doubleValue
}
}
var a:Double = 12.3456
a.roundTo2Digits()
// Playground - noun: a place where people can play
import UIKit
// why rounding double (float) numbers is BAD IDEA
let d1 = 0.04499999999999999 // 0.045
let d2 = d1 + 5e-18 // 0.045 (a 'little bit' bigger)
let dd = d2 - d1 // 0.00000000000000000693889390390723
dd == 5e-18 // false
// this should work by mathematical theory
// and it wokrks ...
// BUT!! the Double DOESN'T means Decimal Number
func round(d: Double, decimalNumbers: UInt) -> Double {
let p = pow(10.0, Double(decimalNumbers))
let s = d < 0.0 ? -1.0 : 1.0
let dabs = p * abs(d) + 0.5
return s * floor(dabs) / p
}
// this works as expected
let r1 = round(d1, 3) // 0.045
let r2 = round(d2, 3) // 0.045
r1 == r2 // true
// this works only in our heads, not in my computer
// as expected too ... :-)
let r11 = round(d1, 2) // 0.04
let r21 = round(d2, 2) // 0.05
r11 == r21 // false
// look at the difference, it is just about the decimal numbers required
// are you able predict such a result?
I read on stackoverflow that easiest way to convert a DateTime variable back to Excel date was simply to do:
let exceldate = int(DateTime)
Admittedly this was in c# and not f#. This is supposed to work as the decimals represent time and int part represents date. I tried this and f# comes back with the error:
The type 'DateTime' does not support a conversion to the type 'int'
So how do I convert back to excel date?
More specificly, I m trying to create a vector of month 1st for a period between start date and end date. Both vector output and start date and end date are floats, i.e. excel dates. Here my clumsy first attempt:
let monthlies (std:float) (edd:float) =
let stddt = System.DateTime.FromOADate std
let edddt = System.DateTime.FromOADate edd
let vecstart = new DateTime(stddt.Year, stddt.Month, 1)
let vecend = new DateTime(edddt.Year, edddt.Month, 1)
let nrmonths = 12 * (edddt.Year-stddt.Year) + edddt.Month - stddt.Month + 1
let scaler = 1.0 - (float(stddt.Day) - 1.0) / float(DateTime.DaysInMonth(stddt.Year , stddt.Month))
let dtsvec:float[] = Array.zeroCreate nrmonths
dtsvec.[0] <- float(vecstart)
for i=1 to (nrmonths-1) do
let temp = System.DateTime.FromOADate dtsvec.[i-1]
let temp2 = temp.AddMonths 1
dtsvec.[i] = float temp2
dtsvec
This doesnt work because of the conversion issue and is rather complicated and imperative.
How do I do the conversion? How can I do this more functionally? Thanks
Once you have the DateTime object, just call ToOADate, like so:
let today = System.DateTime.Now
let excelDate = today.ToOADate()
So your example would end up like so:
let monthlies (std:float) (edd:float) =
let stddt = System.DateTime.FromOADate std
let edddt = System.DateTime.FromOADate edd
let vecstart = new System.DateTime(stddt.Year, stddt.Month, 1)
let vecend = new System.DateTime(edddt.Year, edddt.Month, 1)
let nrmonths = 12 * (edddt.Year-stddt.Year) + edddt.Month - stddt.Month + 1
let scaler = 1.0 - (float(stddt.Day) - 1.0) / float(System.DateTime.DaysInMonth(stddt.Year , stddt.Month))
let dtsvec:float[] = Array.zeroCreate nrmonths
dtsvec.[0] <- vecstart.ToOADate()
for i=1 to (nrmonths-1) do
let temp = System.DateTime.FromOADate dtsvec.[i-1]
let temp2 = temp.AddMonths 1
dtsvec.[i] = temp2.ToOADate()
dtsvec
In regards to getting rid of the loop, maybe something like this?
type Vector(x: float, y : float) =
member this.x = x
member this.y = y
member this.xDate = System.DateTime.FromOADate(this.x)
member this.yDate = System.DateTime.FromOADate(this.y)
member this.differenceDuration = this.yDate - this.xDate
member this.difference = System.DateTime.Parse(this.differenceDuration.ToString()).ToOADate
type Program() =
let vector = new Vector(34.0,23.0)
let difference = vector.difference