1

So I have a NSArrays loaded with for pitch and yaw values saved on a Shot object.

@interface Shot : NSObject
@property (strong, nonatomic) NSArray *pitch;
@property (strong, nonatomic) NSArray *yaw;

I am trying to do some number crunching in swift and running into a very weird problem:

let pitch = Array(shot.recoil_pitch[0..<10]) as! [Float]
let yaw = Array(shot.recoil_yaw[0..<10]) as! [Float]

For some reason that I can't figure out, the first line runs fine. The second line, however, throws a Fatal error: Unable to bridge NSNumber to Float.

When I inspect shot.recoil_pitch[0..<10] in the debugger I get:

- 0 : 0
- 1 : -0.1355667114257812
- 2 : -2.584976196289062
- 3 : -2.238974571228027
- 4 : -1.606719017028809
- 5 : -1.54025936126709
- 6 : -1.570234298706055
- 7 : -1.544057846069336
- 8 : -1.323789596557617
- 9 : -1.073002815246582

and when I inspect the shot.recoil_yaw[0..<10] I get:

- 0 : 0
- 1 : 0.017406463623046875
- 2 : -0.07871246337890625
- 3 : 0.004611968994140625
- 4 : 0.0446014404296875
- 5 : -0.0008544921875
- 6 : 0.010448455810546875
- 7 : 0.01015472412109375
- 8 : -0.002346038818359375
- 9 : -0.0569610595703125

Any ideas on why I might be getting this error?

Chase Roberts
  • 9,082
  • 13
  • 73
  • 131
  • When you inspect `recoil_yaw` does it give you any type information? If you are dealing with stuff from Objective-C maybe it would be best to cast them to the native Objective-C class `NSNumber` and then map them to `Float` using the member `floatValue`. – keji Jan 08 '19 at 20:01
  • 1
    What is the return type of subscript on `recoil_yaw`? Better use `map` instead of force casting as `Array(shot.recoil_pitch[0..<10]).map{ $0.floatValue }` – Kamran Jan 08 '19 at 20:05
  • Also this may be relevant: https://stackoverflow.com/a/50019421/1484378 – keji Jan 08 '19 at 20:07
  • Related: https://stackoverflow.com/questions/49688983/unexpected-behavior-when-casting-an-nsnumber-to-float – Martin R Jan 08 '19 at 20:18
  • 2
    @Kamran - That’s right, but he won’t be able to just call `floatValue` that without either (a) casting `$0` to `NSNumber`; or (b) using lightweight generics in his Objective-C header, e.g. `@property (strong, nonatomic) NSArray *recoil_pitch`. – Rob Jan 08 '19 at 20:34

1 Answers1

3

I am able to reproduce this problem with this simple example:

var arr = [NSNumber(value: 1.3 as Float), NSNumber(value: 2.7)]

// This works
let x = arr.map { $0.floatValue }
print(x)
[1.3, 2.7]
// This fails with error 'Fatal error: Unable to bridge NSNumber to Float'
let y = arr as! [Float]
print(y)

As explained in the duplicate's answer, you can't cast an NSNumber to Float if the cast doesn't preserve the exact value. Double(2.7) loses precision when converted to Float, so the cast fails. Had I chosen 2.5 instead of 2.7 for the Double, the cast would have worked.

Accessing the value with .floatValue works because it uses the property of NSNumber to produce the Float value.

vacawama
  • 150,663
  • 30
  • 266
  • 294
  • 4
    The problem is not that the underlying value is a Double, but that this Double is not exactly preserved when converted to a Float. Compare https://stackoverflow.com/a/49689243/1187415. As an example, `var arr = [NSNumber(value: 1.3 as Float), NSNumber(value: 2.5)]` can be cast to `[Float]` without problems. – Martin R Jan 08 '19 at 20:23