2011-09-30 19 views
18

mam C++/C kod mieszanych, które buduję naDziwny błąd w wykorzystaniu abs() natknąłem się niedawno

a) Visual C++ 2010 ekspresowe (wersja darmowa) na Win-7 x32.

b) Środowisko Cygwin/Gcc zainstalowane w wersji Premium systemu Windows 7 Home x32. Gcc wersja 3.4.4 (cygming szczególną GDC 0,12 korzystając DMD 0,125)

c) Ubuntu 10,04 linux- GCC wersja 4.4.3 (4.4.3-4ubuntu5 Ubuntu)

mam poniżej kodu (jest to funkcja element do mojego zdefiniowane przez użytkownika klasy), która oblicza wartość bezwzględną przekazany obiekt myhalf-

myhalf::myhalfabs(myhalf a) 
{ 
    float tmp; 
    tmp = abs(a.value); //This abs is from math.h :- float abs(float) 
    return tmp; 
} 

ten działał prawidłowo w sposób pożądany w MS - Visual C++ 2010 aBS (I) nr ujemnych zwrócono poprawnie jako + ve nos mający tę samą wartość. Dziwnie, gdy zbudowałem ten kod na b) Cygwin/gcc environment & c) Linux-gcc 4.4.3 wspomniano powyżej, otrzymywałem niepotrzebne wydruki. Tak rozgrzana gdb, a po wielu potu i „podejścia binarnie wyszukiwania” na kod, aby zdecydować, gdzie się zaczęło dzieje źle, uderzył ten fragment kodu, jak pokazano powyżej:

tmp = abs(a.value); 

który zachowuje się dziwnie pod cygwin/gcc.

Dla liczb-abs abs() wracał 0 (zero). WTF ??

potem jako obejście, unikać wywoływania abs() z stdlib i kodowane własną abs jak poniżej:

myhalf::myhalfabs(myhalf a) 
{ 
    float tmp; 
    unsigned int tmp_dbg; 
    // tmp = abs(a.value); 
    tmp_dbg = *(unsigned int*)(&a.value); 
    tmp_dbg = tmp_dbg & 0x7FFFFFFF; 
    tmp = *(float*)(&tmp_dbg); 

    return tmp; 
} 

To działało w porządku na Cygwin/gcc & linux-gcc i wyjście było jak trzeba, I oczywiście działało dobrze na MS-Visual C++ 2010.

To jest cały Makefile dla buildów cygwin/gcc i linux-gcc, których używam. Tylko jeśli ktoś supspects coś śmierdzi tam: -

OBJS= <all my obj files listed here explicitly> 

HEADERS= <my header files here> 
CFLAGS= -Wall 
LIBS= -lm 
LDFLAGS= $(LIBS) 

#MDEBUG=1 
ifdef MDEBUG 
CFLAGS += -fmudflap 
LDFLAGS += -fmudflap -lmudflap 
endif 

myexe: $(OBJS) 
    g++ $(OBJS) $(LDFLAGS) -o myexe 

%.o: %.cpp $(HEADERS) Makefile 
    g++ $(CFLAGS) -c $< 

clean: 
    rm -f myexe $(OBJS) 

1] Co tu się dzieje? Jaka jest przyczyna tego dziwnego błędu?

2] Czy używam starej wersji gcc na cygwin, która ma ten problem jako znany błąd?

3] Czy ta funkcja float abs (float) jest przestarzała lub zastępuje ją nowsza wersja?

Wszelkie wskaźniki są przydatne.

+2

Przydałoby się, jeśli utworzyłeś małą próbkę, aby każdy mógł wziąć fragment, skompilować niezmodyfikowany i zobaczyć na własne oczy, co masz na myśli. – PlasmaHH

+4

Funkcja 'abs()' zwykle zwraca 'int' i przypisujesz ją do' float'. Jesteś pewien, że to nie jest przyczyną twoich problemów? – larsks

+1

Czy zachowujesz się tak samo w przypadku 'fabs'? – littleadv

Odpowiedz

27

math.h ma wersję C abs, która działa na int s. Użyj przeciążenia C++ lub użyj fabs() dla wersji zmiennoprzecinkowej C.

+1

Tak, widzę, że włączam math.h (przestarzałe użycie, aby zawrzeć biblioteki C w kodzie C++, moje złe) Spróbuję cmath.h lub cstdlib, as również zasugerował Mark.B – goldenmean

+3

@goldenmean: cmath, a nie cmath.h. standardowe nagłówki C++ nie mają rozszerzenia – PlasmaHH

+0

@PlasmaHH - Yean, przepraszam. Jest to . – goldenmean

11

Po pierwsze, abs() pobiera i zwraca int. Powinieneś użyć fabs(), która pobiera i zwraca wartość zmiennoprzecinkową.

Teraz abs() jesteś kończąc powołaniem jest rzeczywiście GCC built-in function, który zwraca int, ale najwyraźniej akceptuje float argument i zwraca 0 w tej sprawie.

+1

Rzeczywiście rzutuje 'float' na' int' i obcina wartość. To 0.001 zostaje obcięte do 0, 1.001 zostaje obcięte do 1, itd. –

7

Prawie na pewno się dzieje, że w przypadku MSVC pojawia się przeciążenie C++ abs (które z jakiegoś powodu może zostać przeniesione do globalnej przestrzeni nazw). A następnie w g ++ nie jest to pobranie wersji C++ (która nie jest niejawnie zaimportowana do globalnej przestrzeni nazw), ale wersja C, która działa i zwraca wartość int, która następnie obcina dane wyjściowe do zera.

Jeśli użyjesz #include <cmath> i użyjesz std::abs, powinien on działać poprawnie na wszystkich twoich platformach.