Correct argument type for Float16(bitPattern: bits) on macOS

Hi there, I'm facing a strange behavior when writing a function that unpacks a Float32 to two Float16.

func unpackFloat32ToFloat16(from packed: Float32) -> (Float16, Float16) {
    let packedBits = packed.bitPattern
    
    // Extract the two 16-bit patterns from the 32-bit packed value
    let bits1 = UInt16(packedBits >> 16)
    let bits2 = UInt16(packedBits & 0xFFFF)
    
    // Reinterpret the 16-bit patterns as Float16
    let float1 = Float16(bitPattern: bits1)
    let float2 = Float16(bitPattern: bits2)
    
    return (float1, float2)
}

This code compiles and runs normally on iOS but not on macOS (14.6.1 with Apple Silicon). On macOS the compilation error is: "Cannot convert value of type 'UInt16' to expected argument type 'UInt32'", but clearly Float16(bitPattern: bits) expect a UInt16 according to the document. Passing UInt32 is also not working and the compiler expects UInt16 instead.

I'm using Xcode-beta Version 16.0 beta 4 (16A5211f) with

$ swift --version
swift-driver version: 1.90.11.1 Apple Swift version 5.10 (swiftlang-5.10.0.13 clang-1500.3.9.4)
Target: arm64-apple-macosx14.0

Any help is very much appreciated!

I have not been able to reproduce this. What line is producing the error?

Are you building for AppleSilicon only, or x86_64 as well? Can you capture the actual compile command that's failing from the build log?

I suspect that this is actually an error from building for x86_64 (Float16 is only available on macOS when targeting AppleSilicon).

Thanks for your reply! Steps to reproduce this error from scratch:

  1. Clone and configure this project. The only change I made is assigning a Development Team, Bundle Identifier and changing the Minimum Deployment version of macOS from 11.0 to 14.0.
  2. Add the abovementioned function to the end of this file.
  3. The compiler will produce errors at let float1 = Float16(bitPattern: bits1) and its following line.

When running in the command line with xcodebuild -project SampleApp/MetalSplatter_SampleApp.xcodeproj -scheme MetalSplatter\ SampleApp -destination "platform=macOS,arch=arm64" build, the log says it is still trying to compile for x86_64. It seems that this cannot be disabled according to this reply from the repo author. Will this be the cause of the problem?

CompileSwift normal x86_64 (in target 'MetalSplatter' from project 'MetalSplatter')
    cd /Users/sunjiaming/Repositories/MetalSplatter
    /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift-frontend -c 
# ... omitting multiple lines
/Users/sunjiaming/Repositories/MetalSplatter/MetalSplatter/Sources/SplatRenderer.swift:873:38: error: cannot convert value of type 'UInt16' to expected argument type 'UInt32'
    let float1 = Float16(bitPattern: bits1)
                                     ^
                                     UInt32( )
/Users/sunjiaming/Repositories/MetalSplatter/MetalSplatter/Sources/SplatRenderer.swift:874:38: error: cannot convert value of type 'UInt16' to expected argument type 'UInt32'
    let float2 = Float16(bitPattern: bits2)
                                     ^
                                     UInt32( )

Note that this repo use typealias Float16 = Float for x86_64 at here.

If this is the cause of the problem, since I'm only interested in building for arm64, how to entirely disable building for x86_64? I tried to set the Excluded Architectures flag but it does not work.

Yes, that's absolutely the source of your problem.

The simplest way out is to do something like:

#if arch(arm64)
  let packedBits = packed.bitPattern
  // Extract the two 16-bit patterns from the 32-bit packed value
  let bits1 = UInt16(packedBits >> 16)
  let bits2 = UInt16(packedBits & 0xFFFF)
  
  // Reinterpret the 16-bit patterns as Float16
  let float1 = Float16(bitPattern: bits1)
  let float2 = Float16(bitPattern: bits2)
  
  return (float1, float2)
#else
  fatalError()
#endif

that's not ideal in general, but here, where the app already doesn't work for x86_64 (and doesn't intend to), it's fine.

2 Likes