Different behaviour when casting Float and Double to NSNumber


(Lars-Jørgen Kristiansen) #1

I'm working with a third party API for some external hardware. One of the functions takes a NSNumber, and it fails to interact correctly with the hardware if I cast a Float too NSNumber, but works as expected if I use Double..

I dont know if it is related to NSNumber.stringValue since I dont know what the third part lib does with the NSNumber, but I noticed this:

let float = 100_000_00 as Float
let floatNumber = float as NSNumber

let double = 100_000_00 as Double
let doubleNumer = double as NSNumber

hardware.doThing(number: floatNumber as NSNumber) // Hardware does not work
hardware.doThing(number: doubleNumer as NSNumber) // Hardware works

// Also noticed this:
"\(floatNumber)" // "1e+07"
"\(doubleNumer)" // "10000000"

Is this expected behaviour?


(Jens Alfke) #2

Numbers around 10 million are too large to be represented exactly by a 32-bit float — the mantissa is 24 bits, including sign, so its range is ±8.3 million. (The specific number 10,000,000 does come out exactly, though, since it’s a multiple of 128.)

So even if Float.description did use non-scientific notation for numbers at this scale, they wouldn’t be accurate. In fact the implementor of the .description method may have decided intentionally to switch to scientific notation at this scale so that the number of significant figures can be limited to the available precision.

—Jens

···

On Oct 5, 2016, at 2:30 AM, Lars-Jørgen Kristiansen via swift-users <swift-users@swift.org> wrote:

// Also noticed this:
"\(floatNumber)" // "1e+07"
"\(doubleNumer)" // "10000000"


(Joe Groff) #3

cc'ing Tony and Philippe, who might be able to comment on the intended behavior of NSNumber here. When you cast using `as`, Swift will do the conversion through its Objective-C bridge, and will attempt to use a custom subclass of NSNumber that remembers the exact Swift type it was bridged as, such as Float or Double. There's a small chance your library is making assumptions that it's working with one of the standard NSNumber subclasses used by the Foundation implementation. If you call the NSNumber initializer `NSNumber(value: floatNumber)` instead of using the `as` conversion, you'll get one of these standard NSNumbers instead of a Swift-bridged NSNumber. Try that and see if it works. If using the initializer does indeed fix the problem, we'd appreciate a bug report, so we can investigate fixing the incompatibility with our NSNumber subclasses.

-Joe

···

On Oct 5, 2016, at 2:30 AM, Lars-Jørgen Kristiansen via swift-users <swift-users@swift.org> wrote:

I'm working with a third party API for some external hardware. One of the functions takes a NSNumber, and it fails to interact correctly with the hardware if I cast a Float too NSNumber, but works as expected if I use Double..

I dont know if it is related to NSNumber.stringValue since I dont know what the third part lib does with the NSNumber, but I noticed this:

let float = 100_000_00 as Float
let floatNumber = float as NSNumber

let double = 100_000_00 as Double
let doubleNumer = double as NSNumber

hardware.doThing(number: floatNumber as NSNumber) // Hardware does not work
hardware.doThing(number: doubleNumer as NSNumber) // Hardware works

// Also noticed this:
"\(floatNumber)" // "1e+07"
"\(doubleNumer)" // "10000000"

Is this expected behaviour?


(Zhao Xin) #4

I think you should file a bug on the inconsistence of `description`.
However, the third-party API somehow using the `description` is not a good
idea.

Zhaoxin

···

On Wed, Oct 5, 2016 at 5:30 PM, Lars-Jørgen Kristiansen via swift-users < swift-users@swift.org> wrote:

I'm working with a third party API for some external hardware. One of the
functions takes a NSNumber, and it fails to interact correctly with the
hardware if I cast a Float too NSNumber, but works as expected if I use
Double..

I dont know if it is related to NSNumber.stringValue since I dont know
what the third part lib does with the NSNumber, but I noticed this:

let float = 100_000_00 as Float
let floatNumber = float as NSNumber

let double = 100_000_00 as Double
let doubleNumer = double as NSNumber

hardware.doThing(number: floatNumber as NSNumber) // Hardware does not
work
hardware.doThing(number: doubleNumer as NSNumber) // Hardware works

// Also noticed this:
"\(floatNumber)" // "1e+07"
"\(doubleNumer)" // "10000000"

Is this expected behaviour?

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


(Lars-Jørgen Kristiansen) #5

Hmm, I actually thought about this being the reason but couldn't remember the limits.

And thought that i would receive a overflows when stored into 'Float' error...?

···

5. okt. 2016 kl. 19.26 skrev Jens Alfke <jens@mooseyard.com>:

On Oct 5, 2016, at 2:30 AM, Lars-Jørgen Kristiansen via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

// Also noticed this:
"\(floatNumber)" // "1e+07"
"\(doubleNumer)" // "10000000"

Numbers around 10 million are too large to be represented exactly by a 32-bit float — the mantissa is 24 bits, including sign, so its range is ±8.3 million. (The specific number 10,000,000 does come out exactly, though, since it’s a multiple of 128.)

So even if Float.description did use non-scientific notation for numbers at this scale, they wouldn’t be accurate. In fact the implementor of the .description method may have decided intentionally to switch to scientific notation at this scale so that the number of significant figures can be limited to the available precision.

—Jens


(Lars-Jørgen Kristiansen) #6

Thanks I will give that a try on Friday when I have the hardware available! Posted an issue and will add to that when I have tried, or if the library authors can comment on how they use the NSNumber..

···

5. okt. 2016 kl. 19.31 skrev Joe Groff <jgroff@apple.com>:

On Oct 5, 2016, at 2:30 AM, Lars-Jørgen Kristiansen via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

I'm working with a third party API for some external hardware. One of the functions takes a NSNumber, and it fails to interact correctly with the hardware if I cast a Float too NSNumber, but works as expected if I use Double..

I dont know if it is related to NSNumber.stringValue since I dont know what the third part lib does with the NSNumber, but I noticed this:

let float = 100_000_00 as Float
let floatNumber = float as NSNumber

let double = 100_000_00 as Double
let doubleNumer = double as NSNumber

hardware.doThing(number: floatNumber as NSNumber) // Hardware does not work
hardware.doThing(number: doubleNumer as NSNumber) // Hardware works

// Also noticed this:
"\(floatNumber)" // "1e+07"
"\(doubleNumer)" // "10000000"

Is this expected behaviour?

cc'ing Tony and Philippe, who might be able to comment on the intended behavior of NSNumber here. When you cast using `as`, Swift will do the conversion through its Objective-C bridge, and will attempt to use a custom subclass of NSNumber that remembers the exact Swift type it was bridged as, such as Float or Double. There's a small chance your library is making assumptions that it's working with one of the standard NSNumber subclasses used by the Foundation implementation. If you call the NSNumber initializer `NSNumber(value: floatNumber)` instead of using the `as` conversion, you'll get one of these standard NSNumbers instead of a Swift-bridged NSNumber. Try that and see if it works. If using the initializer does indeed fix the problem, we'd appreciate a bug report, so we can investigate fixing the incompatibility with our NSNumber subclasses.

-Joe


(Jens Alfke) #7

It doesn’t overflow. (I think the maximum value of Float is around 1e37.) There just isn’t enough precision to represent it exactly, which is pretty common; the usual example is that 0.1 can’t be represented exactly by any size binary floating-point.

—Jens

···

On Oct 5, 2016, at 11:36 AM, Lars-Jørgen Kristiansen <larsjk.84@gmail.com> wrote:

And thought that i would receive a overflows when stored into 'Float' error...?


(Lars-Jørgen Kristiansen) #8

Thanks,

Will file a bug.
Not sure how the library uses the NSNumber since it's closed source. So it might be other inconsistencies as well..

···

5. okt. 2016 kl. 13.50 skrev Zhao Xin <owenzx@gmail.com>:

I think you should file a bug on the inconsistence of `description`. However, the third-party API somehow using the `description` is not a good idea.

Zhaoxin

On Wed, Oct 5, 2016 at 5:30 PM, Lars-Jørgen Kristiansen via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:
I'm working with a third party API for some external hardware. One of the functions takes a NSNumber, and it fails to interact correctly with the hardware if I cast a Float too NSNumber, but works as expected if I use Double..

I dont know if it is related to NSNumber.stringValue since I dont know what the third part lib does with the NSNumber, but I noticed this:

let float = 100_000_00 as Float
let floatNumber = float as NSNumber

let double = 100_000_00 as Double
let doubleNumer = double as NSNumber

hardware.doThing(number: floatNumber as NSNumber) // Hardware does not work
hardware.doThing(number: doubleNumer as NSNumber) // Hardware works

// Also noticed this:
"\(floatNumber)" // "1e+07"
"\(doubleNumer)" // "10000000"

Is this expected behaviour?

_______________________________________________
swift-users mailing list
swift-users@swift.org <mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users