Objective-C: Flooring to 3 decimals correctly - objective-c

I am trying to floor a float value to the third decimal. For example, the value 2.56976 shall be 2.569 not 2.570. I searched and found answers like these:
floor double by decimal place
Such answers are not accurate. For example the code:
double value = (double)((unsigned int)(value * (double)placed)) / (double)placed
can return the value - 1 and this is not correct. The multiplication of value and placed value * (double)placed) could introduce something like: 2100.999999996. When changed to unsigned int, it becomes 2100 which is wrong (the correct value should be 2101). Other answers suffer from the same issue. In Java, you can use BigDecimal which saves all that hassels.
(Note: of course, rounding the 2100.9999 is not an option as it ruins the whole idea of flooring to "3 decimals correctly")

The following code should work:
#include <stdio.h>
#include <math.h>
int main(void) {
double value = 1.23456;
double val3;
val3 = floor(1000.0 * value + 0.0001) * 0.001; // add 0.0001 to "fix" binary representation problem
printf("val3 is %.8f; the error is %f\n", val3, 1.234 - val3);
}
this prints out
val3 is 1.23400000; the error is 0.000000
If there are any residual errors, it comes about from the fact that floating point numbers cannot necessarily be represented exactly - the idea behind BigDecimal and things like that is to work around that in a very explicit way (for example by representing a number as its digits, rather than a binary representation - it's less efficient, but maintains accuracy)

I had to consider a solution involving NSString and it worked like a charm. Here is the full method:
- (float) getFlooredPrice:(float) passedPrice {
NSString *floatPassedPriceString = [NSString stringWithFormat:#"%f", passedPrice];
NSArray *floatArray = [floatPassedPriceString componentsSeparatedByString:#"."];
NSString *fixedPart = [floatArray objectAtIndex:0];
NSString *decimalPart = #"";
if ([floatArray count] > 1) {
NSString *decimalPartWhole = [floatArray objectAtIndex:1];
if (decimalPartWhole.length > 3) {
decimalPart = [decimalPartWhole substringToIndex:3];
} else {
decimalPart = decimalPartWhole;
}
}
NSString *wholeNumber = [NSString stringWithFormat:#"%#.%#", fixedPart, decimalPart];
return [wholeNumber floatValue];
}

For example, the value 2.56976 shall be 2.569 not 2.570
Solution is has simple as that :
double result = floor(2.56976 * 1000.0) / 1000.0;
I don't know why you search complication... this works perfectly, doesn't need to pass by some unsigned int or other + 0.0001 or whatever.
Important note :
NSLog(#"%.4f", myDouble);
Actually do a round on your variable. So it's improper to believe you can floor with a %.Xf

Related

Round double in Objective-C

I want to round a double to one decimal place in Objective-C.
In Swift I can do it with an extension:
public extension Double {
/// Rounds the double to decimal places value
func rounded(toPlaces places:Int) -> Double {
let divisor = pow(10.0, Double(places))
return (self * divisor).rounded() / divisor
}
}
However, apparently you cannot call extensions on primitives from Objective-C so I can't use the extension.
I would be happy to do the rounding either on the double directly or as a string, however, neither of the following is working:
double mydub = 122.12022222223322;
NSString *axtstr = [NSString stringWithFormat:#"%2f", mydub]; //gives 122.120222
double rounded = (round(mydub*10)) / 10.0; //gives 122.100000
How do I convert 122.12022222223322; into 122.1?
You need to put a decimal between the % and 2f
[NSString stringWithFormat:#"%.2f", mydub];
double mydouble = 122.12022222223322;
NSString *str = [NSString stringWithFormat:#"%.2f", mydouble];
// = #"122.12"
.. will not round mydouble. Instead it will only apply format to the output as string.
double d = 122.49062222223322;
NSString *dStr = [NSString stringWithFormat:#"%.f %.1f %.2f %.3f", d, d, d, d];
// = #"122 122.5 122.49 122.491"
As Objective-C shares the language rules from C you can round safely with
#include <math.h>
double rounded = round(mydouble);
// = 122.000000
of course you can shift comma with multiplication and dividing the power of ten you want.
double commashifted = round(mydouble*100.0)/100.0;
// = 122.120000;
If you are really into Objective-C Classes to do same in deluxe have a look into 'NSDecimal.h' in the Foundation Framework.
Last but not least you can do the same with C as you did with swift.
double roundbycomma(int commata, double zahl) {
double divisor = pow(10.0, commata);
return round(zahl * divisor) / divisor;
}

Reverse a double value

I'm trying to reverse a double value like this:
Input: 1020304050...... Output: 5040302010
the group of 2 digits remain in the same order. So 10 doesn't become 01. or 53 doesn't become 35.
The input will always have even number of digits, so pairing isn't an issue.
The 2 adjacent digits are actually a code number for a function. And I want to maintain that so I can apply that function again.
double temp = 0.0;
double Singlefilter=1;
double reverseString=0;
temp=filtersequence;
while (temp>0)
{
Singlefilter=fmod(temp, 100.00);
temp=temp/100;
reverseString=(reverseString+Singlefilter)*100;
NSLog(#"reversed string of filter %f",reverseString);
}
But I have no idea why this isn't working. This is generating randomly very very big values.
[This question has been replaced by Reverse a double value while maintaining 2 adjacent digits in same format]
You can do it like this:
#import <Foundation/Foundation.h>
#include "math.h"
void outputGroupReversed(double filtersequence)
{
double reverseString = 0.0;
double temp = filtersequence;
while (temp > 0.0)
{
double groupMultiplier = 100.0;
double singleFilter = fmod(temp, groupMultiplier);
temp = floor(temp / groupMultiplier);
reverseString = reverseString * groupMultiplier + singleFilter;
}
NSLog(#"reversed string of filter %f", reverseString);
}
int main(int argc, const char * argv[])
{
#autoreleasepool {
outputGroupReversed(1020304050.0);
}
return 0;
}
This code does not handle input with a fractional part correctly, though.
You're better off just converting it to a string and reversing it.
NSString *inputString = [#(input) stringValue]; // Eg 1230
NSMutableString *reversedString = [NSMutableString string];
NSInteger charIndex = [inputString length];
while (charIndex > 0) {
charIndex--;
NSRange subStrRange = NSMakeRange(charIndex, 1);
[reversedString appendString:[inputString substringWithRange:subStrRange]];
}
double result = [reversedString doubleValue]; // Eg 0321 -> 321
// Go back to NSString to get the missing length
NSString *resultString = [#(result) stringValue]; // Eg. 321
// Multiple by factors of 10 to add zeros
result *= exp(10, [inputString length] - [resultString length]); // 3210
NSLog(#"reversed value %f", result);
Reverse string method from this answer.
If you wish to store 2-digit decimal integers packed together as a single numeric value you would be better of using uint64_t - unsigned long 64 bit integers rather than double. That will store 9 pairs of two decimal digits precisely which appears to be one more than you need (you mention 16 digits in a comment).
As long as the numeric value of the packed pairs is not important, just that you can pack 8 2-digit decimal numbers (i.e. 0 -> 99) into a single numeric value, then you can do better. A 64-bit integer is 8 pairs of 2-hexadecimal digit numbers, or 8 8-bit bytes, so you can store 8 values 0 -> 99 one per byte. Now adding and extracting values becomes bit-shifts (>> & << operators) by 8 and bitwise-or (|) and bitwise-and (&). This at least makes it clear you are packing values in, which divide & remainder do not.
But there is another payoff, your "reverse" operation now becomes a single call to CFSwapInt64() which reverses the order of the bytes.
However having said the above, you really should look at your model and consider another data type for what you are doing - long gone are the days when programs had to pack multiple values into words to save space.
For example, why not just use a (C) array of 8-bit (uint8_t values[8]) integers? If you require you can place that in a struct and pass it around as a single value.

Converting decimal number to binary Objective-C

Hi I have made an IOS app that converts binary, hexadecimal and decimal values. It all works fine except for my decimal to binary conversion. Here is what I have. It returns 0s and 1s but far too many. Can anyone tell me why this is or help me with a better method?
NSString *newDec = [display text]; //takes user input from display
NSString *string = #"";
NSUInteger x = newDec;
int i = 0;
while (x > 0) {
string = [[NSString stringWithFormat:#"%u", x&1] stringByAppendingString:string];
x = x>> 1;
++i;
}
display.text = string; //Displays result in ios text box
Try this:
NSUInteger x = [newDec integerValue];
And next time don't ignore the Compiler's "Incompatible pointer to Integer conversion" hint...
Explanation: Afaik, assigning an object to an int, actually assigns the address of the object to that integer, not the content of the string (which is what you want).

How to get a CGFloat from a NSString?

In my current app, I have an equation that typically solves to a pretty long amount of decimal numbers, i.e: 0.12345 or .123 . But the way that I need this to work is to only show say 1 or 2 decimal numbers, so that would essentially produce 0.12 or 0.1 based on the values I mentioned.
In order to do this, I have done the following: Taken my CGFloat to a NSString:
CGFloat eX1 = 0.12345
NSLog(#" eX1 = %f", eX1); //This of course , prints out 0.12345
NSNumberFormatter *XFormatter = [[NSNumberFormatter alloc] init];
Xformatter.numberStyle=NSNumberFormatterDecimalStyle;
Xformatter.maximumFractionDigits=1;
NSString *eX1F = [formatter stringFromNumber:#(eX1)];
NSLog (#"eX1F = %#",eX1F); //This prints out 0.1
But my problem is that I need to keep working with this as a CGFloat after it has been formatted, I have tried taken the string back to a number by doing: numberFromString but the problem is that only works with a NSNumber.
What can I do to format my CGFloat and keep working with it as a CGFloat and not a NSString or NSNumber?
Update I have tried:
float backToFloat = [myNumber floatValue];
but the result is number unformatted : 0.10000 I need those extra 0s out
To convert NSString to a CGFloat you can use floatValue:
CGFloat *eX1rounded = [eX1F floatValue];
But you can round eX1 to eX1rounded directly without using a number formatter,
for example:
CFGloat *eX1rounded = roundf(eX1F * 10.0f)/10.0f;
In any case, you should keep in mind that numbers like 0.1 cannot be represented
exactly as a binary floating point number.
Use stringWithFormat: to round and convert to a string for display purposes:
NSString* str = [NSString stringWithFormat:#"%0.2f", eX1];
You can do the same thing in NSLog, the %0.2f says you want 2 decimal places.
NSLog(#" eX1 =%0.2f", eX1); // this prints "0.12"

Positive/Negative button not displaying properly

I am trying to add a positive/negative button onto a numerical input in a UItextfield, but I cannot get it to function properly. What I want it to do is just add or remove a negative sign from the front of the numerical input. I am able to do that, however I cannot find a method to maintain the original number of decimal places. This is what I have tried:
- (IBAction) negsign
{
float input = [userinput.text floatValue];
float result = ((input * (-1)));
negstring = [NSString stringWithFormat:
#"%f", result];
userinput.text = negstring;
}
With this I get just a string of zeros after, like -23.0000000. I've tried limiting the decimal places by changing to #"%.2f" but I dont want extra zeros for whole integers, or rounding more than 2 decimals places. I just want it to take something like 34.658939 or 23 and make it -34.658939 or -23. Does anyone have a method to do this?
What would work best in your case is the following code:
float input = [userinput.text floatValue];
float result = ((input * (-1)));
NSNumber *resultNum = [NSNumber numberWithFloat:result];
NSString *resultString = [resultObj stringValue];
userinput.text = resultString;
If you're trying to make the number negative instead of reversing the sign, it'd be better if you replace float result = ((input * (-1))); with float result = -ABS(input);
Really, the best way to handle this would be to never convert it from a string in the first place. Just replace the first character as needed like this:
- (IBAction) negsign
{
unichar firstCharacter = [userinput.text characterAtIndex:0];
if (firstCharacter == '-') {
// Change the first character to a + sign.
userinput.text = [userinput.text stringByReplacingCharactersInRange:NSMakeRange(0, 1)
withString:#"+"];
} else if (firstCharacter == '+') {
// Change the first character to a - sign.
userinput.text = [userinput.text stringByReplacingCharactersInRange:NSMakeRange(0, 1)
withString:#"-"];
} else {
// There is no sign so we assume that it is positive.
// Insert the - at the beginning.
userinput.text = [userinput.text stringByReplacingCharactersInRange:NSMakeRange(0, 0)
withString:#"-"];
}
}