Bir unsigned int
bağımsız değişken alan ve bağımsız değişkene int
UINT_MAX + 1 uyumlu bir modulo döndüren bir işlev tanımlamak istiyorum .
İlk deneme şuna benzeyebilir:
int unsigned_to_signed(unsigned n)
{
return static_cast<int>(n);
}
Ancak herhangi bir dil avukatının bildiği gibi, INT_MAX'tan daha büyük değerler için imzasızdan imzalıya geçiş uygulama tanımlıdır.
Bunu, (a) yalnızca şartnamenin gerektirdiği davranışa dayanacak şekilde uygulamak istiyorum; ve (b) herhangi bir modern makinede ve optimize edici derleyicide işlemsiz olarak derlenir.
Garip makinelere gelince ... İmzasız int ile UINT_MAX + 1 uyumlu bir int uyumlu modulo yoksa, diyelim ki bir istisna atmak istiyorum. Birden fazla varsa (bunun mümkün olduğundan emin değilim), diyelim ki en büyüğünü istiyorum.
Tamam, ikinci deneme:
int unsigned_to_signed(unsigned n)
{
int int_n = static_cast<int>(n);
if (n == static_cast<unsigned>(int_n))
return int_n;
// else do something long and complicated
}
Benim mütevazı görüşüme göre bu pek olası olmadığı için, tipik bir ikili tamamlayıcı sistemde olmadığım zaman verimliliği pek umursamıyorum. Ve eğer kodum 2050'nin her yerde mevcut olan işaret büyüklüğü sistemlerinde bir darboğaz haline gelirse, bahse girerim ki birisi bunu anlayabilir ve o zaman optimize edebilir.
Şimdi, bu ikinci deneme benim istediğim şeye oldukça yakın. int
Bazı girdiler için dönüştürme uygulama tanımlı olsa da ,unsigned
modulo UINT_MAX + 1 değerini korumak için standart tarafından garanti edilir. Yani koşullu, tam olarak ne istediğimi kontrol ediyor ve karşılaşabileceğim herhangi bir sistemde hiçbir şeye derlenmeyecek.
Ancak ... hala int
uygulama tanımlı davranışı çağırıp çağırmayacağını kontrol etmeden adresine çeviriyorum. 2050'deki bazı varsayımsal sistemlerde, kim bilir ne yapabilirdi. Diyelim ki bundan kaçınmak istiyorum.
Soru: "Üçüncü denemem" neye benzemeli?
Özetlemek gerekirse, şunu yapmak istiyorum:
- İşaretsiz int'ten imzalı int'e çevir
- Değer modunu UINT_MAX + 1 koruyun
- Yalnızca standart zorunlu davranışı çağırın
- Optimize edici derleyici ile tipik bir iki tamamlayıcı makinede işlemsiz olarak derleyin
[Güncelleme]
Bunun neden önemsiz bir soru olmadığını göstermek için bir örnek vereyim.
Aşağıdaki özelliklere sahip varsayımsal bir C ++ uygulamasını düşünün:
sizeof(int)
eşittir 4sizeof(unsigned)
eşittir 4INT_MAX
eşittir 32767INT_MIN
eşittir -2 32 + 32768UINT_MAX
2 eşit 32 - 1- Aritmetik açık
int
, modulo 2 32'dir (aralığaINT_MIN
kadarINT_MAX
) std::numeric_limits<int>::is_modulo
doğru- İşaretsiz
n
olarak int türüne çevrim 0 <= n <= 32767 değerini korur ve aksi takdirde sıfır verir
Bu varsayımsal uygulamada, her bir int
değere uyumlu tam olarak bir değer (mod UINT_MAX + 1) vardır unsigned
. Yani sorum iyi tanımlanmış olacak.
Bu varsayımsal C ++ uygulamasının C ++ 98, C ++ 03 ve C ++ 11 spesifikasyonlarına tamamen uygun olduğunu iddia ediyorum. Her kelimesini ezberlemediğimi kabul ediyorum ... Ama ilgili bölümleri dikkatlice okuduğuma inanıyorum. Öyleyse cevabınızı kabul etmemi istiyorsanız, ya (a) bu varsayımsal uygulamayı dışlayan bir özelliğe atıfta bulunmalısınız ya da (b) doğru şekilde ele almalısınız.
Gerçekte, doğru bir yanıt , standardın izin verdiği her varsayımsal uygulamayı ele almalıdır . Tanım gereği, "yalnızca standart yönetimli davranışı çağırmak" budur.
Bu arada, bunun std::numeric_limits<int>::is_modulo
birçok nedenden dolayı burada tamamen yararsız olduğunu unutmayın . Birincisi, true
büyük işaretsiz değerler için imzasız-imzalı yayınlar çalışmasa bile olabilir. Bir diğeri için, true
aritmetik basitçe tüm tamsayı aralığını modulo ise, kişinin tümleyen veya işaret büyüklüğü sistemlerinde bile olabilir . Ve bunun gibi. Cevabınız şuna bağlıysais_modulo
yanlıştır.
[Güncelleme 2]
HVD cevabı tamsayılar için My varsayımsal C ++ uygulama olduğunu: bana bir şey öğretti değil imzalı tamsayı temsil konusunda çok özeldir Modern C. C99 ve C11 standartlarına göre izin verilen; gerçekte, sadece iki-tümleme, bir-tümleme ve işaret büyüklüğüne izin verirler (bölüm 6.2.6.2 paragraf (2);).
Ancak C ++, C değildir. Görünüşe göre, sorumun tam merkezinde bu gerçek yatıyor.
Orijinal C ++ 98 standardı, (bölüm 3.1.2.5) şunu söyleyen çok daha eski C89'a dayanıyordu:
İşaretli tam sayı türlerinin her biri için, aynı miktarda depolamayı (işaret bilgileri dahil) kullanan ve aynı hizalama gereksinimlerine sahip karşılık gelen (ancak farklı) işaretsiz bir tamsayı türü (işaretsiz anahtar sözcüğüyle belirtilir) vardır. İşaretli bir tamsayı türünün negatif olmayan değerler aralığı, karşılık gelen işaretsiz tamsayı türünün bir alt aralığıdır ve her türdeki aynı değerin gösterimi aynıdır.
C89, yalnızca bir işaret biti olması veya yalnızca iki-tamamlayıcı / bir-tamamlayıcı / işaret büyüklüğüne izin verilmesi hakkında hiçbir şey söylemiyor.
C ++ 98 standardı bu dili neredeyse aynen benimsemiştir (bölüm 3.9.1 paragraf (3)):
İşaretli tam sayı türlerinin her biri için, karşılık gelen (ancak farklı) işaretsiz bir tam sayı türü vardır : "
unsigned char
", "unsigned short int
", "unsigned int
" ve "unsigned long int
", her biri aynı miktarda depolama alanı kaplar ve aynı hizalama gereksinimlerine sahiptir (3.9 ) karşılık gelen işaretli tamsayı türü olarak; yani, her işaretli tamsayı türü, karşılık gelen işaretsiz tamsayı türü ile aynı nesne temsiline sahiptir . İşaretli bir tamsayı türünün negatif olmayan değerler aralığı, karşılık gelen işaretsiz tamsayı türünün bir alt aralığıdır ve karşılık gelen her bir işaretli / işaretsiz türün değer temsili aynı olacaktır.
C ++ 03 standardı, C ++ 11 gibi temelde aynı dili kullanır.
Anlayabildiğim kadarıyla hiçbir standart C ++ özelliği, işaretli tamsayı temsillerini herhangi bir C spesifikasyonu ile sınırlamaz. Ve tek bir işaret parçasını ya da herhangi bir şeyi zorunlu kılan hiçbir şey yoktur. Tek söylediği negatif olmayan işaretli tam sayıların karşılık gelen işaretsizlerin bir alt aralığı olması gerektiğidir.
Bu yüzden, yine INT_MAX = 32767 ile INT_MIN = -2 32 + 32768'e izin verildiğini iddia ediyorum . Cevabınız aksini varsayıyorsa, beni yanlış olduğunu kanıtlayan bir C ++ standardından alıntı yapmadığınız sürece yanlıştır.