NSNumber's stringValue print other strings what we don't want - nsnumber

(lldb) po [#(70.033) stringValue]
70.033
(lldb) po [#(80.138) stringValue]
80.13800000000001
(lldb) po [#(70.138) stringValue]
70.13800000000001
(lldb) po [#(100.01) stringValue]
100.01
(lldb) po [#(90.03) stringValue]
90.03
(lldb) po [#(90.01) stringValue]
90.01000000000001
(lldb) po [#(900.01) stringValue]
900.01
(lldb) po [#(100.01) stringValue]
100.01
(lldb) po [#(80.01) stringValue]
80.01000000000001
Is this a bug form the NSNumber? Or a mistake with my Mac?

No, it is not a bug. It is just the standard issue with the internal representation of numbers.
So (in Swift):
var a = 0.15 + 0.15
var b = 0.1 + 0.2
print (a == b) // can be false!
print (a >= b) // can also be false!
var c = 80.138
print(c) // 80.138
print (c - 80.0 - 0.138) // 5.218048215738236e-15
There are many people observing this "strang" behaviour, in almost any programming language. Just check some web sites like stackoverflow.com:
strange output in comparison of float with float literal
or, with nice images:
https://bitbashing.io/comparing-floats.html

Related

LLDB convenience variable object assignment is nil?

Ran into this phenomenon while debugging something else and am curious:
(lldb) po (NSException *)($eax)
$3 = 0x0d16c510 Test exception message
(lldb) expr NSException *$exception = (NSException *)($eax)
(lldb) po $exception
$exception = 0x00000000 <nil>
I'm running an iOS app in the 6.1 Simulator. Why would assigning a convenience variable give me nil when referencing $eax directly gives me an object?
I tried to reproduce this with Xcode 4.6.2 but could not. I created a template iOS app and launched it in the simulator. I didn't bother getting to a place where I had an exception in a register - I but the register value is the important thing here.
(lldb) reg read eax
eax = 0x10004005
(lldb) p (NSException*) ($eax)
(NSException *) $0 = 0x10004005 <not an Objective-C object>
(lldb) expr NSException*$exception = (NSException *)($eax)
(lldb) p $exception
(NSException *) $exception = 0x10004005 <not an Objective-C object>
(lldb)
In your example $exception had a value of 0x0 but when I tried this with 4.6.2, it looks like it is working. What version of Xcode were you using?

Multiplying double variable by double in Xcode

I'm making in app that includes a user entering the price of an object, and the app outputs the price including tax. However, every time it outputs 0, and I get a run-time error involving the variable, myDouble. I'm trying to take the value put in a text field and multiplying it by 1.06 (I'm starting out with a set tax rate), and then setting a label to the new value. Here is my code:
-(IBAction)textFieldReturn:(id)sender
{
[sender resignFirstResponder];
NSString *myString = inputtext2.text;
double myDouble = [myString doubleValue];
myDouble = myDouble*1.06;
NSLog(#"myDouble: %lf", myDouble);
price.text = [NSString stringWithFormat:#"%g", myDouble];
}
Most likely your inputtext2 variable is nil, so it is returning nothing. If this is an outlet, check the connections in interface builder. If the outlet is not connected, the variable will be nil, so the string value will be nil, so the double value will be 0.
I think you are going to want to use a NSNumberFormatter and check for valid entry.
pseudo code, uncompiled:
-(IBAction)textFieldReturn:(id)sender
{
[sender resignFirstResponder];
NSString *myString = inputtext2.text;
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setNumberStyle: NSNumberFormatterDecimalStyle];
NSNumber *myNumber = [formatter numberFromString: myString];
double myDouble = 0;
if (myNumber != nil) {
myDouble = [myNumber doubleValue];
}
NSString *outputString = [formatter stringFromNumber:myDouble];
price.text = outputString;
}
This should default your number to 0 if the text entered is 0 or not a valid number.

Cocos2D: Passing a CGPoint through CCCallFuncND

What is the appropriate way to call a method with an action, and what should the method itself look like for passing a CGPoint parameter? I've tried to look up examples online without much luck, so I've been pretty much guessing.
What I have tried is this for calling it:
CGPoint spriteCoord = saveStation.sprite.position;
id a1=[CCMoveTo actionWithDuration:.4 position:ccp(saveStation.sprite.position.x,saveStation.sprite.position.y)];
id actionSaveStationReaction = [CCCallFuncND actionWithTarget:self selector:#selector(saveStationReaction : data:) data:&spriteCoord];
[hero.heroSprite runAction:[CCSequence actions:a1, actionSaveStationReaction, nil]];
And the method itself:
-(void) saveStationReaction:(id)sender data:(void *)data {
CGPoint spriteCoord = (void *)data; //error: Invalid initializer
NSLog(#"spriteCoord x = %f", spriteCoord.x);
NSLog(#"spriteCoord y = %f", spriteCoord.y);
}
The proper way to send a CGPoint (or any non-id type like C structs) to a method that takes an id as parameter (any method that uses performSelector) is by wrapping it in an NSValue object:
NSValue* value = [NSValue valueWithBytes:&spriteCoord objCType:#encode(CGPoint)];
In the method that is being called you can retrieve the point from the NSValue object by casting the data pointer to NSValue* and calling getValue:
-(void) saveStationReaction:(id)sender data:(void *)data {
CGPoint spriteCoord;
[((NSValue*)data) getValue:&spriteCoord];
NSLog(#"spriteCoord x = %f", spriteCoord.x);
NSLog(#"spriteCoord y = %f", spriteCoord.y);
}
GamingHorror's suggestion on wrapping the CGPoint in an NSValue is spot-on.
But there's a simpler way than using valueWithByte:objCType: method: valueWithCGPoint:, assuming you are coding for iOS and not MacOS.
NSValue *spriteCoordValue = [NSValue valueWithCGPoint:spriteCoord];
[CCCallFuncND actionWithTarget:self selector:#selector(saveStationReaction:data:) data:spriteCoordValue];
// and ..
-(void) saveStationReaction:(id)sender data:(void *)data {
CGPoint spriteCoord = [(NSValue *)data CGPointValue];
NSLog(#"spriteCoord x = %f", spriteCoord.x);
NSLog(#"spriteCoord y = %f", spriteCoord.y);
}
NSValue can also deal with CGSize and CGRect using similar way.
You can't typecast to a CGPoint with that syntax. Try....
CGPoint *spriteCoord = data;
CGFloat ptX = spriteCoord->x;
CGFloat ptY = spriteCoord->y;
I tried...
CGPoint* spriteCoord = (CGPoint)data;
which didn't work and I guess expectedly so. Try my first suggestion and see if that works for you. It did compile for me but I'm not sure how it will execute and that may depend on your particular situation.
CGPoint is a struct, not an object, so you can't pass it directly to any of the CCCallFunc's. There are several ways of dealing with this, but the quickest converts the CGPoint to NSString using NSStringFromCGPoint, passing the string, then converting it back to a CGPoint using CGPointFromString.

NSTask Output Formatting

I'm using an NSTask to grab the output from /usr/bin/man. I'm getting the output but without formatting (bold, underline). Something that should appear like this:
Bold text with underline
(note the italic text is actually underlined, there's just no formatting for it here)
Instead gets returned like this:
BBoolldd text with _u_n_d_e_r_l_i_n_e
I have a minimal test project at http://cl.ly/052u2z2i2R280T3r1K3c that you can download and run; note the window does nothing; the output gets logged to the Console.
I presume I need to somehow interpret the NSData object manually but I have no idea where to start on that. I'd ideally like to translate it to an NSAttributedString but the first order of business is actually eliminating the duplicates and underscores. Any thoughts?
What is your actual purpose? If you want to show a man page, one option is to convert it to HTML and render it with a Web view.
Parsing man’s output can be tricky because it is processed by groff using a terminal processor by default. This means that the output is tailored to be shown on terminal devices.
One alternative solution is to determine the actual location of the man page source file, e.g.
$ man -w bash
/usr/share/man/man1/bash.1.gz
and manually invoke groff on it with -a (ASCII approximation) and -c (disable colour output), e.g.
$ gunzip -c /usr/share/man/man1/bash.1.gz | groff -c -a -Tascii -man
This will result in an ASCII file without most of the formatting. To generate HTML output,
$ gunzip -c /usr/share/man/man1/bash.1.gz | groff -Thtml -man
You can also specify these options in a custom configuration file for man, e.g. parseman.conf, and tell man to use that configuration file with the -C option instead of invoking man -w, gunzip, and groff. The default configuration file is /private/etc/man.conf.
Also, you can probably tailor the output of the terminal device processor by passing appropriate options to grotty.
Okay, here's the start of my solution, though I would be interested in any additional (easier?) ways to do this.
The output returned from the Terminal is UTF-8 encoding, but the NSUTF8StringEncoding doesn't interpret the string properly. The reason is the way NSTask output is formatted.
The letter N is 0x4e in UTF-8. But the NSData corresponding to that is 0x4e 0x08 0x4e. 0x08 corresponds to a Backspace. So for a bold letter, Terminal prints letter-backspace-letter.
For an italic c, it's 0x63 in UTF-8. The NSData contains 0x5f 0x08 0x63, with 0x5f corresponding to an underscore. So for italics, Terminal prints underscore-backspace-letter.
I really don't see any way around this at this point besides just scanning the raw NSData for these sequences. I'll probably post the source to my parser here once I finish it, unless anybody has any existing code. As the common programming phrase goes, never write yourself what you can copy. :)
Follow-Up:
I've got a good, fast parser together for taking man output and replacing the bold/underlined output with bold/underlined formatting in an NSMutableAttributedString. Here's the code if anybody else needs to solve the same problem:
NSMutableIndexSet *boldChars = [[NSMutableIndexSet alloc] init];
NSMutableIndexSet *underlineChars = [[NSMutableIndexSet alloc] init];
char* bBytes = malloc(1);
bBytes[0] = (char)0x08;
NSData *bData = [NSData dataWithBytes:bBytes length:1];
free(bBytes); bBytes = nil;
NSRange testRange = NSMakeRange(1, [inputData length] - 1);
NSRange bRange = NSMakeRange(0, 0);
do {
bRange = [inputData rangeOfData:bData options:(NSDataSearchOptions)NULL range:testRange];
if (bRange.location == NSNotFound || bRange.location > [inputData length] - 2) break;
const char * buff = [inputData bytes];
if (buff[bRange.location - 1] == 0x5f) {
// it's an underline
//NSLog(#"Undr %c\n", buff[bRange.location + 1]);
[inputData replaceBytesInRange:NSMakeRange(bRange.location - 1, 2) withBytes:NULL length:0];
[underlineChars addIndex:bRange.location - 1];
testRange = NSMakeRange(bRange.location, [inputData length] - (bRange.location));
} else if (buff[bRange.location - 1] == buff[bRange.location + 1]) {
// It's a bold
//NSLog(#"Bold %c\n", buff[bRange.location + 1]);
[inputData replaceBytesInRange:NSMakeRange(bRange.location - 1, 2) withBytes:NULL length:0];
[boldChars addIndex:bRange.location - 1];
testRange = NSMakeRange(bRange.location, [inputData length] - (bRange.location));
} else {
testRange.location = bRange.location + 1;
testRange.length = [inputData length] - testRange.location;
}
} while (testRange.location <= [inputData length] - 3);
NSMutableAttributedString *str = [[NSMutableAttributedString alloc] initWithString:[[NSString alloc] initWithData:inputData encoding:NSUTF8StringEncoding]];
NSFont *font = [NSFont fontWithDescriptor:[NSFontDescriptor fontDescriptorWithName:#"Menlo" size:12] size:12];
NSFont *boldFont = [[NSFontManager sharedFontManager] convertFont:font toHaveTrait:NSBoldFontMask];
[str addAttribute:NSFontAttributeName value:font range:NSMakeRange(0, [str length])];
__block NSUInteger begin = [underlineChars firstIndex];
__block NSUInteger end = begin;
[underlineChars enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
if (idx - end < 2) {
// it's the next item to the previous one
end = idx;
} else {
// it's a split, so drop in the accumulated range and reset
[str addAttribute:NSUnderlineStyleAttributeName value:[NSNumber numberWithInt:NSSingleUnderlineStyle] range:NSMakeRange(begin, (end-begin)+1)];
begin = idx;
end = begin;
}
if (idx == [underlineChars lastIndex]) {
[str addAttribute:NSUnderlineStyleAttributeName value:[NSNumber numberWithInt:NSSingleUnderlineStyle] range:NSMakeRange(begin, (end-begin)+1)];
}
}];
begin = [boldChars firstIndex];
end = begin;
[boldChars enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
if (idx - end < 2) {
// it's the next item to the previous one
end = idx;
} else {
// it's a split, so drop in the accumulated range and reset
[str addAttribute:NSFontAttributeName value:boldFont range:NSMakeRange(begin, (end-begin)+1)];
begin = idx;
end = begin;
}
if (idx == [underlineChars lastIndex]) {
[str addAttribute:NSFontAttributeName value:boldFont range:NSMakeRange(begin, (end-begin)+1)];
}
}];
Another method would be to convert the man page to PostScript source code, run that through the PostScript-to-PDF converter, and put that into a PDFView.
The implementation would be similar to Bavarious's answer, just with different arguments to groff (-Tps instead of -Thtml).
This would be the slowest solution, but also probably the best for printing.

floatValue for CGFloat

I have a NSSlider that is attempting to convert a regular float into a CGFloat. The code that I am using:
CGFloat testing = [mainSlider floatValue];
However this simply makes the testing variable "0.00" when I attempt to log it.
NSLog(#"%f", testing);
Does anyone have any ideas as to why this would happen? Thanks for any help.
check that mainSlider isn't nil
try this just in case (can't remember if CGFloat is a double or not):
NSLog(#"%f", (double)testing);
Check that the variable isn't being shadowed like this:
CGFloat testing = 0.0;
if(YES){
CGFloat testing = [mainSlider floatValue];
//should be: testing = [mainSlider floatValue];
}
NSLog(#"testing = %f", testing); //this will print "testing = 0.000000"

Resources