gcc-fråga om math.h

Här diskuteras programmering och utveckling
Användarvisningsbild
Johnny Rosenberg
Inlägg: 1256
Blev medlem: 23 jun 2007, 16:18
OS: Ubuntu
Utgåva: 22.10 Kinetic Kudu
Kontakt:

gcc-fråga om math.h

Inlägg av Johnny Rosenberg »

När jag övade lite på programmering i C i mitten av 1980-talet har jag för mig att man var tvungen att inkludera någon flagga (-lm om jag inte minns fel) när man kompilerade något som hade raden #include <math.h> i sig, men efter att ha sökt på diverse saker, som ”math” och ”-lm” i manualsidorna (som jag hittade på nätet) för gcc kunde jag inte se att detta fanns beskrivet. Behöver man inte ha med denna flagga numera? Känner mig lite mossig här… ha ha ha…
:P
Eller kan det ha berott på att jag använde en annan kompilator på den tiden? Vet att vi hade gcc men osäker på om just jag använde den… Vet att jag kompilerade C-program med cc i början och att jag provade gcc någon gång (vi hade en stor dator i något rum någonstans på högskolan där jag gick och alla körde via terminaler, operativsystemet var Unix – Berkeley och System V, om nu detta kan vara av intresse för någon).
Senast redigerad av 1 Johnny Rosenberg, redigerad totalt 28 gånger.
Vänliga hälsningar

Johnny Rosenberg
ジョニー・ローゼンバーグ

IEEE 1541 - binära prefix
ISO 8601 - datum och tid
Användarvisningsbild
Konservburk
Inlägg: 5919
Blev medlem: 07 apr 2007, 22:28

Re: gcc-fråga om math.h

Inlägg av Konservburk »

Vad jag vet så behövs flaggan -lm fortfarande.

Enkel testkod:

Kod: Markera allt

#include <stdio.h>
#include <math.h>

main()
{
	int x = 2;
	printf("%f\n", sqrt(x));
}
Utan -lm:

Kod: Markera allt

$ gcc lm.c
/tmp/ccnvxP1x.o: In function `main':
lm.c:(.text+0x36): undefined reference to `sqrt'
collect2: ld returned 1 exit status
Med -lm:

Kod: Markera allt

$ gcc lm.c -lm
$
Lars
Inlägg: 6191
Blev medlem: 14 jan 2007, 19:31
OS: Ubuntu
Utgåva: 22.10 Kinetic Kudu
Ort: Stockholm

Re: gcc-fråga om math.h

Inlägg av Lars »

Det här var en intressant fråga :)

Jag har provat lite olika funktioner med gcc 4.4.3 på Intel Core 2 Duo (amd64) och gcc 4.1.2 på PowerPC. På amd64 fungerar både sqrt() och sin() utan -lm, men på PowerPC är det bara sqrt() som fungerar utan -lm.

Förklaringen är att processorn har speciella instruktioner för dessa funktioner så det behövs inget extra bibliotek för att utföra beräkningen. Om processorn saknar det stödet så implementeras motsvarande funktion i libm och då krävs -lm.

Konservburk, vad använder du för arkitektur?
Användarvisningsbild
Johnny Rosenberg
Inlägg: 1256
Blev medlem: 23 jun 2007, 16:18
OS: Ubuntu
Utgåva: 22.10 Kinetic Kudu
Kontakt:

Re: gcc-fråga om math.h

Inlägg av Johnny Rosenberg »

Men jag fattar inte riktigt varför -lm ska vara där. Kan inte kompilatorn lista ut vad den behöver enbart genom att det står #include <math.h> i själva koden? Låter som att användaren gör ett jobb kompilatorn lika gärna kunde göra själv…
Vänliga hälsningar

Johnny Rosenberg
ジョニー・ローゼンバーグ

IEEE 1541 - binära prefix
ISO 8601 - datum och tid
Lars
Inlägg: 6191
Blev medlem: 14 jan 2007, 19:31
OS: Ubuntu
Utgåva: 22.10 Kinetic Kudu
Ort: Stockholm

Re: gcc-fråga om math.h

Inlägg av Lars »

Men -lm är egentligen ett argument till länkaren och inte till kompilatorn, och länkaren ser aldrig #include <math.h> ;)

Det skulle förmodligen gå att göra en kompilator/länkare som fixar det där automatiskt. Men av tradition görs ju inte det. Jag vet inte riktigt varför det är som det är.
Användarvisningsbild
Konservburk
Inlägg: 5919
Blev medlem: 07 apr 2007, 22:28

Re: gcc-fråga om math.h

Inlägg av Konservburk »

Lars skrev:Konservburk, vad använder du för arkitektur?
Helt vanlig x86 (med gcc 4.3.4). Vilken assemblerkod genereras av gcc -S i de fall du inte behöver -lm?
Johnny Rosenberg skrev:Kan inte kompilatorn lista ut vad den behöver enbart genom att det står #include <math.h> i själva koden?
Det är faktiskt inte raden #include <math.h> som kräver -lm, utan det är anropen till funktioner i libm.so, t.ex. sqrt().

Följande program kräver inte -lm:

Kod: Markera allt

#include <math.h>
main(){}
Inte heller detta kräver -lm:

Kod: Markera allt

#include <math.h>
double main() {
	return sqrt(2);
}
I det här fallet ersätter kompilatorn sqrt(2) med en konstant istället för ett anrop till libm.so. Gör vi det anigen mer komplicerat så blir anropet kvar och då krävs -lm:

Kod: Markera allt

#include <math.h>
double main() {
	int x = 2;
	return sqrt(x);
}
Lars
Inlägg: 6191
Blev medlem: 14 jan 2007, 19:31
OS: Ubuntu
Utgåva: 22.10 Kinetic Kudu
Ort: Stockholm

Re: gcc-fråga om math.h

Inlägg av Lars »

Konservburk skrev:Helt vanlig x86 (med gcc 4.3.4). Vilken assemblerkod genereras av gcc -S i de fall du inte behöver -lm?
Jag hade nog inte helt rätt igår kväll. Det var nog kompilatorn som optimerade bort lite konstanter så att anropet aldrig behövdes. Men det går att få till m.h.a. -ffast-math. Då blir det så här på amd64:

Kod: Markera allt

	movq	%rax, -8(%rbp)
	movsd	-8(%rbp), %xmm1
	cvtpd2ps	%xmm1, %xmm1
	sqrtss	%xmm1, %xmm0
	ucomiss	%xmm0, %xmm0
Och på PowerPC:

Kod: Markera allt

        lfd 0,8(31)
        fsqrt 0,0
Det roliga med x86-arkitekturen är att det finns två sätt att utföra flyttalsoperationer på, du kan ange om du vill använda den äldre 387-varianten eller den nyare SSE. Som kuriosa kan nämnas att 387 var en separat krets. Om man hade gott om pengar kunde man köpa till den och montera i sin 386-dator. När 486:an kom fanns det två varianter, 486SX utan flyttalsdel och 486DX med inbyggt stöd för flyttal. 387-varianten ser ut så här:

Kod: Markera allt

	movq	%rax, -8(%rbp)
	fldl	-8(%rbp)
	fsqrt
	fstps	-20(%rbp)
	movss	-20(%rbp), %xmm0
Användarvisningsbild
Konservburk
Inlägg: 5919
Blev medlem: 07 apr 2007, 22:28

Re: gcc-fråga om math.h

Inlägg av Konservburk »

Lars skrev:Jag hade nog inte helt rätt igår kväll. Det var nog kompilatorn som optimerade bort lite konstanter så att anropet aldrig behövdes. Men det går att få till m.h.a. -ffast-math.
Det var intressant. Tack för infon!

Manualen säger:

Kod: Markera allt

       -ffast-math
           Sets -fno-math-errno, -funsafe-math-optimizations, -ffi-
           nite-math-only, -fno-rounding-math, -fno-signaling-nans and
           -fcx-limited-range.

           This option causes the preprocessor macro "__FAST_MATH__" to be
           defined.

           This option is not turned on by any -O option since it can result
           in incorrect output for programs which depend on an exact implemen-
           tation of IEEE or ISO rules/specifications for math functions. It
           may, however, yield faster code for programs that do not require
           the guarantees of these specifications.
Jag provade med ett par olika alternativ och kom fram till att för mig räckte det med -fno-math-errno eller -ffinite-math-only för att anropet till libm.so ska optimeras bort.
Konservburk skrev:Gör vi det anigen mer komplicerat så blir anropet kvar och då krävs -lm:

Kod: Markera allt

#include <math.h>
double main() {
	int x = 2;
	return sqrt(x);
}
Jag upptäckte nu att i det här fallet optimeras anropet bort (och ersätts med en konstant) redan med -O1, så då krävs med andra ord inte -lm. Men vet kompilatorn inte vad x är så blir anropet kvar även med -O2 och -O3, och då krävs åter igen -lm (om vi inte använder -ffast-math eller liknande):

Kod: Markera allt

#include <math.h>
double main(int x) {
	return sqrt(x);
}
Användarvisningsbild
Johnny Rosenberg
Inlägg: 1256
Blev medlem: 23 jun 2007, 16:18
OS: Ubuntu
Utgåva: 22.10 Kinetic Kudu
Kontakt:

Re: gcc-fråga om math.h

Inlägg av Johnny Rosenberg »

Okej, så långt allt väl, men vad är bäst…? Verkar ju finnas många sätt att göra samma sak på, men det måste väl finnas för- och nackdelar med de olika sätten? Om man exempelvis vill att programmet ska vara så snabbt som möjligt, vad väljer man lämpligen då och vilka nackdelar kan det medföra?

Hm… känns om att svaret på den frågan kan bli väldigt komplext, i och för sig…
Vänliga hälsningar

Johnny Rosenberg
ジョニー・ローゼンバーグ

IEEE 1541 - binära prefix
ISO 8601 - datum och tid
Användarvisningsbild
Konservburk
Inlägg: 5919
Blev medlem: 07 apr 2007, 22:28

Re: gcc-fråga om math.h

Inlägg av Konservburk »

Johnny Rosenberg skrev:Okej, så långt allt väl, men vad är bäst…? Verkar ju finnas många sätt att göra samma sak på, men det måste väl finnas för- och nackdelar med de olika sätten? Om man exempelvis vill att programmet ska vara så snabbt som möjligt, vad väljer man lämpligen då och vilka nackdelar kan det medföra?
Det här har inget alls med math.h att göra längre? Ska vi fortsätta här så är det kanske läge att byta namn på tråden.

Det är ofta en avvägning mellan stabilitet och prestanda. Jag brukar nöja mig med -march=native -O2 när jag kompilerar för eget bruk på samma dator. Sen finns -O3 också som optimerar ännu mer, och så ett helt gäng andra saker i stil med -ffast-math. Risken är dock att programmen beter sig instabilt och kanske till och med krashar.
Lars
Inlägg: 6191
Blev medlem: 14 jan 2007, 19:31
OS: Ubuntu
Utgåva: 22.10 Kinetic Kudu
Ort: Stockholm

Re: gcc-fråga om math.h

Inlägg av Lars »

Just det där mer -ffast-math har inte så mycket med stabilitet att göra. Det handlar mer om hur väl beräkningarna ska följa IEEE 754, om errno ska sättas, hur Inf och NaN ska behandlas o.s.v. Jag skulle vilja hävda att de allra flesta program fungerar bra med -ffast-math.

Men om man håller på med avancerade matematiska algoritmer så kan datorernas flyttal spela en en del spratt. Små fel här och där kan fortplanta sig genom iterationerna och leda till division med noll eller andra fel. I sådana fall är det bra att använda strikta IEEE-beräkningar som åtminstone har ett beteende som är väl känt. Men man får ändå se upp för en del fallgropar.

När man skriver egna program tycker jag gott att man kan använda både -O3 och -ffast-math. Då har man ju förhoppningsvis tillräcklig förståelse av programmet för att kunna hitta de buggar som uppstår. Men man ska vara medveten om att den typen av buggar kan vara ganska obskyra och svåra att hitta :)
Skriv svar

Återgå till "Programmering och webbdesign"