I'm currently having a very strange issue. I'm running into the "float imprecision problem" when defining a number as an NSString. Here is an excerpt from my JSON file, which I am reading from my iOS application:
{"id":"804760","addtime":"1398886836","symbol":"UG","exchange":"NAS(USA)","shares":"71.00","cost":"0","in_price":"32.64","price":28.78,"rchange":-0.72,"rchange_p":-2.44,"a_open":"28.72","volume":"8K","a_low":"28.54","a_high":"29.44","pettm":"23.40","ps":"8.60","pb":"8.40","gain_p":"-11.83%","gain":-274.06,"comments":""}
I read this into an NSMutableArray, and then I want to display individual pieces of information. The code I use to do this is as follows:
label.text = [NSString stringWithFormat:#"%#",(NSString *)json[indexPath.section][#"detail"][indexPath.row][#"price"]];
However, I'm getting the floating point precision error where there's a ton of extra decimal places that were not there in the original JSON output. This confuses me, because I specified the data type as NSString, so even though the string consists of all numbers and decimal points, shouldn't it still be treated as such?
What I am not understanding correctly, and why is this happening? By the way, the error is also resolved if I add quotation marks when creating the JSON output, such as "a_high":"\"29.44\"". I would prefer not to do this though.
,"in_price":"32.64","price":28.78,"rchange":-0.72, -- do you notice something different about "price" vs "in_price"? "in_price" has a string value, "price" has a numeric value, as does "rchange". The numerics will translate into an NSNumber, not an NSString.
Related
I'm trying to get a json string but I noticed that when I pass some double numbers to my query the integer part is separated by a coma from the floating part. I need them to be separated by a point. Is this connected to the Language of the os?
You can use a simple regex to fix this issue:
\d+,\d+ to select the offending bits of JSON, and a string-based replace on the results. If you already have the numbers separated out, use the replace function (something like val.replace(",",".")) on the value you have, and cast it to a float (float(val) in Python).
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.
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."
I'm retrieving a string from an NSData object dataOut (coming from a CBCharacteristic), and defining a testString as well which is defined as the same value as shown below:
The problem is when I try to compare the two, I get that the two strings are not equal, even though the debugger shows otherwise:
Here is the comparison:
The log keeps logging "Strings are not equal!"
What am I doing wrong? Is the encoding incorrect, even though the strings are the same?
You will see them are different when you convert them to NSData and print out the data.
What you see is not always what you get especially with Unicode characters. There maybe invisible characters or some characters that looks similar.
As already pointed out in the topic, I got the following error:
Character #\u009C cannot be represented in the character set CHARSET:CP1252
trying to print out a string given back by drakma:http-request, as far as I understand the error-code the problem is that the windows-encoding (CP1252) does not support this character.
Therefore to be able to process it, I might/must convert the whole string.
My question is what package/library does support converting strings to certain character-sets efficiently?
An alike question is this one, but just ignoring the error would not help in my case.
Drakma already does the job of "converting strings": after all, when it reads from some random webserver, it just gets a stream of bytes. It then has to convert that to a lisp string. You probably want to bind *drakma-default-external-format* to something else, although I can't remember off-hand what the allowable values are. Maybe something like :utf-8?