Re: [Pyobjc-dev] NSNumbers and NSDecimalNumbers
Brought to you by:
ronaldoussoren
From: Pierce T.W. I. <pi...@ma...> - 2005-02-05 18:03:36
|
On Feb 5, 2005, at 7:45 AM, Ronald Oussoren wrote: > > On 5-feb-05, at 15:22, Bob Ippolito wrote: > >>> >> >> int -> float doesn't lose precision, float -> complex doesn't lose >> precision, int -> decimal doesn't lose precision. float -> decimal >> *does* lose precision. It's already inconsistent with every similar >> type of math in Python. > > The problem is not that float -> decimal would loose precission > (although it sometimes does), but that float is a base-2 floating > point number while decimal is base-10. Because float is base-2 it > cannot exactly represent some numbers, which confuses people at first. > E.g. repr(1.1) == '1.1000000000000001' on most systems. What would > NSDecimalNumber(1.1) return? The same as NSDecimalNumber("1.1") or the > same as NSDecimalNumber("1.1000000000000001")? The first is incorrect, > the seconds is likely to confuse some people. Foundation handles this correctly actually, it rounds as needed when converting to NSDecimalNumber, such that NSDecimalNumber("1.1")==NSDecimalNumber(1.1). NSDecimalNumber(1.1111111111111115) may not be equal to NSDecimalNumber("1.1111111111111115") but that's a pretty standard failure: for (double x=0;x<1.0;x+=0.1) will run eleven times. >> >> What's your point? >> >> -(void)setValue:(NSString *)newValue >> >> obj.setValue_(1) >> >> Can still fail, perhaps not immediately. The same is true of Python. >> PyObjC can not possibly verify all arguments and return values. > > This is not a pyobjc isssue: > > NSObject* foo = [NSNumber numberWithInt:1]; > [obj setValue:foo]; > > This code is likely to compile and will fail at runtime, just like the > python equivalent. You'll also get a type warning at _compile_ time something python never does. I suspect you have the (id) everywhere ObjC coding style. Not me, I declare all my types, and so I get warnings from the compiler as much as possible, not runtime errors. > >> >>> As an aside, the bridge also assumes that an NSNumber of the >>> minimum range is the appropriate conversion, which also may not make >>> sense. >>> >>> So >>> >>> print Foundation.NSNumber.numberWithChar_(126)+1 >>> >>> outputs 127 >>> >>> while >>> >>> print Foundation.NSNumber.numberWithChar_(127)+1 >>> >>> outputs 128.0, because 128 is out of the range of Py_Char, so its >>> now a different type of NSNumber. So if you do math with an >>> NSNumber, you may get a different type of NSNumber back. >> >> Two things: >> (a) PyObjC 1.3a0 doesn't have that behavior, because it currently >> converts the result of an operation involving NSNumber subclasses >> *back* to a NSNumber, and NSNumber always uses the integer value for >> its description if is an integer regardless of what you initialized >> it with. Hmm... I just got: >>> print Foundation.NSNumber.numberWithChar_(126)+1 127.0 >>> print Foundation.NSNumber.numberWithChar_(127)+1 128.0 >> >> (b) NSNumber is effectively a "single class". There is NO DIFFERENCE >> between NSNumber.numberWithFloat_(10.0) and >> NSNumber.numberWithChar_(10). It may use a different internal >> representation, but that is not of your concern. Not strictly true, covered in previous email. The cases are obscure, but there is not "NO DIFFERENCE". >> >>>> PyObjC generally does not try and contort semantics of Foundation >>>> classes because we think it might be "easier to use". PyObjC is a >>>> language bridge, not a new set of Foundation classes tailored for >>>> Python users. >>> >>> If that were true then you would have to rip out all the automatic >>> conversion of python numbers to NSNumbers, since: >>> >>> - (void) setValue: (NSNumber *) newValue; >>> >>> obj.setValue_(1) >>> >>> Isn't Foundation semantics either, given that the equivalent objc >>> code would be: >>> >>> [obj setValue: [NSNumber numberWithInt:1]]; >>> >>> the python code should be: >>> >>> obj.setValue_(NSNumber.numberWithInt_()) instead. >>> >>> So given all that, I'm sure I'm not going to convince you of the >>> utility of the way I'm doing it, but I am going to mock you a little >>> bit because you aren't self consistent. >>> >>> So...is there any way to override the handling of NSNumbers at >>> runtime. I see some dead code that used to use a mapping table? >> >> It is consistent. PyObjC automatically converts several Python types >> to id (the Objective-C type system NEVER cares which class name was >> declared, only whether it is an id or not). If you write [obj >> setValue:1] then you would be completely violating the type system, >> and the compiler would complain about that. This is not invalidating >> any semantics of Foundation classes because it simply is not possible >> to do [obj setValue:1] in Foundation. It's also not possible to say >> anObject[someKey] in Foundation either, or nsNumber + >> anotherNSNumber, but we allow that too. These are *conveniences* >> where we allow behavior that would otherwise simply raise an >> exception because such an operation can't even cross the bridge. Personally, I think the conveniences are a good thing, not a bad thing. I just wished you were a little less attached to excluding those same conveniences from NSDecimalNumber. Your argument is that you have to be self consistent, but the reality is that consistency is an impossible goal because Python and ObjC have slightly different type systems. So perhaps NSDecimalNumbers should not be converted to python decimal types, instead the necessary methods could be added to the bridge from the python bridge side so that they worked much like NSNumbers do, so that: dn2= dn + 1.1 is really a known substitute for dn.decimalNumberByAdding_(NSDecimalNumber.numberWithDouble_(1.1)). I see that you're doing something similar in: Lib/objc/_convenience.py where you add the __add__ methods to NSNumber. I suppose I could do a similar thing for NSDecimalNumber and then remove the bridging to python.decimal. (Which might make sense for the same reason that it turned out not to be a good thing to bridge NSNumber.) Which leads to a Python question: A + B is really A.__add__(B) in python. For NSDecimalNumbers, I could add my own __add__ method so that if A is an NSDecimalNumber, it converts B to a decimal and returns the sum. But what if A is a regular float, and B is an NSDecimalNumber. How can I tell Python it needs to promote A to an NSDecimalNumber before doing the math? Presumably the complex number types had the same problem. This must come up with NSNumbers too, since: 1 + NSNumber(1) and NSNumber(1) + 1 both seem to work, but I can't figure out how you're doing it in _convenience.py. >> All conversions we do are lossless. No, they aren't because not all NSNumbers are created equal, and the python types don't map over the entire set of NSNumber types either. >>> print Foundation.NSNumber.numberWithUnsignedLong_(4000000000) -294967296 >>> print Foundation.NSNumber.numberWithUnsignedLong_(4000000000)+1 -294967295.0 >>> |