Biraz bir boolean ile karşılaştırma


12

Diyelim ki bir uint16_t kodlanmış bir dizi bayrak var flags. Örneğin AMAZING_FLAG = 0x02,. Şimdi bir fonksiyonum var. Bu işlevin bayrağını değiştirmek isteyip istemediğimi kontrol etmesi gerekir, çünkü bunu yapmak istersem flaşa yazmam gerekir. Ve bu pahalı. Bu nedenle, bana flags & AMAZING_FLAGeşit olup olmadığını söyleyen bir kontrol istiyorum doSet. Bu ilk fikir:

setAmazingFlag(bool doSet)
{
    if ((flags & AMAZING_FLAG) != (doSet ? AMAZING_FLAG : 0)) {
        // Really expensive thing
        // Update flags
    }
}

Bu sezgisel bir if ifadesi değildir. Daha iyi bir yol olmalı gibi hissediyorum, şöyle bir şey:

if ((flags & AMAZING_FLAG) != doSet){

}

Ama bu aslında işe yaramıyor, trueeşit gibi görünüyor 0x01.

Yani, bir boolean ile biraz karşılaştırmanın düzgün bir yolu var mı?


2
Mantığınızın ne olması gerektiği tam olarak belli değil. Bu (flags & AMAZING_FLAG) && doSetmu:?
kaylum

Soru açık değil. 'Bayrakların' 0x01 olup olmadığını kontrol etmek istiyoruz. İstediğin bu mu? Cevabınız evet ise, '&' bitsel operatörünü kullanabiliriz.
Vikas Vijayan

Eğer daha okunabilir bir çağrı yapmak istiyorsanız setAmazingFlag sadece doSet doğru olduğunda fonksiyon adı daha iyi kontrol eder, aksi takdirde adın söylediklerini yapabilecek veya yapamayacak bir fonksiyonunuz olabilir, kötü kod okuma için yapar
AndersK

Yapmaya çalıştığınız tek şey onu daha okunaklı hale getirmekse, sadece "flagsNotEqual" işlevini yapın.
Jeroen3

Yanıtlar:


18

Sıfır olmayan herhangi bir sayıyı 1'e (doğru) dönüştürmek için eski bir hile vardır: !(değil) operatörünü iki kez uygulayın.

if (!!(flags & AMAZING_FLAG) != doSet){

4
Ya da (bool)(flags & AMAZING_FLAG) != doSet- daha doğrudan bulduğum. ( Bu , Microsoft'un bununla ilgili bir sorunu olduğunu düşündürse de.) Ayrıca, ((flags & AMAZING_FLAG) != 0)muhtemelen tam olarak derlediği şeydir ve kesinlikle açıktır.
Chris Hall

"Ayrıca, ((flags & AMAZING_FLAG)! = 0) muhtemelen tam olarak derlediği şeydir ve kesinlikle açıktır". Olur mu? DoSet doğruysa ne olur?
Cheiron

@ChrisHall (bool) 2 1 ile sonuçlanıyor mu yoksa hala 2, C'de mi?
user253751

3
C11 Standardı, 6.3.1.2 Boole türü: "Herhangi bir skaler değer _Bool'a dönüştürüldüğünde, değer 0 ile karşılaştırılırsa sonuç 0 olur; aksi takdirde sonuç 1 olur." 6.5.4 Döküm işleçleri: "Bir ifadenin parantez içine alınmış tür adından önce gelmesi, ifadenin değerini adlandırılan türe dönüştürür."
Chris Hall

@Cheiron, daha açık olmak gerekirse: ((bool)(flags & AMAZING_FLAG) != doSet)ile aynı etkiye sahiptir (((flags & AMAZING_FLAG) != 0) != doSet)ve her ikisi de muhtemelen aynı şey için derlenecektir. Önerilen (!!(flags & AMAZING_FLAG) != doSet)eşdeğerdir ve ben de aynı şeyi derleyeceğimizi hayal ediyorum. Bu tamamen daha açık olduğunu düşündüğünüz bir tat meselesidir: (bool)oyuncu kadrosu _Bool'a nasıl yayınlar ve dönüşümler yapıldığını hatırlamanızı gerektirir; !!diğer bazı beyin jimnastiği gerektiren veya "hile" bilmek için.
Chris Hall

2

Sen C değerlerine eşdeğer bir boolean deyimi, bit maskesi dönüştürmek gerekir 0ya 1.

  • (flags & AMAZING_FLAG) != 0. En yaygın yol.

  • !!(flags & AMAZING_FLAG). Biraz yaygın, aynı zamanda kullanmak için Tamam, ama biraz şifreli.

  • (bool)(flags & AMAZING_FLAG). Modern C yolu sadece C99 ve ötesi.

Yukarıdaki alternatiflerden herhangi birini alın, sonra !=veya öğesini kullanarak boole'nizle karşılaştırın ==.


1

Mantıksal bir bakış açısından, flags & AMAZING_FLAGdiğer tüm bayrakları maskeleyen sadece biraz işlemdir. Sonuç sayısal bir değerdir.

Bir boole değerine almak için bir karşılaştırma kullanırsınız

(flags & AMAZING_FLAG) == AMAZING_FLAG

ve şimdi bu mantıksal değeri ile karşılaştırabiliriz doSet.

if (((flags & AMAZING_FLAG) == AMAZING_FLAG) != doSet)

C'de, sayıların boole değerlerine örtük dönüşüm kuralları nedeniyle kısaltmalar olabilir. Yani sen de yazabilirsin

if (!(flags & AMAZING_FLAG) == doSet)

daha kısa yazmak için. Ancak önceki sürüm, okunabilirlik açısından daha iyidir.


1

Değere dayalı bir maske oluşturabilirsiniz doSet:

#define AMAZING_FLAG_IDX 1
#define AMAZING_FLAG (1u << AMAZING_FLAG_IDX)
...

uint16_t set_mask = doSet << AMAZING_FLAG_IDX;

Şimdi çekiniz şu şekilde görünebilir:

setAmazingFlag(bool doSet)
{
    const uint16_t set_mask = doSet << AMAZING_FLAG_IDX;

    if (flags & set_mask) {
        // Really expensive thing
        // Update flags
    }
}

Bazı mimarilerde, !!bir dalda derlenebilir ve bununla iki dalınız olabilir:

  1. Tarafından normalleştirme !!(expr)
  2. Karşılaştırmak doSet

Teklifimin avantajı garantili tek bir şubedir.

Not: 30'dan fazla sola kaydırarak tanımlanmamış davranış getirmediğinizden emin olun (tamsayının 32 bit olduğunu varsayarak). Bu kolayca birstatic_assert(AMAZING_FLAG_IDX < sizeof(int)*CHAR_BIT-1, "Invalid AMAZING_FLAG_IDX");


1
Her tür boole ifadesi bir dalla sonuçlanma potansiyeline sahiptir. Garip manuel optimizasyon hileleri uygulamadan önce sökeceğim.
Lundin

1
@Lundin, tabi, bu yüzden "Bazı mimarilerde" yazdım ...
Alex Lop.

1
Bu çoğunlukla derleyicinin optimizatörü için bir konudur ve ISA'nın kendisi değildir.
Lundin

1
@Lundin katılmıyorum. Bazı mimariler koşullu bit kümesi / sıfırlaması için ISA'ya sahiptir, bu durumda braket gerekmez, diğerleri yoktur. godbolt.org/z/4FDzvw
Alex Lop.

Not: Bunu yaparsanız, tanımlar mısınız AMAZING_FLAGaçısından AMAZING_FLAG_IDX(örn #define AMAZING_FLAG ((uint16_t)(1 << AMAZING_FLAG_IDX))iki yerde böyle bir güncellenebilir olması (dan diyelim ki, tanımlanan aynı veriyi zorunda kalmamak), 0x4için 0x8) diğeri ise ( IDXbir 2) değişmeden kalır .
ShadowRanger

-1

Bu testi yapmanın birden çok yolu vardır:

Üçlü operatör pahalı sıçramalar oluşturabilir:

if ((flags & AMAZING_FLAG) != (doSet ? AMAZING_FLAG : 0))

Ayrıca, verimli olabilecek veya olmayabilecek boole dönüşümünü de kullanabilirsiniz:

if (!!(flags & AMAZING_FLAG) != doSet)

Veya eşdeğer alternatifi:

if (((flags & AMAZING_FLAG) != 0) != doSet)

Çarpma ucuzsa, aşağıdakilerle atlamayı önleyebilirsiniz:

if ((flags & AMAZING_FLAG) != doSet * AMAZING_FLAG)

Eğer flagsimzasız ve derleyici çok akıllı, aşağıda bölünme basit değişime derlemek olabilir:

if ((flags & AMAZING_FLAG) / AMAZING_FLAG != doSet)

Eğer mimari ikinin tamamlayıcı aritmetiğini kullanıyorsa, işte başka bir alternatif:

if ((flags & AMAZING_FLAG) != (-doSet & AMAZING_FLAG))

Alternatif olarak, flagsbit alanlı bir yapı olarak tanımlanabilir ve çok daha basit, okunabilir bir sözdizimi kullanılabilir:

if (flags.amazing_flag != doSet)

Ne yazık ki, bitfieldlerin spesifikasyonu bit seviyesi uygulaması üzerinde kesin kontrole izin vermediğinden, bu yaklaşım genellikle hoşnutsuzdur.


Herhangi bir kod parçasının birincil amacı, örneklerinizin çoğu olmayan okunabilirlik olmalıdır. Örneklerinizi bir derleyiciye gerçekten yüklediğinizde daha da büyük bir sorun ortaya çıkar: ilk iki örnek uygulama en iyi performansa sahiptir: en az atlama ve yük miktarı (ARM 8.2, -Os) (eşdeğerdir). Diğer tüm uygulamalar daha kötü performans gösterir. Genel olarak, süslü şeyler yapmaktan kaçınmalısınız (mikro optimizasyon), çünkü derleyicinin neler olduğunu anlamasını zorlaştıracak, bu da performansı düşürecektir. Bu nedenle -1.
Cheiron
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.