Inconsistent values with NSNumber to double multiplication - ios

I'm currently parsing NSString values to NSNumbers and then adding them into a NSMutableArray called operands in an object called "data" like so:
NSNumberFormatter * f = [[NSNumberFormatter alloc] init];
[f setNumberStyle:NSNumberFormatterDecimalStyle];
NSNumber * myNumber = [f numberFromString:*operandString];
[data.operands addObject:myNumber];
I then retrieve those numbers, perform some math on them, then update the array:
double x = [[data.operands objectAtIndex: i]doubleValue];
double y = [[data.operands objectAtIndex: i + 1]doubleValue];
double answer = x * y;
[data.operands replaceObjectAtIndex:(i) withObject:[NSNumber numberWithDouble:answer]];
When I get the answer, everything looks fine eg: ( 3.33 * 5 = 16.65)
BUT, when I look in the debugger I'm seeing some crazy values for x and answer, such as:
x = 3.3300000000000001
answer = 16.649999999999999
Why is this happening? Am I loosing some precision with parsing these back and fourth? Is it how I've used the NSNumberFormatter to parse the string?
The reason I'm in trouble with this is because I'm trying to ensure there's no double overflow errors so I'm using this simple test to check the integrity:
if (answer / y != x){
//THROW OVERFLOW ERROR
}
With the above crazy numbers this is always inconsistent. When I NSLog the answer it comes out fine:
NSLog (#"%g", [[data.operands objectAtIndex:i]doubleValue]]);
Same for
NSLog (#"%f", [[data.operands objectAtIndex:i]doubleValue]]);

You are not losing any precision that you need to worry about. Those are the correct values. There are only about 2^60 different double numbers, that finite set has to try to approximately cover the infinite 'number of numbers' in the range that doubles cover.
In other words, there are no exact answers in computer land and your
if (answer / y != x){
//THROW OVERFLOW ERROR
}
Will not work. Or it may work much of the time, but fail if you push it. Instead you need to acknowledge the limited precision (which is pretty high precision) of doubles:
//Don't waste time worrying like this...
if (fabs(answer / y - x) > 1e-12*fabs(answer)){
//Not correct or useful thing to check don't use this - i did not check
}
// let the math package handle it:
if (isnan(answer)){
// we gots problems
}
if (!isnormal(answer)){
// we gots some other problems
}
Also don't forget that 10^300 is a very large number, doubles work pretty well. To use 32 bit floats you need to pay much more attention to order of execution, etc.
NSLog is likely outputting with fewer decimals of precision, and rounds to the nearest thing, so the answers look better.

Related

How to Convert Float to NSData Bytes

Using the following code, I am attempting to convert three float values into a single NSData object, which I can then transmit over a serial port.
float kP = [[self.kPTextField stringValue] floatValue];
float kI = [[self.kITextField stringValue] floatValue];
float kD = [[self.kDTextField stringValue] floatValue];
float combined[] = {kP, kI, kD};
NSData *dataPackage = [NSData dataWithBytes:&combined length:sizeof(combined)];
[self.serialPort sendData:dataPackage];
The problem is that it doesn't seem to work very well. Whenever I use the "sizeof()" C function, it tells me that the "dataPackage" is only 8 bytes, even though 3 float values should total 12 bytes. I am receiving the data with an Arduino. It sees the bytes coming in, but they aren't legible at all. I don't think it's a problem on the Arduino side of things (but who knows?).
Any help would be appreciated! I'm not a CS major, just a bio major, and I've never learned this stuff in a formal way so I am sorry if my question is ridiculous. I've spent several hours searching the net about this problem and haven't found anything that helped.
EDIT: It turns out this code was completely correct. I made a simple mistake on the arduino side of things by using a struct instead of a union to take the bytes and convert them back into floats.
For others who may be in a similar predicament, a successful way to convert floats from bytes coming out of the serial port is the following:
(at top of implementation file)
union {
float pidVals[3];
byte bytes[12];
} pidUnion;
(inside loop)
if (Serial.available() > 11) {
for (int i = 0; i < 12; i++) {
pidUnion.bytes[i] = Serial.read();
}
}
//Now, you can get access to all three floats of data using pidUnion.pidVals[0], pidUnion.pidVals[1], etc.
This probably isn't the best or most reliable way to transmit data. There is no error-correcting mechanism or packet structure. But it does work in a pinch. I imagine you would probably want to find a way to create a packet of data along with a hash byte to make sure all of the data is correct on the other side, this code doesn't have any of that though.
There are multiple problems with your code.
First, you don't want to use stringValue on a text field. You want the text property, which is a string.
So the first line should read like this:
float kP = [self.kPTextField.text floatValue];
Second, in C, an array of things is a pointer. The data type of
float combined[]
and
float *combined
is identical. Both are "pointer to float".
So this code:
NSData *dataPackage = [NSData dataWithBytes:&combined
length: sizeof(combined)];
Should not have an ampersand in front of combined. It should read:
NSData *dataPackage = [NSData dataWithBytes:combined
length: sizeof(combined)];
Third, what matters is sizeof(combined), not sizeof(dataPackage).
The expression sizeof(dataPackage) will tell you the size of the variable dataPackage, which is a pointer to an NSData object. You must be running on a 64 bit device, where pointers are 8 bytes.
To test the length of the data in your NSData object, you want to ask it with the length property:
NSLog(#"sizeof(combined) = %d", sizeof(combined)";
NSData *dataPackage = [NSData dataWithBytes:&combined
length: sizeof(combined)];
NSLog(#"dataPackage.length = %d", dataPackage.length";
Both log statements should display values of 12.

Convert Double to NSString for label text

I'm trying to get an NSNumber out of an NSMutableArray that's been previously manipluated as a double and then added to the array to print out in a label (NSString).
It's important that the number stays as an accurate representatoin of a double with no scientific notation to abbreviate the answer.
The other requirement is to have it print to maybe 15 or 16 decimal places, rounding is optional but not required.
I also do not want trailing 0's when displaying the double
I've tried the following but these do not work...
This is ok but ends the number with a . (eg: 1+1=2.)
double test = [[data.argOperands objectAtIndex:0]doubleValue];
label.text = [NSString stringWithFormat:#"%3.2f", test];
label.text = [label.text stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:[NSString stringWithFormat:#"0"]]];
I then try something like this, which is wrong because if I do 9^99 it'll print inf or 0.0003/4 it'll give scientific numbers instead of the value
float y = [[calcData.argOperands objectAtIndex:0]doubleValue];;
label.text = [NSString stringWithFormat:#"%g", y];
If I do the following using double it's getting close, 9^99 works, but 3.33/5 returns 0.666000 with trailing 0's
double y = [[data.argOperands objectAtIndex:0]doubleValue];
label.text = [NSString stringWithFormat:#"%f", y];
Any code examples of how to do it this way using either NSNumberFormatter or NSDecimalNumber would be greatly appreciated.
"%.13f" Would give you 13 decimal places, but that would give you trailing zeros.
You may need to create an NSNumberFormatter and use that.
I suspect you're not going to be happy no matter what. Binary floating point is not an exact representation of decimal. The decimal value .1 is not an exact value in binary, and might display as something like .09999999998 if you display it with 13 decimal places.
You also might look at using NSDecimalNumber, which stores values as decimal digits. It's slower than other ways of doing math but you can control the results exactly.
After looking over http://nshipster.com/nsformatter/ and the giant NSNumberFormatter_Class doc I've come up with this code that prints everything to my requirements:
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setUsesSignificantDigits: YES];
numberFormatter.maximumSignificantDigits = 100;
[numberFormatter setGroupingSeparator:#""];
[numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
label.text = [numberFormatter stringFromNumber:stringFromNumber:#(1234.567800000555)];
This will actually print 1234.56780000056 (missing the 12th decimal place and rounding it up to the 11th decimal place) though I'm happy enough with this.
I'm still cleaning up the answer, I don't need maximumSignificantDigits = 100 obviously, but generally having a large number there helps to ensure I'm getting all the decimal places I need.
I had to set setGroupingSeparator:#"" because NSNumberFormatterDecimalStyle puts commas in numbers and I don't want them (eg: Instead of getting 1,000 I want 1000).

objective c float increment wrong value

I have a query regarding floating value increment in loop.
I have following code
float add = 1.02f;
float counter = 0.0f;
for (int i = 0; i < 20; i++) {
counter += add;
NSLog(#"%f",counter);
}
While executing this loop I am getting following result
1.020000
2.040000
3.060000
4.080000
5.100000
6.120000
7.140000
8.160000
9.180000
10.200001
11.220001
12.240002
13.260002
14.280003
15.300003
16.320004
17.340004
18.360004
19.380005
20.400005
Here is expected result
1.020000
2.040000
3.060000
4.080000
5.100000
6.120000
7.140000
8.160000
9.180000
10.200000
11.220000
12.240000
13.260000
14.280000
15.300000
16.320000
17.340000
18.360000
19.380000
20.400000
Why i am getting some floating point in loop without adding it.
I need to loop more then 1000 times. And I want the value in float variable.
Thanks in advance.
This happens because float cannot represent the values that you have with exact precision. There are two simple ways of fixing this:
Represent the number as 100 times the target value, and use integers - 1.02 becomes 102, 2.04 becomes 204, and so on.
Use NSDecimalNumber to represent your numbers - Unlike float, NSDecimalNumber can represent all your values with full precision.
Here is how to implement the first approach:
int add = 102;
int counter = 0;
for (int i = 0; i < 20; i++) {
counter += add;
NSLog(#"%d.%d", counter/100, counter%100);
}
Here is how to implement the second approach:
NSDecimalNumber add = [NSDecimalNumber decimalNumberWithString:#"1.02"];
NSDecimalNumber counter = [NSDecimalNumber zero];
for (int i = 0; i < 20; i++) {
counter = [counter decimalNumberByAdding:add];
NSLog(#"%#", counter);
}
Why i am getting some floating point in loop without adding it.
Because float is a binary type that doesn't represent decimal values exactly. Rather than trying to explain completely and correctly, let me point you to the well-known paper What Every Computer Scientist Should Know About Floating Point Arithmetic.
Floating point number representations in computers are approximations, they are not exact. Sometimes you end up trying to display a number that can't be exactly represented in the computer's floating point number implementation, so it gives you an approximation. Also you get small arithmetic errors from repeated multiplications, additions, etc. of floating point numbers. The best you can do is to use doubles, which have more precision than floats do. In special circumstances, you could also represent your data in a different format and just change how you display it to the user to fit what they expect. For example, when working with dollars and cents, you could just store a total as a number of cents (which would be only an integer) and then format it to be shown as dollars and cents correctly for the user. There's no floating point rounding issues happening then.
Floating point numbers use four bytes = 32 bits.
1 bit for sign
8 bits for exponent
23 bits for mantissa
Precision: The number of decimal digits precision is calculated via number_of_mantissa_bits * Log10(2). Thus ~7.2 and ~15.9 for single and double precision respectively.
That's why you start to see rounding errors on the 7th digit
Source link.

Why is converting my float to an int making the number negative?

NSTimeInterval expirationTime = (secondsSinceUnixEpoch*1000)+120000;
expirationTime = ceil(expirationTime/2);
int expirationInt = (int)expirationTime;
NSLog(#"%d", expirationInt);
The log output is always negative, even though before I convert it to an int it's positive... I tried just multiplying it by -1 to make it positive again and it's just staying negative! I'm totally perplexed.... don't know much about C, am I just doing something silly??
The number (secondsSinceUnixEpoch*1000)+120000 looks to me like it's going to be way too large to fit in an int. Chances are the integer is overflowing and becoming negative.
Converting to long long is one solution. As you stated in a comment, you want to show a whole number for use in a URL. Just do this:
NSTimeInterval expirationTime = (secondsSinceUnixEpoch*1000)+120000;
expirationTime = ceil(expirationTime/2);
NSString *urlString = [NSString stringWithFormat:#"http://example.com?time=%.0f", expirationTime];
This will format the decimal number as a whole number.

Changing the number of significant digits for a string format with %g

I'm building an app that allows the user to perform some calculations except the calculations result in numbers with lots of decimal digits. It's fine for me to see that kind of precision but I want to let the users be able to choose how many significant digits they want shown. I'm creating a result string using a double and using the %g format shown here:
NSString *resultString = [[NSString alloc] initWithFormat:#"%.14g", result];
I have created a stepper that the users can interact with and storing the number they have chosen in another double. My question is, how can insert that double where the 14 is to change the number of significant digits? Or is this even possible? Please comment if you need clarification.
Any field width or precision in a format can be replaced by an * to indicate a dynamic value which is supplied by an int argument.
For example:
double d = 1.0/7;
for(int i = 4; i < 12; i++)
NSLog(#"%.*g", i, d);
Outputs:
0.1429
0.14286
0.142857
0.1428571
0.14285714
0.142857143
0.1428571429
0.14285714286

Resources