float imprecision in printf / appendFormat - objective-c

I have a float value, that I would like to show it in format string and if it corresponds to an int, showing the integer, if not, showing it with one decimal.
Like this :
3.1
3
2.9
2.8
For now I'm stuck, in the concept, I'd do something like that :
float myFloat = 3.1
float mySecondFloat = 3
[NSString stringWithFormat:#"%g %g", myFloat, mySecondFloat];
My question is:
1/ the type format "%g" works in most cases but sometimes i have result shown like "0.600001" while in reality there should be 0.6 because all I do is 0.7 - 0.1.
Is there a kind of type cast for float at 1 decimal or maybe a bitwise operation to get rid of the final imprecision, or other way to make it works in 100% of cases ?
Thanks for your answers.

If your need absolute precision when working with decimal numbers, you may consider using the NSDecimalNumber class.
Number and Value Programming Topics: Using Decimal Numbers
Otherwise, the %.1g format specifier will be OK.

You have to use this code:
float myFloat = 3.1
float mySecondFloat = 3
[NSString stringWithFormat:#"%.1f %.1f", myFloat, mySecondFloat];
EDIT:
If I really want to print the integer value of a float, i would do it this way (ugly code)
int intValue = myFloatNumber/1;
NSString *string;
if(myFloatNumber == intValue)
{
string = [NSString stringWithFormat:#"%.0f", myFloatValue];
}
else
{
string = [NSString stringWithFormat:#"%.1f", myFloatValue];
}
Doing an integer division by 1, you automatically cast your float to an int.
Once you have the NSString *string you can concat it to your string.

Seeing others answers, it seems that there is no standard C formatter to achieve this.
I went following Nicolas answer (NSDecimalNumber), hence the solve flag. It worked fine indeed, but, in my (very simple) case it might be overkill. Giving it a second thought, if I had to do it again, I think that I would go using only NSNumberFormatter on a NSNumber (using numberWithFloat: method)
If it can help someone..

Related

Xcode Decimal x.x.x

xcode can use Decimal 3 place?
Example:
NSDecimal *test = 1.0.0; //not work
double test = 1.0.0; //not work
float test = 1.0.0; //not work
Can I use decimal 3 place in xcode?
Version numbers like 1.0.3 are neither floating point nor decimal numbers. If you need to store them in a variable, use a string:
NSString *test = #"1.0.0";
Use string and componentsSeparatedByString: method and then convert each object in array to appropriate format.

How to convert NSDecimalNumber to double

I have a value being stored as an NSDecimalNumber and when I convert it to a double it's losing precision.
For the current piece of data I'm debugging against, the value is 0.2676655. When I send it a doubleValue message, I get 0.267665. It's truncating instead of rounding and this is wreaking havoc with some code that uses hashes to detect data changes for a syncing operation.
The NSDecimalNumber instance comes from a third-party framework so I can't just replace it with a primitive double. Ultimately it gets inserted into an NSMutableString so I'm after a string representation, however it needs to be passed through a format specifier of "%.6lf", basically I need six digits after the decimal so it looks like 0.267666.
How can I accomplish this without losing precision? If there's a good way to format the NSDecimalNumber without converting to a double that will work as well.
The NSDecimalNumber instance comes from a third-party framework so I
can't just replace it with a primitive double.
Yes you can. NSDecimalNumber is an immutable subclass of NSNumber, which is a little too helpful when it comes to conversion:
double myDub = [NSDecimalNumber decimalNumberWithDecimal:[[NSNumber numberWithDouble:((double)0.2676655)] doubleValue]];
Ultimately it gets inserted into an NSMutableString so I'm after a
string representation, however it needs to be passed through a format
specifier of "%.6lf", basically I need six digits after the decimal so
it looks like 0.267666.
Double precision unfortunately does not round, but getting a string value that's off by one-millionth is not that big of a deal (I hope):
NSDecimalNumber *num = [NSDecimalNumber decimalNumberWithDecimal:[[NSNumber numberWithDouble:((double)0.2676655)] decimalValue]];
NSString *numString = [NSString stringWithFormat:#"%.6lf", [num doubleValue]];
NSLog(#"%#",numString);
I think that your are on a wrong path and somewhere lost in what to do.
First of all, keep in mind that in objective-c lond double is not supported, so you might better want to use something like %f instead of %lf.
[to be found in the documentation library under "Type encodings" of the objective c runtime programming guide]
Then I would rather expect that the value is show as being truncated, as the doubleValue returns an approximate value but the range you are using is still within the correct range.
You should use a simple formatter instead of moving numbers around, like:
// first line as an example for your real value
NSDecimalNumber *value = [NSDecimalNumber decimalNumberWithString:#"0.2676655"];
NSNumberFormatter *numFmt = [[NSNumberFormatter alloc] init];
[numFmt setMaximumFractionDigits:6];
[numFmt setMinimumFractionDigits:6];
[numFmt setMinimumIntegerDigits:1];
NSLog(#"Formatted number %#",[numFmt stringFromNumber:value]);
This has another benefit of using a locale aware formatter if desired. The result of the number formatter is the desired string.

NSNumberFormatter rounding to negative zero

I'm using NSNumberFormatter to format float values as integer strings, i.e. omitting the fractional part. I find it odd, that numbers in range (-0.5, 0) *open interval end up as -0. As this value will be displayed to the user, I think that negative zero is not appropriate. I have experimented with various combinations of numberStyle and roundingMode without success.
Is there a way to configure the NSNumberFormatter to output them as 0, or do I have to resort to manual correction for that range?
I had to do correct this myself. I am using the NSNumberFormatter to display temperature with default numberStyle — NSNumberFormatterNoStyle which rounds the numbers to the whole integer, roundingMode set to NSNumberFormatterRoundHalfUp. In the end I did intercept the values in problematic range and round it myself:
- (NSString *)temperature:(NSNumber *)temperature
{
float f = [temperature floatValue];
if (f < 0 && f > -0.5)
temperature = [NSNumber numberWithLong:lround(f)];
return [self.temperatureFormatter stringFromNumber:temperature];
}
No, there is no way to configure it to do that.
In "10.4 mode", NSNumberFormatter basically just wraps CFNumberFormatter (although it's not a direct toll-free-bridged wrapping). You can look at the list of Number Formatter Property Keys and it's pretty clear that there's nothing that will do what you want. (It may be possible in "10.0" mode; it would take a bit of trial and error to find out. But I doubt you want to use that.)
So pre-rounding (as Justin Boo suggests) is probably your best option.
You could, of course, post-process instead. Exactly what you want to do probably depends on whether you want to also render -0.00 as 0.00, what you want to happen for localizations that don't use "-0", etc. The simplest case would be as simple as this:
#interface NSNumberFormatter (NegativeZero)
- (NSString *)stringFromNumberNoNegativeZero:(NSNumber *)number;
#end
#implementation NSNumberFormatter (NegativeZero)
- (NSString *)stringFromNumberNoNegativeZero:(NSNumber *)number {
NSString *s = [self stringFromNumber:number];
if ([s isEqualToString:#"-0"]) return #"0";
return s;
}
#end
But if you want anything more complicated, it'll get more complicated.

Objective c: Parsing string to float, avoid extra decimals

Im converting some information Im receiving by a string to a float to get the sum of all of them.
The problem is when I convert the float, for example:
myString = #"13502.63"
float *f = [myString floatValue];
NSLog(#"Converted %f", f);
The result of "f" is 13502.629883
This thing is ok for some values, but when I have to add a big amount of this values, these extra decimals make the result incorrect.
Could anybody help me, please?
Thanks
If you want accuracy you should not use float. Use NSDecimalNumber.
NSString *myString = #"13502.63";
NSDecimalNumber *number = [NSDecimalNumber decimalNumberWithString:myString];
Unfortunately all floating point types in any language will have this problem, as they have to convert into an underlying binary integer format.
Have you considered using NSDecimalNumber?
These will be much slower than a float, but if that is not a problem, then they are much more accurate for such calculations.
If you need speed for some reason, would a double or long-double be accurate enough?
float numbers have no exact representation, that is the reason why "13502.63" is converted to 13502.629883; this is the closest float to the original number.
So, I don't think there is an easy solution with float. You should try NSDecimalNumber. I don't know about the performance, but it should give you an exact representation.

How to Turn Number into a String with Format?

Say I have a number, double, or NSNumber, 3.333333
I want that to turn that into #"3.3"
How would I do so?
Can I do NSString stringWithFormat? But what's the format?
NSString *str = [NSString stringWithFormat:#"%.1f", yourDouble];
or for NSNumber:
NSString *str = [NSString stringWithFormat:#"%.1f", [yourNSNumber doubleValue]];
0.0f means the amount of digits before and after the decimal. So #Wevah's answer would be correct, but keeping that in mind will save you time in the future.
%f stands for a float variable which I am sure you understand. What you would need to display this is a float variable because it contains a decimal. Like people have stated before, you will need to use %.1f
The % just tells the compiler that a special character is coming up.
The f tells the compiler that it is a float variable.
The .1 tells the compiler how many decimal places your float variable is to have. If you would want to have 6 decimal places, then you would us %.6f
Yes, you will want to use string with format.
Say you have a UILabel, then you will want to say
theLabel'sName.text = [NSString stringWithFormat:#"%.1f", ((float)int1 / int2)];
You need the (float) to tell the compiler that whatever int1 / int2 is, is a float variable.
If your NSNumber instance is called myNumber then do
[myNumber stringValue];