2016-04-18 7 views
8

Jak mogę szybko przekonwertować uint32_t na int32_t z zawijaniem, w C++?Konwersja uint32_t na int32_t bez ryzyka przepełnienia lub nadmiernej złożoności

Niektóre stara:

uint32_t y = UINT32_MAX; 
int32_t x = (int32_t)y; // UB on overflow 
int32_t x = *(int32_t*)&y; // does this violate strict aliasing? 
+2

Czy teoretycznie niemożliwe jest dokonanie konwersji bez ryzyka przepełnienia? – pingul

+0

Co powinien zrobić, gdy wystąpi przepełnienie? Zawijanie dopełnienia dwójki (co tak naprawdę "nieokreślone zachowanie" faktycznie stanowi 99% wszystkich kompilatorów/procesorów na świecie)? – ShadowRanger

+0

Zakładam, że 'y' jest zadeklarowane jako' uint32_t y; '(dobrze by było dodać to do pytania) –

Odpowiedz

5

int32_t x = (int32_t)y; nie jest przepełnienie i nie UB. Przepełnienie występuje wtedy, gdy operacja arytmetyczna daje wynik poza zakresem reprezentowalnych wartości. Jednak konwersja nie jest operacją arytmetyczną.

Ta sytuacja jest określana jako w przypadku implementacji. Wszystkie implementacje, o których wiem, definiują zachowanie jako nie dokonujące żadnych zmian w reprezentacji.

Uwaga: tutaj nie jest wymagana obsada. Możesz napisać int32_t x = y;. W praktyce jest to prostsze i zawsze będzie działać. Tak wiele kodu polega na tym, że żaden sprzedawca nigdy nie zdefiniuje żadnego innego zachowania (nie, że i tak mają ku temu jakiekolwiek powody).


int32_t x = *(int32_t*)&y nie jest UB. Nie narusza ścisłego aliasingu, ponieważ podpisana wersja typu może aliasować niepodpisaną wersję. Ten kod gwarantuje wytworzenie int32_t z tym samym odwzorowaniem, co odpowiadający uint32_t (tj. "Zawijanie", ponieważ te typy mają gwarantowane uzupełnienie 2).

5
union { 
    int32_t i; 
    uint32_t u; 
} u; 
u.i = ...; 
printf("%" PRIu32 "\n", u.u); 

To i memcpy(&uint_var, &int_var, sizeof uint_var) są dwa standardowe sposoby wykonywania takiej konwersji bez wywoływania niezdefiniowanej zachowanie.

Zobacz także:

+0

Co powiesz na odwzorowanie pułapek dla podpisanej int? –

+0

@RolandIllig Dokładne szerokości całkowite nie mają bitów dopełniających i jako takie nie mogą mieć odwzorowań pułapek. – a3f

+0

Czytanie 6.2.6.2p2 z C99 wydaje się mówić inaczej. Chociaż może nie być bitu dopełniającego, nadal może być reprezentacja pułapki. –

1
#include <assert.h> 
#include <limits.h> 
#include <stdlib.h> 

int makesigned(unsigned x) { 
    if (x <= (unsigned) INT_MAX) { 
    return (int) x; 
    } 

    /* assume 2's complement */ 
    if (x >= (unsigned) INT_MIN) { 
    return 0 - (int)(-x); 
    } 

    abort(); 
    return 0; 
} 

int main(void) { 
    assert(makesigned(0) == 0); 
    assert(makesigned(INT_MAX) == INT_MAX); 
    assert(makesigned(UINT_MAX) == -1); 
    assert(makesigned(INT_MIN) == INT_MIN); 
    return 0; 
}