Поплавки с половинной точностью в Swift очень неудобны, так как пока нет типа Float16
(хотя был предложен ), а нестандартный тип __fp16
, поддерживаемый Clang, не поддерживается полностьюлибо в Swift.
Однако, используя магию заголовков типа Punching и Bridging, вы можете объединить работоспособное решение.
Основной подход заключается в следующем:Заголовок C, объявите тип half2
с двумя uint16_t
членами.Это будет наш тип хранения.Также объявите функцию, которая принимает float и записывает его так, как если бы он был __fp16
для указателя на uint16_t
:
typedef struct {
uint16_t x, y;
} half2;
static void storeAsF16(float value, uint16_t *_Nonnull pointer) { *(__fp16 *)pointer = value; }
Вернувшись в Swift, вы можете объявить typealias и использовать егов определении структуры вашей частицы:
typealias Half2 = half2
struct Particle {
var position: Half2
}
(Здесь я печатаю от строчного типа к имени Swiftier; вы можете пропустить это и просто назвать тип Obj-C Half2
, если хотите).
Вместо привязки к типу частицы, вам нужно вместо этого привязать буфер к вашему типу половинного вектора:
var pointer = particleBuffer.contents().bindMemory(to: Half2.self, capacity: particleCount)
Когда мы используем нашу служебную функцию для хранения числа с плавающей запятойбитовый шаблон для соответствующего половинного значения записывается в UInt16
:
var x: UInt16 = 0
var y: UInt16 = 0
storeAsF16(1.0, &x) // 0x3c00
storeAsF16(0.5, &y) // 0x3800
Теперь, когда у нас есть правильно отформатированные половинные значения в этой паре переменных, мы можем записать их в буфер:
pointer.pointee = Half2(x: x, y: y)
Обратите внимание, что этот подход не является ни переносимым, ни безопасным, особенно потому, что Swift не дает никаких гарантий относительно структуры элементов struct.Могут быть и другие, менее громоздкие подходы;это то, что работало для меня в прошлом.