Nima uchun - (- 2147483648) = - 2147483648 32-bitli mashinada?

Menimcha, bu savol o'z-o'zidan tushuntirib beradi, deb o'ylayman, ehtimol, bu toshqin bilan bog'liq bo'lgan narsa bor, lekin men hali ham buni qabul qilmayapman. Nima bo'layapti, bitsel, qopqon ostida?

Nima uchun - (- 2147483648) = -2147483648 (hech bo'lmaganda Sda kompilyatsiya qilinayotganda)?

61
Sharhlar kengaytirilgan muhokamada emas; bu suhbat chatga ko'chirildi .
qo'shib qo'ydi muallif Bhargav Rao, manba
Intervyu savol: abs (INT_MIN) ==?
qo'shib qo'ydi muallif imallett, manba
UB, faqat Intel/AMD protsessorlari bo'yicha NEG ko'rsatmalarining xatti-harakatlarini ko'rasiz. Agar siz ushbu raqamni -1 ga bo'ladigan bo'lsak, bu juda ham qiziqarli.
qo'shib qo'ydi muallif Hans Passant, manba
Odatda Java kodi bilan bunday savolni berish foydali bo'ladi, hatto siz C kodini yozishni rejalashtirmoqchi bo'lsangiz ham ... har bir narsa uchun UB ogohlantirishidan foydalanish shart emas, chunki signalning shovqin nisbatlarini oshiradi.
qo'shib qo'ydi muallif harold, manba
@ LưuVĩnhPhúc: 16 bitli mashinalarda bo'lsa-da, int da -2147483648 ni ifodalay olmaysiz.
qo'shib qo'ydi muallif Martin Bonner, manba
@MartinBonner, OPning kutilganidan tashqariga chiqmasin. U oddiygina ikkita qo'shimchani bilishni istaydi va u "32-bitlik mashinada"
qo'shib qo'ydi muallif Lưu Vĩnh Phúc, manba
64-bitli mashinada ham xuddi shunday. int ning hajmi bu sizning kompyuteringiz emas, balki muhimdir
qo'shib qo'ydi muallif Lưu Vĩnh Phúc, manba
Eslatma: -INT_MIN-ning qiymati S-da aniqlanmagan. Ko'pincha amalga oshiriladigan dasturlarni INT_MIN-ga qaytara olaman deb taxmin qildim, lekin ular shart emas.
qo'shib qo'ydi muallif mwfearnley, manba
Qarang: quora.com /… kabi savol/javob uchun.
qo'shib qo'ydi muallif Dwayne Towell, manba

6 javoblar

(To'liq bo'lmagan) tamsayı doimiyligini noto'g'ri qilish:

- (- 2147483648) ifodasi Cda yaxshi aniqlangan bo'lsa-da, nima uchun bu shunday ekanligi aniq bo'lmasligi mumkin.

-2147483648 yozganingizda, unikal minus operatori integer sobitiga tatbiq etiladi. Agar 2147483648 kodi int sifatida ifoda eta olmasa, u s long yoki long (qaysi biri mos keladigan bo'lsa), u holda bu qiymat ni qoplash uchun C standarti tomonidan kafolatlanadi.

Buni tasdiqlash uchun uni quyidagicha tekshirishingiz mumkin:

printf("%zu\n", sizeof(-2147483648));

Bu mening mashinamda 8 ni beradi.

Keyingi qadam - operatorini qo'llashdir. Bu holda oxirgi qiymat 2147483648L (oxirida long sifatida ifodalanadi deb taxmin qilinsa). Agar siz buni int ob'ektiga quyidagi tarzda tayinlamoqchi bo'lsangiz:

int n = -(-2147483648);

u holda haqiqiy xatti-harakatlar dastur-ta'riflangan dir. Standartni nazarda tutgan holda:

C11 §6.3.1.3/3 imzolangan va imzolangan bo'lmagan tamsayılar

     

Aks holda, yangi turdagi imzolanadi va qiymatni ko'rsatish mumkin emas   unda; natija, yoki amalga oshirish yoki aniqlanadi   amalga oshirishni aniqlovchi signal ko'tariladi.

Eng keng tarqalgan usul shunchaki yuqori bitlarni kesishdir. Masalan, GCC hujjatlarni quyidagi kabi:

N kengligi turiga aylantirish uchun qiymati 2 ^ N modulini qisqartiradi   turdagi turlarga bo'lish; hech qanday signal ko'tarilmaydi.

Kontseptual tarzda, kenglik 32 turiga konvertatsiya qilish bitwise VA ish bilan tasvirlangan bo'lishi mumkin:

value & (2^32 - 1)//preserve 32 least significant bits

In accordance with two's complement arithmetic, the value of n is formed with all zeros and MSB (sign) bit set, which represents value of -2^31, that is -2147483648.

int ob'ektini buzish:

2147483648 qiymatini saqlaydigan int ob'ektini bekor qilmoqchi bo'lsangiz, u holda ikkita kompleman mashinasini qabul qilsangiz, dastur undefined behavior ni namoyish qiladi:

n = -n;//UB if n == INT_MIN and INT_MAX == 2147483647

C11 §6.5/5 ifodalari

     

Agar bir istisno holat , bir   ifodasi (ya'ni, natijalar matematik tarzda aniqlanmagan bo'lsa)   uning turiga taaluqli qiymatlar oralig'ida emas)   noma'lum.

Qo'shimcha ma'lumot:


*) Chiqarilgan C90 standartida long long turi yo'q edi va qoidalar boshqacha edi. Xususan, int , long int , unsigned long int (C90 §6.1.3.2 Integer constants)

†) bu kamida +9223372036854775807 (C11 §5.2.4.2.1/1) bo'lishi kerak LLONG_MAX ga bog'liq.

74
qo'shib qo'ydi
@ M.M: tuzatildi. Men "norasmiy ravishda" targ'ibot so'zini ishlatardim, lekin siz haqsiz, u tilni turli tomonlari bilan chalkashtirmaslik uchun tuzatilgan bo'lishi kerak.
qo'shib qo'ydi muallif Grzegorz Szpetkowski, manba
@ Random832: O'ylaymanki, bu masala faqat alohida savolga loyiqdir, lekin qisqagina bir ko'z tashlang: "http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_298.htm" "rel =" nofollow noreferrer "> DR # 298 . Buning sababi shundaki, agar u 9223372036854775808 kodi tomonidan namoyish etilmasa, cheklov buzilishi (C11 §6.4.4/2) uzoq muddatli turini (shu sababli u LLONG_MAX dan yuqori), 2) bajarilgan kengaytirilgan tamsayı turlarini qo'llab-quvvatlamaydi (masalan, GCC emas).
qo'shib qo'ydi muallif Grzegorz Szpetkowski, manba
2147483648 hech narsa uchun targ'ib qilinmaydi. Bu kod int , uzun yoki uzunligi (qaysi biri mos keladigan eng kichik) turiga ega. "Rag'batlantirish", aslida int dan ancha kichik bo'lgan qiymatga ishora qiladi, bir ifodada ishlatilganda turli turdagi qiymatga o'zgaradi
qo'shib qo'ydi muallif M.M, manba
@Hurkyl C, 2147483648 da bir tamsayı emas, balki integer doimiyi deb belgilansin. C ning literallari 2147483648 dan farqli ravishda string litals va compound literals kabi manzillarini egallashi mumkin.
qo'shib qo'ydi muallif chux, manba
u faqat zamonaviy kompilyatorlarda (C99 yoki C ++ 11 va undan keyingi versiyalarda) uzoq vaqt kodli ga da'vat qilinadi. Eski kompilyatorlarda bu ajablantiradigan natijalar beradi Nima uchun -2147483648 va (int) -2147483648 orasida farq bor, Yugurish uchun minimal 32-bit tamsayıni (-2147483648) quyish musbat raqamni (2147483648.0) beradi
qo'shib qo'ydi muallif Lưu Vĩnh Phúc, manba
"To'g'ridan-to'g'ri uzoq yoki uzoq muddatli * (bu qiymatni qoplash kafolatlangan) *" - " -9223372036854775808 haqida nimani anglatadi?
qo'shib qo'ydi muallif Random832, manba
Bu javob faqat tamsayı literatürlerine tegishli ekanligini ta'kidlash kerak; xususan, -2147483648 qiymatini o'z ichiga olgan int ob'ektini inkor etishga taalluqli emas.
qo'shib qo'ydi muallif Hurkyl, manba

Eslatma: ushbu javob hali ham ko'plab kompilyatorlar tomonidan ishlatilayotgan eskirib qolgan ISO C90 standartiga taalluqli emas.

Birinchidan, C99, C11da - (- 2147483648) == -2147483648 iborasi aslida FALSE dir:

int is_it_true = (-(-2147483648) == -2147483648);
printf("%d\n", is_it_true);

bosib chiqaradi

0

So how it is possible that this evaluates to true? The machine is using 32-bit two's complement integers. The 2147483648 is an integer constant that quite doesn't fit in 32 bits, thus it will be either long int or long long int depending on whichever is the first where it fits. This negated will result in -2147483648 - and again, even though the number -2147483648 can fit in a 32-bit integer, the expression -2147483648 consists of a >32-bit positive integer preceded with unary -!

Siz quyidagi dasturni sinab ko'rishingiz mumkin:

#include 

int main() {
    printf("%zu\n", sizeof(2147483647));
    printf("%zu\n", sizeof(2147483648));
    printf("%zu\n", sizeof(-2147483648));
}

Bunday mashinadagi chiqimlar ehtimol 4, 8 va 8 bo'lishi kerak.

Keling, -2147483648 negated bo'lsa, yana long int yoki uzoq turi bo'lgan +214783648 >, va hamma narsa yaxshi.

C99, C11 da integer doimiy ifodasi - (- 2147483648) barcha mos keladigan ilovalarda yaxshi belgilangan.


Endi, bu qiymat 32 xil va ikkita komplement tavsifi bilan int turidagi o'zgaruvchiga tayinlanganida, qiymat unchalik ahamiyatga ega emas - 32-bitli 2 ning qo'shimchaidagi qiymatlar -2147483648 dan 2147483647gacha.

C11 standarti 6.3.1.3p3 , shunday deydi: butunlay ayirboshlash:

      
  • [qachon] yangi turdagi imzolangan va qiymat uning ichida mavjud emas; natija implementation-defined yoki implement-defined signalidan iborat.
  •   

Ya'ni, C standarti, bu holatda qiymatning nima ekanligini aniqlamaydi yoki dasturning bajarilishini to'xtatish uchun signal ko'tarilganligi sababli to'xtab qolishiga imkon bermaydi, balki uni amalga oshirishga qoldiradi (ya'ni, kompilyatorlar ) qanday ishlashni hal qilish uchun (C11 3.4.1) :

amalga oshirilgan xatti-harakatlar

     

Tanlovning qanday amalga oshirilganligini hujjatlashtirilmaganligi aniqlanmagan xatti-harakatlar

va (3.19.1) :

amalga oshirilgan qiymat

     

Tanlovning qanday amalga oshirilganligini hujjatlashtirilmagan qiymati aniqlanmagan


Sizning holatingizda, dasturni aniqlagan xatti- bu qiymat 32 ta eng past bit bit [*]. 2-ilovaga muvofiq, uzun (long) long int value 0x80000000 31 bitga ega va barcha boshqa bitlar tozalanadi. 32-bitli ikkita qo'shimcha tamsayılarda bit 31 belgining bitikidir - bu raqam salbiy; barcha qiymat bitlari nolga teng bo'lsa, bu qiymat minimal ko'rsatish mumkin bo'lgan raqam, ya'ni INT_MIN .


[*] GCC bu holda amalga oshirilgan dasturni aniqlagan xatti-harakatlarini quyidagicha ifodalaydi :

Agar qiymat shu turdagi ob'ektda ifodalanmasa, natijada butun sonni imzolangan tamsayı turiga aylantiruvchi natijani yoki signalni ko'taradi (C90 6.2.1.2, C99 va C11 6.3.1.3).

     

N kengligidagi turga o'tkazish uchun qiymat turi ichida bo'lishi uchun qiymat 2 ^ N kamayadi; hech qanday signal ko'tarilmaydi.

16
qo'shib qo'ydi

int uchun 32-bitli ikkita komplement ko'rinishini o'z ichiga olgan C ilovasida, bu bir C savol emas, balki yagona tartibsizlik operatorini int -2147483648 qiymati undefined hisoblanadi. Ya'ni, C tili, bunday operatsiyani baholash natijalarini tayinlashni rad etadi.

Birinchidan, umumiy holda, - operatori ikkita qo'shimcha arifmetikada qanday aniqlanadi: x 1 qo'shib qo'ying. Xuddi shu ta'rif, shuningdek bit belgilaridan kamida bittasi bo'lgan har qanday salbiy son uchun ham xizmat qiladi.

Ammo kichik muammolar paydo bo'ladi, lekin ikkita son uchun bit qiymatlari mavjud emas: 0, hech qanday bit o'rnatilgan emas va bittadan bitikli belgisi bo'lgan raqam (-2147483648 32-bitli ko'rinishda). Buning har ikkisining bittasini almashtirganingizda, siz o'rnatilgan barcha qiymat bitlari bilan yakunlanadi. Shuning uchun, keyinchalik 1 qo'shsangiz, natijalar bit bitlarini to'kiladi. Agar qo'shimchani raqamni imzolamasa, bittasini bittadan bitik sifatida ko'rib, tasavvur qiling

    -2147483648 (decimal representation)
-->  0x80000000 (convert to hex)
-->  0x7fffffff (flip bits)
-->  0x80000000 (add one)
--> -2147483648 (convert to decimal)

Xuddi shu tarzda nolni qaytarish uchun ham amal qiladi, lekin bu holatda 1 ta qo'shilganidan keyin to'lib toshgan belgining biti ham bor. Agar toshib ketish e'tiborga olinmasa, natijada hosil bo'lgan 32 ta past darajadagi bitlar nolga teng, shuning uchun -0 == 0.

6
qo'shib qo'ydi
Men Grzegorz Szpetkowskining bunga erishishidan qo'rqaman: - (- 2147483648) ifodasi juda yaxshi ta'riflangan.
qo'shib qo'ydi muallif chqrlie, manba
@chkrlie, siz to'g'ri, albatta, lekin bu savolning nuqsoni yo'q. Men javobimning bu qismini ushbu texnikaviylikni to'g'rilash uchun qaytadan ochdim.
qo'shib qo'ydi muallif John Bollinger, manba
Bu juda yaxshi ta'riflangan, chunki zamonaviy kompilyatorlardagi uzoq vaqt va uzunligi -2147483648 kodi> imzolanmagan uzun ni eski sahifalarda . Har ikki holatda ham natijalar farq qiladi, ammo ular hali ham aniqlangan
qo'shib qo'ydi muallif Lưu Vĩnh Phúc, manba
@chqrlie: Agar siz faqat -2147483648 qiymatini o'z ichiga olgan int o'zgarmaydiganini bekor qilsangiz nima sodir bo'lishini so'ramasdan, OP o'ng harflar haqida gapiradi deb hisoblasangiz.
qo'shib qo'ydi muallif Hurkyl, manba

Shu bilan birga, 000dan (001 002 003 dan 500 tagacha) 500 gradusgacha bo'lgan lenta pastki o'lchagichni saralash 500 ga teng bo'ladi va uni orqaga qaytarish uchun 000dan orqada (999 998 997 ...) 500 .

Bu ikkita qo'shimcha belgisidir. Albatta, ikkita qo'shimcha tamg'asi konvensiyasi belgining eng yuqori bitini hisobga olish uchun natija vakili oralig'ini to'ldiradi, xuddi 2000000000 + 2000000000 kabi vakili oralig'i to'kiladi.

Natijada, protsessorning "to'ldirish" biti o'rnatiladi (bu tizim mashinaning arifmetik bayroqlariga kirishni talab qiladi, aksariyatida assemblerdan tashqari dasturlash tillarining ko'pchiligida bunday holat mavjud emas). Bu 2 ta qo'shimcha raqamni bekor qilganda "toshib ketish" bitini o'rnatadigan faqat qiymati: boshqa qiymatning negizligi 2 komplementi tomonidan taqdim etiladigan intervalda yotadi.

1
qo'shib qo'ydi

Bu C versiyasiga, amalga oshirishning o'ziga xos xususiyatlariga va o'zgaruvchan yoki tijoriy qiymatlar haqida gapirayotganimizga bog'liq.

Birinchidan, tushunish kerakki, "-2147483648" da mutlaq tamsaytli matnlar mavjud emas.

Int va uzunligi ham 32 bit, ham uzunligi 64 bit bo'lgan odatiy 32-bitlik platformada ishlayotganimizni va gapni hisobga oling.

(- (- 2147483648) == -2147483648)

Derivat 2147483648 ni tutishi mumkin bo'lgan bir turni topishi kerak, C99 kompilyatorida u "uzoq vaqt" turini qo'llaydi, lekin C90 derleyici "unsigned uzun" turini qo'llashi mumkin.

Derleyici uzoq vaqtdan beri foydalanadigan bo'lsa, u holda hech narsa to'kilmaydi va taqqoslash noto'g'ri. Agar derleyici uzoq vaqt imzolangan bo'lmagan belgini qo'llamasa, imzolanmagan yoppasiga qoidalar o'ynaydi va taqqoslash haqiqiydir.

0
qo'shib qo'ydi

Men matematikani oddiy qilish uchun 4-bitli raqamni ishlataman, lekin fikr bir xil.

4-bitli raqamda mumkin bo'lgan qiymatlar 0 dan 1111 gacha. Bu 0 dan 15 gacha bo'lishi mumkin, ammo salbiy sonlarni bildirmoqchi bo'lsangiz, birinchi bit belgini ko'rsatish uchun ishlatiladi (0 uchun ijobiy va 1 uchun salbiy).

Shunday qilib, 1111 emas, balki 15. Birinchi bit 1 bo'lsa, u salbiy raqam. Uning qiymatini bilish uchun avvalgi javoblarda aytilganidek, ikkita qo'shimcha usulni qo'llaymiz: "bitni invert va 1-qo'shing":

  • Bitlarni tirnaltirish: 0000
  • qo'shib 1: 0001

Ikkilik ichida 0001 1 kasrda, shuning uchun 1111 -1 bo'ladi.

Ikki komplement uslubi ikkala yo'lni ham o'z ichiga oladi, shuning uchun siz uni biron-bir raqam bilan ishlatsangiz, u sizning raqamingizning teskari belgisi bilan ikkilik vakillikini beradi.

Keling, 1000ni ko'raylik. Birinchi bit 1, shuning uchun salbiy raqam. Ikki komplement usulini qo'llash:

  • bitni invert: 0111
  • 1: 1000 qo'shing (kasrda 8)

Shunday qilib, 1000 - 8. Agar biz - (- 8) ni amalga oshirsak, ikkilik jihatdan - (1000) degan ma'noni anglatadi. Bu aslida ikkita komplement usulini 1000 da ishlatishni anglatadi. natijasi ham 1000. Masalan, 4-bitli sonda - (- 8) tengdir -8.

Ikki tomonlama kodda 32 bitli -2147483648 kodi 1000 .. (31 nol) hisoblanadi, lekin agar siz ikki komplement usulini qo'llasangiz, bir xil qiymat (natija bir xil raqam).

That's why in 32-bit number -(-2147483648) is equals -2147483648

0
qo'shib qo'ydi