How to prevent rounding up float numbers in Objective-C? - objective-c

Maybe this is a dumb question, but I really can't get it. For some reason float values in my program get rounded up during the assignment statement.
Here is the code:
Float64 time,test;
for( Shot *sh in _arrayOfShots)
{
time = sh.bFrameTime;
// right here time variable gets value rounded up to 2 decimal places
// for example: if sh.bFrameTime = 81.919998 => time = 81.92
NSNumber *num = [NSNumber numberWithFloat:time];
test = [num floatValue];
[edgeTime addObject:num];
}
// this is my Shot object structure
#interface Shot : NSObject
{
int bFrame;
int eFrame;
Float64 bFrameTime;
}
If anyone knows how to deal with this, please give me hint!
Thank you!

81.919998 => time = 81.92
is not "rounded up to two decimal places". The difference in the two is 0.000002, a change in magnitude of 0.0000000244, or about 8 decimal places. If your source value is single-precision float then it only has 7 decimal places of accuracy. (But you didn't show the declaration of that so we can't tell.)
The difference could also have to do with how you displayed the two values, as NSLog does a modicum of rounding.

Add an if statement where you check if the rounded number is larger than the initial number, if so subtract it by 1

Related

Count number of arbitrary repeating decimal numbers in NSString

In my code, I'm dealing with an NSString that contains an NSNumber value. This NSNumber value could possibly be a repeating decimal number (e.x. 2.333333333e+06) that shortens to "2.333333" in a string format. It could also be a terminating number (e.x. 2.5), negative, or irrational number (2.398571892858...) (only dealing with decimals here)
I need to have a way to figure out if there are the repeating numbers in the string (or the NSNumber, if necessary). In my code, I would have no way to know what the repeating number would be, as it's a result of computations started by the user. I have tried this for loop (see below) that doesn't work the way I want it to, due to my inexperience with string indexing/ranges/lengths.
BOOL repeat = NO; //bool to check if repeating #
double repNum, tempNum; //run in for loop
NSString *repeating = [numVal stringValue]; //string that holds possible repeating #
for (int i = 3; i <= [repeating length]-3; i++) { //not sure about index/length here
if (i == 3) {
repNum = [repeating characterAtIndex:i];
}
tempNum = [repeating characterAtIndex:i];
if (tempNum == repNum) {
repeat = YES;
} else {
repeat = NO;
}
}
This code doesn't work as I'd like it to, mainly because I also have to account for negative dashes in the string and different amounts of numbers (13 1/3 vs. 1 1/3). I've used the modffunction to separate the integers from the decimals, but that hasn't worked well for me either.
Thank you in advance. Please let me know if I can clarify anything.
EDIT:
This code works with the finding of different solutions for polynomials (quadratic formula). Hope this helps put it into context. See here. (Example input)
NSNumber *firstPlusSolution, *secondMinusSolution;
NSString *pValueStr, *mValueStr;
firstPlusSolution = -(b) + sqrt(square(b) - (4)*(a)*(c)); //a, b, c: "user" provided
firstPlusSolution /= 2*(a);
secondMinusSolution = -(b) - sqrt(square(b) - 4*(a)*(c));
secondMinusSolution /= 2*(a);
pValueStr = [firstPlusSolution stringValue];
mValueStr = [secondMinusSolution stringValue];
if ([NSString doesString:pValueStr containCharacter:'.']) { //category method I implemented
double fractionPart, integerPart;
fractionPart = modf(firstPlusSolution, &integerPart);
NSString *repeating = [NSString stringWithFormat:#"%g", fractionPart];
int repNum, tempNum;
BOOL repeat = NO;
//do for loop and check for negatives, integers, etc.
}
if ([NSString doesString:mValueStr containCharacter'.']) {
//do above code
//do for loop and check again
}
Use C. Take the fractional part. Convert to a string with a known accuracy. If length of string indicates that last digits are missing, then it does not repeat. Use NSString-UTF8String to convert a string. Get rid of the last digit (may be rounding or actual floating point arithmetic error). Use function int strncmp ( const char * str1, const char * str2, size_t num ) to perform comparison within the string itself. If the result is 8 characters long and the last 2 characters match the first 2 characters, then shall the first 6 characters be considered repeating?
Assuming that fraction knowledge your desire:
• Possibility 1: Use fractions. Input fractions. Compute with fractions. Output fractions. Expand upon one of the many examples of a c++ fraction class if necessary and use it.
• Possibility 2: Choose an accuracy which is much less than double. Make a fraction from the result. Reduce the fraction allowing rounding based upon accuracy.
I suggest use not optimal but easy to write solution
Create NSMutableDictionary that will contain number as key and count of occurrence as value.
You can use componentsSeparatedByString: if numbers in string delimited by known symbol
In loop check valueForKey in dictionary and if need increase value
Last step is analyzing our dictionary and do anything you need with numbers

Simple division not happening

I'm trying to calculate the appropriate height for a UITableViewCell and a UIImageView by using the following method that gets called:
-(void)ratioCalculator
{
picH = image.size.height;
picW = image.size.width;
NSLog(#"%d = width %d = height", picW, picH);
picRatio = (picW/picH);
NSLog(#"%F", picRatio);
imageViewH = (260/picRatio);
NSLog(#"%d int", imageViewH);
return;
}
The thing is, when it gets called, the picRatio = (picW/picH); doesn't seem to be happening as the console says this:
2012-11-18 21:56:48.787 Name[5374:c07] 640 = width 360 = height
2012-11-18 21:56:48.788 Name[5374:c07] 1.000000
2012-11-18 21:56:48.788 Name[5374:c07] 260 int
The 1.000000 is the float picRatio and the 260 is the imageViewH. Obviously this means the incorrect heights are used for both the UITableViewCell and the UIImageView which are calculated like this:
else {
[self ratioCalculator];
return (imageViewH + 20);
}
and
- (void)showImage:(UIImage *)theImage
{
self.imageView.image = theImage;
self.imageView.hidden = NO;
[self ratioCalculator];
self.imageView.frame = CGRectMake(10, 10, 260, imageViewH);
self.photoLabel.hidden = YES;
}
As stupid a question as this probably is, why isn't this simple division working? Again, sorry if this is a stupid question, but it has me stumped. I feel like I'm going slightly mad.
Any help would be greatly appreciated,
Regards,
Mike
picRatio = ((float)picW/(float)picH);
Objective-c needs the proper iVar type assignment before dividing.
When you divide 2 integers, the output isn't a float, but an integer.
So 640/360 = 1.777 and this is treated as an integer, so the decimal value is ignores, which gives you the 1, you are getting.
If you do what I wrote, will actually produce a float, and give you the 1.777 output.
What you are missing is to cast the integers to float in order to make a float division
picRatio = (picW/picH); // gives an int division if picW and picH are declared integers
picRatio = (float)(picW)/picH; // gives you a floating point division
Since no one is posting an official answer, I thought I'd do so to help anyone in a similar position. The comments on my question put me on the right track, and after further investigations I discovered that the compiler calculates the output based on the types of the operands. Therefore, if all the operands are ints, it casts an int to the destination, regardless of if it's a float or not. Solution: make at least one of your operands a float.
Hope this helps anyone in a similar position.

Floating Point things work.....Most of the time [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Correcting floating point numbers
float randNum = arc4random()%(100)-1;
I read somewhere that this will give me random numbers between 1-100. Or something close to that.
This line seems to work all the time, but I convert this number to an NSString for storage/printing to text, and then convert it back to a float for ordering and other calculations. But when I get that float back sometimes it looks something like gibberish; like this in the variable view:
9 float 9.75303731e-41
Why would converting to an NSString and back to a float ruin the float value I'm using? (e.g. What could I have screwed up? Or should I be using CGFloat instead?)
I think this is all the pertinent code:
NSMutableArray *stringArray = [[NSMutableArray alloc] init];
floatArray[30];
// put three random floats into an NSMutableArray of NSStrings
for(int i = 0; i < 3; i++)
{
float randNum = arc4random()%(100)-1;
NSString *randString = [NSString stringWithFormat:#"%.3f", randNum];
[stringArray addObject:randString];
}
// convert NSStrings back to float
for(NSString *string in stringArray)
{
float temp = [string floatValue];
floatArray[iterator] = temp;
}
Thanks in advance for any help/advice.
EDIT: When I step through the code it looks like the float value looks sane until the line "float temp = [string floatValue]", which is where the value seems to be garbaged.
Why are you using float when the result from arc4random() is a uint32_t? Switching to integer types would almost certainly get around all this, as I suspect the problem is because of the conversion to string form allowing only 3 significant digits. What happens if you use %.15f as your format?
Sidenote: use arc4random_uniform() - it's simpler and guaranteed to be a uniformly random distribution within that range.

iOS: NSString variable returning null

I am probably missing something obvious (still learning about Objective-C!) but for some reason one of my NSString variables has a null value in my if statement and I don't know why?
I have even output to NSLog and I still can't see why it's behaving like this.
Basically, the user enters an amount in a text field (itemWeight) and this if statement validates the input and displays an alert according to the result. The problem only seems to be when 0.751 is entered, if you enter any other amount (0.750, 0.749, 0.752, 0.753 and so on) it works as expected.
Relevant code samples as follows...
.h file:
#property (strong, nonatomic) IBOutlet UITextField *itemWeight;
.m file:
NSString *rawWeightText = itemWeight.text;
float convertedWeightText = rawWeightText.floatValue;
NSString *weightMessage;
if (convertedWeightText <= 0.750)
{
weightMessage = #"under 0.750";
}
else if (convertedWeightText >= 0.751)
{
weightMessage = #"0.751 or over";
}
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle: #"Error"
message: weightMessage
delegate: nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
Any ideas where I'm going wrong/what I've forgotten to do would be much appreciated, thank you!
Looking at the condition of your if-else if code does not have the range of from 0.750 to 0.751.
floating-point values ​​must be careful to compare. you should consider about 0.751 possibility 0.75099999...
the following loop, the result is 99.999046, not 100. Continue to add more accuracy is poor.
float a= 0.1f;
float result = 0.f;
for(int i = 0; i<1000; i++)
{
result += 0.1f;
}
printf("result:%f", result); //99.999046
So, In General, Comparison of these expressions is not recommended.
if (result == expectedResult)
Writing the following method is recommended to compare.
bool AlmostEqualRelative(float A, float B, float maxRelativeError)
{
if (A == B)
return true;
float relativeError = fabs((A - B) / B);
if (relativeError <= maxRelativeError)
return true;
return false;
}
For more information, please read here
http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm
also, you must read this wiki:
http://en.wikipedia.org/wiki/Floating_point
http://en.wikipedia.org/wiki/Denormal_number
Decimal values often can't be exactly represented in binary. When the user enters 0.751, its likely that the actual float that you get back is slightly smaller, like 0.750999942. Try logging the value to see what's going on.
You are not the first programmer who is surprised by the actual behavior of floating point numbers.
In fact it is more a problem with binary decimal numbers, then the floating point details.
Try to convert 0.751, which has only 3 digits in the fractional part, into a binary number and you'll see.
To make it short, 0.751 is represented as a binary exponent of -1 and a mantissa of 1.10000000100000110001001 in a float (float only has 23+1 bits of precision for the mantissa).
That is, in decimal, an exponent of -1 and a mantissa of 1.5019999742507935.
Therefore 2^-1 * 1.5019999742507935 = 0.7509999871254 and it does not pass the test in the if.

Converting NSString to a decimal

I need to change the code below to make "intAmount" a decimal or an integer (i.e. a person can enter .10 or 1) in my uitextfield. The last line "myProduct" has to be a decimal not an integer and return the product in the format "18.00" for example. Can someone help someone help me alter my code snippit for this?
//amt has to be converted into a decimal value its a NSString now
NSInteger intAmount = [amt intValue];
//where total gets updated in the code with some whole (integer) value
NSInteger total=0;
//Change myProduct to a decimal with presicion of 2 (i.e. 12.65)
NSInteger myProduct=total*intAmount;
THIS DOESN'T WORK
NSDecimalNumber intAmount = [amt doubleValue];
//Keep in mind totalCost is an NSInteger
NSDecimalNumber total=totalCost*intAmount;
Use doubleValue instead of intValue to get the correct fractional number out of your text field. Put it in a variable of type double rather than NSInteger. Then use the format %.2g when you print it out and it will look like you want it to.
If you need to track decimal values explicitly, you can use NSDecimalNumber. However, if all you're doing is this one operation, Carl's solution is most likely adequate.
If you have a string representation of a real number (non-integer), you can use an NSScanner object to scan it into a double or float, or even an NSDecimal structure if that is your true intention (the NSDecimal struct and NSDecimalNumber class are useful for containing numbers that can be exactly represented in decimal).
NSString *amt = #"1.04";
NSScanner *aScanner = [NSScanner localizedScannerWithString:amt];
double theValue;
if ([aScanner scanDouble:&theValue])
{
// theValue should equal 1.04 (or thereabouts)
}
else
{
// the string could not be successfully interpreted
}
The benefit to using a localised NSScanner object is that the number is interpreted based on the user's locale, because “1.000” could mean either one-thousand or just one, depending on your locale.