NSSet: should I use float/double in NSSet? - ios

As we know, there is float number precision problem when comparing float numbers.
Also that NSSet uses equality compare to keep elements unique in it.
So what happens when NSSet stocks NSValues that have float/double in it,
Should we never use float in NSSet to avoid float precision problem?

there is float number precision problem when comparing float numbers.
To be precise, only comparison for equality presents a problem. Unfortunately, that's the kind of comparison done by NSSet.
So what happens when NSSet stocks NSValues that have float/double in it?
It treats numbers that are really close to each other but not exactly equal as different items in the set. That is why one should be extremely careful when using floating point numbers in NSSet. Ideally, you should avoid it if you can.

Related

Comparing NSNumber which contains float values

I am facing issue while comparing NSNumber. Below is my code:
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setNumberStyle:NSNumberFormatterDecimalStyle];
NSNumber *avgRating= [formatter numberFromString:attemptedQuizDetailsModel.avgRating];
NSNumber *firstMinVal=[NSNumber numberWithFloat:0.6];
NSNumber *firstMaxVal=[NSNumber numberWithFloat:1.5];
NSNumber *secondMinVal=[NSNumber numberWithFloat:1.6];
NSNumber *secondMaxVal=[NSNumber numberWithFloat:2.5];
NSNumber *thirdMinVal=[NSNumber numberWithFloat:2.6];
NSNumber *thirdMaxVal=[NSNumber numberWithFloat:3.5];
NSNumber *fourthMinVal=[NSNumber numberWithFloat:3.6];
NSNumber *fourthMaxVal=[NSNumber numberWithFloat:4.5];
NSNumber *fifthMinVal=[NSNumber numberWithFloat:4.6];
if(avgRating >= firstMinVal && avgRating <= firstMaxVal){
} else if (avgRating>=fifthMinVal) {
}
if avgRating=4.6 and fifthMinVal=4.60,my comparison check is returning they are not equal. How to handle this type of comparison?
You have two issues, one to do with objects and the other floating-point arithmetic.
First, the expression:
avgRating>=firstMinVal&&avgRating<=firstMaxVal
does not do what you think it does.
Every variable in this expression is of type NSNumber *, that is it is a pointer to an object containing a number, and the comparisons you are doing are between pointers – which is perfectly legal in (Objective-)C.
To compare two NSNumber objects you should use the compare: method, this returns an NSComparisonResult value indicating the order of the two values.
However you would be better off in your case simply sticking with double or float values throughout and not using object types.
In either case you must be careful about comparing floating-point values, comparing for equality (== or !=) may not give the results you expect due to limited precision and number base issues (a topic you should study if programming with floating-point). Ordering comparisons (>, <, >=, <=) are generally better, and you appear to be using only those, but may still give unexpected results for two "equal" values. To test for (in)equality it is usual to test for the absolute difference (abs(), fabs()) to be less than a small value – the magnitude of which depends on the application.
HTH
This is a classic problem with comparing floating-point values; due to the way floating-point works, you can't reliably test them for equality.
What you should do instead is to compare the absolute value of the difference between the two floating-point values, and see if the difference is less than some suitably small value (say, 0.001). What value you use for the delta depends on what precision you need.

Converting Double to NSNumber in Swift Loses Accuracy [duplicate]

This question already has answers here:
Is floating point math broken?
(31 answers)
Closed 6 years ago.
For some reason, certain Doubles in my Swift app are giving me trouble when converting to NSNumber, while some are not. My app needs to convert doubles with 2 decimal places (prices) to NSNumbers so they can be stored and retrieved using Core Data. For example, a few particular prices such as 79.99 would evaluate to 99.98999999999999 unless specifically formatted using NSNumber's doubleValue method.
Here selectedWarranty.price = 79.99 as shown in debugger
// item.price: NSNumber?
// selectedWarranty.price: Double?
item.price = NSNumber(double: selectedWarranty.price!)
I programmed some print statements to show how the conversion works out
Original double: 79.99
Converted to NSNumber: 79.98999999999999
.doubleValue Representation: 79.99
Can somebody explain if there is a reason why the initializer cannot surely keep 2 decimal places for every number? I would really like to store the prices in Core Data like they should be. Formatting every time it is displayed doesn't sound very convenient.
UPDATE:
Converted Core Data object to type NSDecimalNumber through data model, 79.99 and 99.99 no longer a problem, but now more manageable issue with different numbers...
Original double: 39.99
Converted to NSDecimalNumber: 39.99000000000001024
Firstly, you're confusing some terms. 79.98999999999999 is higher precision than 79.99 (it has a longer decimal expansion), but lower accuracy (it deviates from the true value).
Secondly, NSNumber does not store neither 79.99 nor 79.98999999999999. It stores the magnitude of the value according to the IEEE 754 standard. What you're seeing is likely the consequence of the printing logic that's applied to convert that magnitude into a human readable number. In any case, you should not be relying on Float or Double to store values with a fixed precision. By their very nature, they sacrifice precision in order to gain a longer range of representable values.
You would be much better off representing prices as an Int of cents, or as an NSDecimalNumber.
Please refer to Why not use Double or Float to represent currency?
That's how double works everywhere. If you need only 2 decimal places consider using integer/long instead adding point after second digit, when need to display the value.

NSNumber to NSDecimalNumber conversion issue

When I convert NSNumber to NSDecimalNumber this conversion is frequently not true.
I have a number like 92.43 when I convert this value to decimal or double
[number decimalValue] or [number doubleValue] value changes as 92.4299999999..
I did so many things like [NSDecimalNumber decimalNumberWithDecimal:[number decimalValue] its always returns "92.429999" to this number.
How do I use NSNumber originalValue decimal or double it is not matter I want to use "92.43" this number as "92.43". And Why this value changing?
Why is this happening?
A simplified explanation would be that it is related to how computers perform the calculations and how floating point numbers are being represented. Most floating point numbers simply does not have an accurate enough representation since they require infinite number of digits to be represented, hence the rounding (also known as roundoff or rounding error).
What can you do?
If you really need to perform accurate calculations (calculating prices for example), you should not work with NSNumber at all, but use NSDecimalNumber all the way and use it for all calculation. The safest way would be to create it from a string, for example:
NSDecimalNumber *decimalNumber = [NSDecimalNumber decimalNumberWithString:#"92.34567891"];
If accuracy doesn't matter, you can alway format the result to a fixed number of decimal places. In this case, you might also want to look into NSDecimalNumberHandler to define the rounding behaviour.
If you just need to print this number then try:
NSLog(#"%.2f", decimalNumber);
This will round 92.4299999999.. on two decimals and result will be:
92.43

Comparing NSNumber instances with isEqualToNumber

Yes, I've read the other posts on stackoverflow about comparing NSNumber and none of them seem to quite address this particular situation.
This solution was particularly bad ... NSNumber compare: returning different results
Because the suggested solution doesn't work at all.
Using abs(value1 - value2) < tolerance is flawed from the start because fractional values are stripped off, making the tolerance irrelevant.
And from Apple documentation... NSNumber explicitly doesn't guarantee that the returned type will match the method used to create it. In other words, if you're given an NSNumber, you have no way of determining whether it contains a float, double, int, bool, or whatever.
Also, as best I can tell, NSNumber isEqualToNumber is an untrustworthy method to compare two NSNumbers.
So given these definitions...
NSNumber *float1 = [NSNumber numberWithFloat:1.00001];
NSNumber *double1 = [NSNumber numberWithDouble:1.00001];
If you run the debugger and then do 2 comparisons of these identical numbers using ==, one fails, and the other does not.
p [double1 floatValue] == [float1 floatValue] **// returns true**
p [double1 doubleValue] == [float1 doubleValue] **// returns false**
If you compare them using isEqualToNumber
p [float1 isEqualToNumber:double1] **// returns false**
So if isEqualToNumber is going to return false, given that the creation of an NSNumber is a black box that may give you some other type on the way out, I'm not sure what good that method is.
So if you're going to make a test for dirty, because an existing value has been changed to a new value... what's the simplest way to do that that will handle all NSNumber comparisons.. not just float and double, but all NSNumbers?
It seems that converting to a string value, then compariing would be most useful, or perhaps a whole lot of extra code using NSNumberFormatter.
What are your thoughts?
It is not possible to reliably compare two IEEE floats or doubles. This has nothing to do with NSNumber. This is the nature of floating point. This is discussed in the context of simple C types at Strange problem comparing floats in objective-C. The only correct way to compare floating point numbers is by testing against a tolerance. I don't know what you mean by "fractional values are stripped off." Some digits are always lost in a floating point representation.
The particular test value you've provided demonstrates the problems quite nicely. 1.00001 cannot be expressed precisely in a finite number of binary digits. Wolfram Alpha is a nice way to explore this, but as a double, 1.00001 rounds to 1.0000100000000001. As a float, it rounds to 1.00001001. These numbers, obviously, are not equal. If you roundtrip them in different ways, it should not surprise you that isEqualToNumber: fails. This should make clear why your two floatValue calls do turn out to be equal. Rounded to the precision of float, they're "close enough."
If you want to compare floating point numbers, you must compare against an epsilon. Given recent advances in compiler optimization, even two identical pieces of floating point C code can generate slightly different values in their least-significant digits if you use -Ofast (we get big performance benefits by allowing that).
If you need some specific number of significant fractional digits, then it is usually better to work in fixed point notation. Just scale everything by the number of digits you need and work in integers. If you need floating point, but just want base-10 to work well (rather than base-2), then use NSDecimalNumber or NSDecimal. That will move your problems to things that round badly in base-10. But if you're working in floating point, you must deal with rounding errors.
For a much more extensive discussion, see "What Every Programmer Should Know About Floating-Point Arithmetic."

parsing and reading a floating point values in Haskell

I'm working on parsing with haskell, I want to parse a timestamp value expressed in such a way
946685561.618847
I have no problem to recognize (parse) it, but my problem is about the type of the result. I think of two situations:
Is there a fractional type in Haskell so that the result can be associated with the fractional value?
If this is not the case then how to store this value, since Int range from -229 to 229 - 1?
There are actually multiple fractional types--there is even a whole Fractional class.
The most commonly used is a Double, which is a double-precision floating point number. You can also use Float which is single precision.
Another alternative is to use the Rational type, which lets you store a number as a ratio of two Integers. (Coincidentally, Integer is an unbounded integral type. Int is the name for the bounded version.)
These types (Double, Float and Rational) are good for storing rational values. If you just want to store a large integral value, use Integer which is unbounded. (That is, it can store arbitrarily sized integers.)

Resources