Swift .isNaN Understand how it works - ios

I am currently facing an issue in understanding how .isNan works.
I am maintaining an application which is not developed (in Swift 2.3) by myself.
We have a nice amount of crashes from this code, and from my understanding I don't understand how.
Here is the method, which is simply a format method in order to set the appropriate value to your label by testing different cases.
static func formatFloat(float: Float?, withMaxDigits
max: Int, andUnit unit: String) -> String {
var label: String = "-"
if let float = float {
let numberFormatter = NSNumberFormatter()
numberFormatter.numberStyle = NSNumberFormatterStyle.DecimalStyle
numberFormatter.minimumFractionDigits = 0
numberFormatter.maximumFractionDigits = max
numberFormatter.roundingMode = .RoundHalfUp
if !float.isNaN {
var formattedValue = numberFormatter.stringFromNumber(float)!
if(formattedValue == "-0")
{
formattedValue = "0"
}
label = "\(formattedValue) \(unit)"
}
}
return label
}
Am I right that it justs check to determine whether a value is NaN or not, in order to test everything, and set the text accordingly ?
I read some posts/documentations and I don't understand this :
In some languages NaN != NaN, but this isn't the case in Cocoa.
What about nil and NaN ? I mean isNan check for false right ?

The IEEE floating point spec documents certain bit patterns that represent NaN invalid results.
nil is different from a NaN. In Swift, only an optional can contain nil, and it indicates the absence of a value.
a NaN means you performed some operation that resulted in an invalid result. You should check the isNaN property to see if a number contains a NaN.
Edit:
Note that there are different values that are marked as NaN, so one .NaN value may not be equal to another .NaN.

No, nan is a value that a floating point can take. nil can only be taken by optional vars. Also I'm not sure where you got that quote, but .nan == .nan is false. For more information read https://developer.apple.com/reference/swift/floatingpoint

Related

NumberFormatter negative value to positive

I would like to convert a negative value into a positive one using NumberFormatter. The value should represent a percent change and should look like this: 1.46% even if the value is negative -1.46.
There is a numberFormatter.negativeFormat, but I am unsure what the format should look like. I tried numberFormatter.negativeFormat = "0.00" but the percent sign disappears and I do not want to add it explicitly at the end, because I am using numberStyle
numberFormatter.numberStyle = .percent
Any ideas what would be the best solution?
You can extend the Double type to create a specific property that returns a String with the absolute value:
extension Double {
var positivePercent: String {
return abs(self).formatted(.percent)
}
}
Usage:
let a = -0.0146
print(a.positivePercent) // 1.46%

Scale and Precision of NSDecimalNumber value

Let us suppose I have a variable v of type NSDecimalNumber
let v = 34.596904 in its own format.
I want to know the precision and scale of this number, not the default one. I did not find any function in the NSDecimalNumber class which gives these values or maybe someone would like to throw some light on how it works.
precision = 8
scale = 6
precision is count of significant digits in number and scale is count of significant digit after decimal
This extension will give you the specific value for your only example:
extension Decimal {
var scale: Int {
return -self.exponent
}
var precision: Int {
return Int(floor(log10((self.significand as NSDecimalNumber).doubleValue)))+1
}
}
Usage:
let v: NSDecimalNumber = NSDecimalNumber(string: "34.596904")
print("precision=\((v as Decimal).precision)") //->precision=8
print("scale=\((v as Decimal).scale)") //->scale=6
But I cannot be sure if this generates expected results in all cases you have in mind, as you have shown only one example...
One more, in Swift, Decimal and NSDecimalNumber are easily bridgeable and you should better use Decimal as far as you can.

String to Double conversion loses precision in Swift

I want to covert a string to double and keep the same value:
let myStr = "2.40"
let numberFormatter = NSNumberFormatter()
numberFormatter.locale = NSLocale(localeIdentifier: "fr_FR")
let myDouble = numberFormatter.numberFromString(myStr)?.doubleValue ?? 0.0
myDouble is now
Double? 2.3999999999999999
So how to convert "2.40" to exact be 2.40 as Double ??
Update:
Even rounding after conversion does not seem to work
I don't want to print, I want to calculate and it's important that the number should be correct, it's Money calculation and rates
First off: you don't! What you encountered here is called floating point inaccuracy. Computers cannot store every number precisely. 2.4 cannot be stored lossless within a floating point type.
Secondly: Since floating point is always an issue and you are dealing with money here (I guess you are trying to store 2.4 franc) your number one solution is: don't use floating point numbers. Use the NSNumber you get from the numberFromString and do not try to get a Double out of it.
Alternatively shift the comma by multiplying and store it as Int.
The first solutions might look something like:
if let num = myDouble {
let value = NSDecimalNumber(decimal: num.decimalValue)
let output = value.decimalNumberByMultiplyingBy(NSDecimalNumber(integer: 10))
}

Getting weird value in Double

Hello i made a "Clicker" as a first project while learning swift i have an automated timer that is supposed to remove some numbers from other numbers but sometimes i get values like 0.600000000000001 and i have no idea why.
Here is my "Attack" function that removes 0.2 from the Health of a zombie.
let fGruppenAttackTimer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("fGruppenAttackTime"), userInfo: nil, repeats: true)
func fGruppenAttackTime() {
zHealth -= 0.2
if zHealth <= 0 {
zHealth = zSize
pPengar += pPengarut
}
...
}
And here is my attackZ button that is supposed to remove 1 from the health of the zombie
#IBAction func attackZ(sender: UIButton) {
zHealth -= Double(pAttack)
fHunger -= 0.05
fGruppenHunger.progress = Float(fHunger / 100)
Actionlbl.text = ""
if zHealth <= 0 {
zHealth = zSize
pPengar += pPengarut
}
}
Lastly here are the variables value:
var zHealth = 10.0
var zSize = 10.0
var pAttack = 1
var pPengar = 0
var pPengarut = 1
When the timer is on and the function is running and i click the button i sometimes get weird values like 0.600000000000001 and if i set the 0.2 in the function to 0.25 i get 0.0999999999999996 sometimes. I wonder why this happens and what to do with it.
In trojanfoe's answer, he shares a link that describes the source of the problem regarding rounding of floating point numbers.
In terms of what to do, there are a number of approaches:
You can shift to integer types. For example, if your existing values can all be represented with a maximum of two decimal places, multiply those by 100 and then use Int types everywhere, excising the Double and Float representations from your code.
You can simply deal with the very small variations that Double type introduces. For example:
If displaying the results in the UI, use NumberFormatter to convert the Double value to a String using a specified number of decimal places.
let formatter = NumberFormatter()
formatter.maximumFractionDigits = 2
formatter.minimumFractionDigits = 0 // or you might use `2` here, too
formatter.numberStyle = .decimal
print(formatter.string(for: value)!)
By the way, the NSNumberFormatter enjoys another benefit, too, namely that it honors the localization settings for the user. For example, if the user lives in Germany, where the decimal place is represented with a , rather than a ., the NSNumberFormatter will use the user's native number formatting.
When testing to see if a number is equal to some value, rather than just using == operator, look at the difference between two values and seeing if they're within some permissible rounding threshold.
You can use Decimal/NSDecimalNumber, which doesn't suffer from rounding issues when dealing with decimals:
var value = Decimal(string: "1.0")!
value -= Decimal(string: "0.9")!
value -= Decimal(string: "0.1")!
Or:
var value = Decimal(1)
value -= Decimal(sign: .plus, exponent: -1, significand: 9)
value -= Decimal(sign: .plus, exponent: -1, significand: 1)
Or:
var value = Decimal(1)
value -= Decimal(9) / Decimal(10)
value -= Decimal(1) / Decimal(10)
Note, I explicitly avoid using any Double values such as Decimal(0.1) because creating a Decimal from a fractional Double only captures whatever imprecision Double entails, where as the three examples above avoid that entirely.
It's because of floating point rounding errors.
For further reading, see What Every Computer Scientist Should Know About Floating-Point Arithmetic.
Squeezing infinitely many real numbers into a finite number of bits
requires an approximate representation. Although there are infinitely
many integers, in most programs the result of integer computations can
be stored in 32 bits. In contrast, given any fixed number of bits,
most calculations with real numbers will produce quantities that
cannot be exactly represented using that many bits. Therefore the
result of a floating-point calculation must often be rounded in order
to fit back into its finite representation. This rounding error is the
characteristic feature of floating-point computation.

Round function doesn't work as expected

I'm trying round a Double value with two decimal places:
var x = 0.68999999999999995
var roundX = round(x * 100.0) / 100.0
println(roundX) // print 0.69
If print the value is correct.. but the var value isn't that i expect, continue 0.68999999999999995
I need the Double value... not String like other StackOverflow answers :(
Floating point numbers like doubles do not have a number of decimal places. They store values in binary, and a value like .69 can't be represented exactly. It's just the nature of binary floating point on computers.
Use a number formatter, or use String(format:) as #KRUKUSA suggests
var x:Double = 0.68999999999999995
let stringWithTwoDecimals = String(format: "%.2f", x)
println(stringWithTwoDecimals)

Resources