So is it correct to say that for all types T which NSNumber can hold (Double, 
Float, Int, UInt, ... )

    T(exactly: someNSNumber)
    
will succeed if and only if

    NSNumber(value: T(truncating: someNSNumber)) == someNSNumber

holds? 

> On 20. Apr 2017, at 18:10, Philippe Hausler <phaus...@apple.com> wrote:
> 
>> 
>> On Apr 19, 2017, at 6:09 PM, Xiaodi Wu <xiaodi...@gmail.com 
>> <mailto:xiaodi...@gmail.com>> wrote:
>> 
>> 
>> 
>> On Wed, Apr 19, 2017 at 6:35 PM, Philippe Hausler <phaus...@apple.com 
>> <mailto:phaus...@apple.com>> wrote:
>> 
>> 
>>> On Apr 19, 2017, at 16:17, Xiaodi Wu <xiaodi...@gmail.com 
>>> <mailto:xiaodi...@gmail.com>> wrote:
>>> 
>>> On Wed, Apr 19, 2017 at 6:00 PM, Philippe Hausler <phaus...@apple.com 
>>> <mailto:phaus...@apple.com>> wrote:
>>> 
>>>> On Apr 19, 2017, at 3:23 PM, Xiaodi Wu <xiaodi...@gmail.com 
>>>> <mailto:xiaodi...@gmail.com>> wrote:
>>>> 
>>>> On Wed, Apr 19, 2017 at 3:19 PM, Martin R <martinr...@gmail.com 
>>>> <mailto:martinr...@gmail.com>> wrote:
>>>>> On 19. Apr 2017, at 01:48, Xiaodi Wu <xiaodi...@gmail.com 
>>>>> <mailto:xiaodi...@gmail.com>> wrote:
>>>>> 
>>>>> So, as I understand it, `Float.init(exactly: Double.pi) == nil`. I would 
>>>>> expect NSNumber to behave similarly (a notion with which Martin 
>>>>> disagrees, I guess). I don't see a test that shows whether NSNumber 
>>>>> behaves or does not behave in that way.
>>>> 
>>>> At present they behave differently: 
>>>> 
>>>>     print(Float(exactly: Double.pi) as Any)
>>>>     // nil
>>>>     print(Float(exactly: NSNumber(value: Double.pi)) as Any)
>>>>     // Optional(3.14159274)
>>>> 
>>>> I realize that identical behavior would be logical and least surprising. 
>>>> My only concern was about cases like
>>>> 
>>>>     let num = ... // some NSNumber from a JSON deserialization
>>>>     let fval = Float(exactly: num)
>>>> 
>>>> where one cannot know how the number is represented internally and what 
>>>> precision it needs. But then one could use the truncating conversion or 
>>>> `.floatValue` instead.
>>>> 
>>>> JSON numbers are double-precision floating point, unless I'm 
>>>> misunderstanding something. If someone writes `Float(exactly: 
>>>> valueParsedFromJSON)`, surely, that can only mean that they *really, 
>>>> really* prefer nil over an imprecise value. I can see no other reason to 
>>>> insist on using both Float and .init(exactly:).
>>> 
>>> JSON does not claim 32 or 64 bit floating point, or for that matter 128 or 
>>> infinite bit floating point :(
>>> 
>>> 
>>> Oops, you're right. I see they've wanted to future-proof this. That said, 
>>> RFC 7159 *does* say:
>>> 
>>> This specification allows implementations to set limits on the range
>>> and precision of numbers accepted.  Since software that implements
>>> IEEE 754-2008 binary64 (double precision) numbers [IEEE754] is
>>> generally available and widely used, good interoperability can be
>>> achieved by implementations that expect no more precision or range
>>> than these provide, in the sense that implementations will
>>> approximate JSON numbers within the expected precision.
>>> 
>>> So JSON doesn't set limits on how numbers are represented, but JSON 
>>> implementations are permitted to (and I'd imagine that all in fact do). A 
>>> user of a JSON deserialization library can rightly expect to know the 
>>> numeric limits of that implementation; for the purposes of bridging 
>>> NSNumber, if the answer is that the implementation parses JSON numbers as 
>>> double-precision values, Double(exactly:) would be the right choice; 
>>> otherwise, if it's 80-bit values, then Float80(exactly:) would be the right 
>>> choice, etc.
>>> 
>> 
>> Float80 is not compatible with NSNumber; and is well out of scope for this 
>> proposal.
>> 
>> OK, so Double is the largest floating point type compatible with NSNumber? 
>> It stands to reason that any Swift JSON implementation that uses NSNumber 
>> for parsed floating point values would at most have that much range and 
>> precision, right?
> 
> For JSONSerialization (which I am most familiar with and ships with 
> Foundation); it can emit both NSNumbers and NSDecimalNumber. A rough 
> approximation of the behavior: if it can store the value in an integer type 
> it stores it as such in a NSNumber (iirc up to UINT64_MAX) and then if it has 
> a decimal point it will attempt to parse as a double but if that is not 
> enough storage it will store the best possible value into NSDecimalNumber. 
> 
> So NSNumber itself (excluding subclasses) can only store up to a 64 bit 
> value. 
> 
>> 
>> If so, then every floating point value parsed by any such Swift JSON 
>> implementation would be exactly representable as a Double: regardless of 
>> whether that specific implementation uses Float or Double under the hood, 
>> every Float can be represented exactly as a Double. If a user is trying to 
>> bridge such a NSNumber instance specifically to *Float* instead of Double, 
>> and they are asking for an exact value, there's no a priori reason to think 
>> that this user would be more likely to care only about the range and not the 
>> precision, or vice versa. Which is to say, I don't think you'll get too many 
>> bug reports :)
> 
> In my mind there are two considerations here; balance against the surprise 
> from new developers learning their first programming language versus 
> consistency. In the end even if I believe the behavior is sub-par I would 
> rather it be consistent. Primarily consistency is easier to teach even if it 
> is derived from a standard developed with legacy behavior of C at its heart.
> 
> Perhaps in the future we might want to eventually allow conversions to and 
> from NSNumber via the Integer and FloatingPoint protocols; however I would 
> guess that there needs to be a lot more thought and perhaps some 
> modifications there to pull that off. Not to sound like a broken record, but 
> again that it is out of scope for right now.
> 
>> 
>>> 
>>> After thinking about it more; it seems reasonable to restrict it to the 
>>> behavior of Float(exactly: Double(…)). I am certain this will probably in 
>>> the end cause more bugs for me to have to address and mark as “behaves 
>>> correctly” and confuse a few new developers - but in the end they chose 
>>> Swift and the consistent story would be the current behavior of 
>>> Float(exactly: Double).
>>> 
>>>> 
>>>>> 
>>>>> 
>>>>> On Tue, Apr 18, 2017 at 11:43 AM, Philippe Hausler <phaus...@apple.com 
>>>>> <mailto:phaus...@apple.com>> wrote:
>>>>> 
>>>>>> On Apr 18, 2017, at 9:22 AM, Stephen Canon <sca...@apple.com 
>>>>>> <mailto:sca...@apple.com>> wrote:
>>>>>> 
>>>>>>> 
>>>>>>> On Apr 18, 2017, at 12:17 PM, Joe Groff <jgr...@apple.com 
>>>>>>> <mailto:jgr...@apple.com>> wrote:
>>>>>>> 
>>>>>>> 
>>>>>>>> On Apr 17, 2017, at 5:56 PM, Xiaodi Wu via swift-evolution 
>>>>>>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>>>>>>>> 
>>>>>>>> It seems Float.init(exactly: NSNumber) has not been updated to behave 
>>>>>>>> similarly?
>>>>>>>> 
>>>>>>>> I would have to say, I would naively expect "exactly" to behave 
>>>>>>>> exactly as it says, exactly. I don't think it should be a synonym for 
>>>>>>>> Float(Double(exactly:)).
>>>>>>>> On Mon, Apr 17, 2017 at 19:24 Philippe Hausler via swift-evolution 
>>>>>>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>>>>>>>> I posted my branch and fixed up the Double case to account for your 
>>>>>>>> concerns (with a few inspired unit tests to validate)
>>>>>>>> 
>>>>>>>> https://github.com/phausler/swift/tree/safe_nsnumber 
>>>>>>>> <https://github.com/phausler/swift/tree/safe_nsnumber>
>>>>>>>> 
>>>>>>>> There is a builtin assumption here though: it does presume that the 
>>>>>>>> swift’s representation of Double and Float are IEEE compliant. However 
>>>>>>>> that is a fairly reasonable assumption in the tests.
>>>>>>> 
>>>> 
>>>> 
>>>> Even with the updated code at 
>>>> https://github.com/phausler/swift/tree/safe_nsnumber 
>>>> <https://github.com/phausler/swift/tree/safe_nsnumber>
>>>> 
>>>>     print(Double(exactly: NSNumber(value: Int64(9000000000000000001))) as 
>>>> Any)
>>>>     // Optional(9e+18)
>>>> 
>>>> still succeeds, however the reason seems to be an error in the 
>>>> `init(exactly value: someIntegerType)` inititializers of Float/Double, I 
>>>> have submitted a bug report: https://bugs.swift.org/browse/SR-4634 
>>>> <https://bugs.swift.org/browse/SR-4634>.
>>>> 
>>>> 
>>>>>>> (+Steve Canon) What is the behavior of Float.init(exactly: Double)? 
>>>>>>> NSNumber's behavior would ideally be consistent with that.
>>>>>> 
>>>>>> The implementation is essentially just:
>>>>>> 
>>>>>>  self.init(other)
>>>>>>  guard Double(self) == other else {
>>>>>>          return nil
>>>>>>  }
>>>>>> 
>>>>>> i.e. if the result is not equal to the source when round-tripped back to 
>>>>>> double (which is always exact), the result is nil.
>>>>>> 
>>>>>> – Steve
>>>>> 
>>>>> Pretty much the same trick inside of CFNumber/NSNumber

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to