How to store a price in two integers in swift - swift

I have read that I shouldn't treat prices as double numbers and that I should store them as two integers.
If for example I have an input of type 246.464 in a UITextField how should I make it two integers?

Forget two integers, store it as NSDecimalNumber. That's the class specifically created for that task.
Unlike float or double, NSDecimalNumber uses decadic arithmetic therefore there is no loss in precision when converting between decadic to binary and viceversa.
let text = "246.464" // textField.text
let number = NSDecimalNumber(string: text)

Related

How can I declare and initialize a constant bigger than UInt64 in Swift?

I like to know, How can I declare and initialize a constant bigger than UInt64 in Swift?
Swift infer seems unable to work for down number. How I should solve this issue?
let number = 11111111222222233333333344444445555555987654321 // Error: overflow
print(number, type(of: number))
Decimal is the numeric type capable of holding the largest value in Swift. However,you can't declare a Decimal literal, since integer literals are inferred to Int, while floating point literals are inferred to Double, so you need to initialise the Decimal from a String literal.
let number = Decimal(string: "321321321155564654646546546546554653334334")!
From the documentation of NSDecimalNumber (whose Swift version is Decimal and hence their numeric range is equivalent):
An instance can represent any number that can be expressed as mantissa x 10^exponent where mantissa is a decimal integer up to 38 digits long, and exponent is an integer from –128 through 127.
If you need to be able to represent arbitrary-length numbers in Swift, you need to use a 3rd party library (or create one yourself), there's no built-in type that could handle this in Swift.

How to store 1.66 in NSDecimalNumber

I know float or double are not good for storing decimal number like money and quantity. I'm trying to use NSDecimalNumber instead. Here is my code in Swift playground.
let number:NSDecimalNumber = 1.66
let text:String = String(describing: number)
NSLog(text)
The console output is 1.6599999999999995904
How can I store the exact value of the decimal number 1.66 in a variable?
In
let number:NSDecimalNumber = 1.66
the right-hand side is a floating point number which cannot represent
the value "1.66" exactly. One option is to create the decimal number
from a string:
let number = NSDecimalNumber(string: "1.66")
print(number) // 1.66
Another option is to use arithmetic:
let number = NSDecimalNumber(value: 166).dividing(by: 100)
print(number) // 1.66
With Swift 3 you may consider to use the "overlay value type" Decimal instead, e.g.
let num = Decimal(166)/Decimal(100)
print(num) // 1.66
Yet another option:
let num = Decimal(sign: .plus, exponent: -2, significand: 166)
print(num) // 1.66
Addendum:
Related discussions in the Swift forum:
Exact NSDecimalNumber via literal
ExpressibleByFractionLiteral
Related bug reports:
SR-3317
Literal protocol for decimal literals should support precise decimal accuracy, closed as a duplicate of
SR-920
Re-design builtin compiler protocols for literal convertible types.

How do I convert a 50 digit string into the appropriate integer type in Swift?

I need to convert this 50 digit string 53503534226472524250874054075591789781264330331690 into the appropriate number type. I tried this:
let str = "53503534226472524250874054075591789781264330331690"
let num = str.toInt(); // Returns nil
let num = Int64(str.toInt()); // Errors out
The maximimum size of an Int64 is 9,223,372,036,854,775,807 when it is signed. So you cannot convert it just like that.
You need something like the BigInt class found in other languages. Check this other question where they answer with alternatives about BigInt in Swift:
BigInteger equivalent in Swift?
In summary, there are third-party libraries out there for arbitrary long integers. The only alternative from Apple is NSDecimalNumber but its limit is 38 digits, whereas your number has 50.

Number Operations and Return Types

I am confused by what is returned when performing number operations in Swift between various types. Consider the following:
var castedFoo = Float(7.0/5.0) // returns 1.39999997...
var specifiedTypeFoo:Float = 7/5.0 //returns 1.39999997...
var foo = (7/5.0) //returns 1.4
What separates the first two from the last one? They are all returning floats, so why is the value from the last one rounded? I understand that the first is casted and the second explicitly specified to be a Float, but the last one also returns a Float value. So what makes the difference here?
According to Swift documentation,
Unless otherwise specified, the default type of a floating-point literal is the Swift standard library type Double, which represents a 64-bit floating-point number.
In other words, the literal 5.0 is of type Double.
Your first two examples set the result type to Float; your last example keeps the type of the result a Double, because the result of the division of an Int and a Double is a Double. Because of that difference, the last result has higher precision.

formatting NSDecimalNumber issue

I'm trying to create NSDecimalNumber with simply format like: 123.00 with two fractional digits after dot, always. I tried use the NSFormatter and many other ways like converting float from string and creating then NSDecimalNumber from this string, but it's not working.
The problem is that I need only NSDecimalNumber in this format, not NSString or any other.
Thanks for any advice,
Paul
You may get idea from this.
float num = 123.1254546;
NSString *str = [NSString stringWithFormat:#"%.2f",num];
NSLog(#"%.2f %#",num,str);
I think you simply need to do Type Casting operation for Two times as bellow
float value = 123.562;
int myDigit = value;
it gives value 123 in myDigit variable
NSLog(#"MyDigit in Decimal = %d",myDigit);
Output is MyDigit in Decimal = 123
now if you want output like 123.000 then simply write as bellow
float valueWithZiro = myDigit;
NSLog(#"MyDigit with 000 == %3f",valueWithZiro);
Output is MyDigit in Decimal = 123.000
NSDecimalNumber, like NSNumber, cannot contain formatting information. The object structure simply doesn't support it. It represents a number, not the way the number is displayed.
You can convert it to a formatted NSString (which you say you don't want). But you can't do what you're asking.
You convert it to a formatted NSString using an NSNumberFormatter. It's the object that allows you to specify the decimal and thousands separators, number of decimal places to display, the currency symbol, etc.
Maybe you were looking to store a NSNumberDecimal with just two digits after the fraction?
If so NSDecimalNumberBehaviors is your friend.
I had a similar need and used the following:
self.decimalHandlingBehaviorForApp = [NSDecimalNumberHandler
decimalNumberHandlerWithRoundingMode:NSRoundUp
scale:2 raiseOnExactness:NO
raiseOnOverflow:NO raiseOnUnderflow:NO
raiseOnDivideByZero:YES];
Edit: added example of using it
// update the taxable total first
self.cartTaxableTotal = [self.cartTaxableTotal decimalNumberByAdding:itemAdded.priceOfItem
withBehavior:self.decimalHandlingBehaviorForApp];