Получение битового шаблона bool в Swift

В ObjC может быть получен бит-битовый шаблон, отбрасывая его на UInt8.

например

  • true => 0x01
  • false => 0x00

Затем этот бит-шаблон можно использовать в дальнейших операциях манипулирования бит.


Теперь я хочу сделать то же самое в Свифт.

До сих пор я работал

UInt8(UInt(boolValue))

но это не похоже, что это предпочтительный подход.

Мне также требуется преобразование в O (1) без зависящего от данных ветвления. Таким образом, недопустимы такие вещи, как следующие.

boolValue ? 1 : 0

Кроме того, есть ли какая-то документация о том, как внедрены инициализаторы UInt8 и UInt? например, если инициализатор UInt для преобразования из bool использует зависящее от данных ветвление, я не могу его использовать.

Конечно, резервное копирование всегда должно использовать дополнительные побитовые операции, чтобы избежать значения bool вообще (например, проверить, не является ли число без нуля с помощью побитовых операторов в C).


  • Предлагает ли Swift элегантный способ доступа к битовой схеме Bool/конвертировать ее в UInt8 в O (1) без зависящего от данных ветвления?
+2
источник поделиться
2 ответа

@martin-rs ответ более увлекателен :-), но это можно сделать на детской площадке.

// first check this is true or youll be sorry...
sizeof(Bool) == sizeof(UInt8)

let t = unsafeBitCast(true, UInt8.self)   // = 1
let f = unsafeBitCast(false, UInt8.self)  // = 0
+2
источник

Если у вас есть сомнения, посмотрите на сгенерированный код сборки :)

func foo(someBool : Bool) -> UInt8 {
    let x = UInt8(UInt(someBool))
    return x
}

скомпилировано с помощью ("-O" = "Компиляция с оптимизациями")

xcrun -sdk macosx swiftc -emit-assembly -O main.swift

дает

    .globl  __TF4main3fooFSbVSs5UInt8
    .align  4, 0x90
__TF4main3fooFSbVSs5UInt8:
    .cfi_startproc
    pushq   %rbp
Ltmp2:
    .cfi_def_cfa_offset 16
Ltmp3:
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
Ltmp4:
    .cfi_def_cfa_register %rbp
    callq   __TFE10FoundationSb19_bridgeToObjectiveCfSbFT_CSo8NSNumber
    movq    %rax, %rdi
    callq   __TFE10FoundationSuCfMSuFCSo8NSNumberSu
    movzbl  %al, %ecx
    cmpq    %rcx, %rax
    jne LBB0_2
    popq    %rbp
    retq

Названия функций можно разбить на

$ xcrun -sdk macosx swift-demangle __TFE10FoundationSb19_bridgeToObjectiveCfSbFT_CSo8NSNumber __TFE10FoundationSuCfMSuFCSo8NSNumberSu
_TFE10FoundationSb19_bridgeToObjectiveCfSbFT_CSo8NSNumber ---> ext.Foundation.Swift.Bool._bridgeToObjectiveC (Swift.Bool)() -> ObjectiveC.NSNumber
_TFE10FoundationSuCfMSuFCSo8NSNumberSu ---> ext.Foundation.Swift.UInt.init (Swift.UInt.Type)(ObjectiveC.NSNumber) -> Swift.UInt

Нет инициализатора UInt который принимает аргумент Bool. Таким образом, интеллектуальный компилятор использовал автоматическое преобразование между типами Swift и Foundation и генерировал некоторый код, например

let x = UInt8(NSNumber(bool: someBool).unsignedLongValue)

Вероятно, не очень эффективно с двумя вызовами функций. (И он не компилируется, если вы import Swift только import Swift, без Foundation.)

Теперь другой метод, в котором вы предполагали зависящее от данных ветвление:

func bar(someBool : Bool) -> UInt8 {
    let x = UInt8(someBool ? 1 : 0)
    return x
}

Код сборки -

    .globl  __TF4main3barFSbVSs5UInt8
    .align  4, 0x90
__TF4main3barFSbVSs5UInt8:
    pushq   %rbp
    movq    %rsp, %rbp
    andb    $1, %dil
    movb    %dil, %al
    popq    %rbp
    retq

Нет ветвления, просто операция "И" с 0x01 !

Поэтому я не вижу причины не использовать это "прямолинейное" преобразование. Затем вы можете профилировать с помощью инструментов, чтобы проверить, является ли это узким местом для вашего приложения.

+4
источник

Посмотрите другие вопросы по меткам или Задайте вопрос