tft每日頭條

 > 圖文

 > c語言最基本的類型

c語言最基本的類型

圖文 更新时间:2024-07-29 07:08:41

數學庫中包含許多有用的數學函數。math.h頭文件提供這些函數的原型。表16.2中列出了一些聲明在math.h中的函數。注意,函數中涉及的角度都以弧度為單位(1弧度=180/π=57.296度)。參考資料V“新增C99和C11标準的ANSI C庫”列出了C99和C11标準的所有函數。

c語言最基本的類型(C語言數學庫的3種類型)1

Some ANSI C Standard Math Functions

1 三角問題

我們可以使用數學庫解決一些常見的問題:把x/y坐标轉換為長度和角度。例如,在網格上畫了一條線,該線條水平穿過了4個單元(x的值),垂直穿過了3個單元(y的值)。那麼,該線的長度(量)和方向是什麼?根據數學的三角公式可知:

magnitude = square root (x^2 y^2)

and

angle = arctangent (y/x)

數學庫提供平方根函數和一對反正切函數,所以可以用C程序表示這個問題。平方根函數是sqrt(),接受一個double類型的參數,并返回參數的平方根,也是double類型。 atan()函數接受一個double類型的參數(即正切值),并返回一個角度(該角度的正切值就是參數值)。但是,當線的x值和y值均為-5時,atan()函數産生混亂。因為(-5)/(-5)得1,所以atan()返回45°,該值與x和y均為5時的返回值相同。也就是說,atan()無法區分角度相同但反向相反的線(實際上,atan()返回值的單位是弧度而不是度,稍後介紹兩者的轉換)。 當然,C庫還提供了atan2()函數。它接受兩個參數:x的值和y的值。這樣,通過檢查x和y的正負号就可以得出正确的角度值。atan2()和atan()均返回弧度值。把弧度轉換為度,隻需将弧度值乘以180,再除以pi即可。pi的值通過計算表達式4*atan(1)得到。程序rectpol.c演示了這些步驟。另外,學習該程序還複習了結構和typedef相關的知識。

The rectpol.c Program

/* rect_pol.c -- converts rectangular coordinates to polar */ #include <stdio.h> #include <math.h> ​ #define RAD_TO_DEG (180/(4 * atan(1))) ​ typedef struct polar_v { double magnitude; double angle; } Polar_V; ​ typedef struct rect_v { double x; double y; } Rect_V; ​ Polar_V rect_to_polar(Rect_V); ​ int main(void) { Rect_V input; Polar_V result; ​ puts("Enter x and y coordinates; enter q to quit:"); while (scanf("%lf %lf", &input.x, &input.y) == 2) { result = rect_to_polar(input); printf("magnitude = %0.2f, angle = %0.2fn", result.magnitude, result.angle); } puts("Bye."); ​ return 0; } ​ Polar_V rect_to_polar(Rect_V rv) { Polar_V pv; ​ pv.magnitude = sqrt(rv.x * rv.x rv.y * rv.y); if (pv.magnitude == 0) pv.angle = 0.0; else pv.angle = RAD_TO_DEG * atan2(rv.y, rv.x); ​ return pv; }

下面是運行該程序後的一個輸出示例:

Enter x and y coordinates; enter q to quit: 10 10 magnitude = 14.14, angle = 45.00 -12 -5 magnitude = 13.00, angle = -157.38 q Bye.

如果編譯時出現下面的消息:

Undefined:_sqrt

或者

'sqrt': unresolved external

或者其他類似的消息,表明編譯器鍊接器沒有找到數學庫。UNIX系統會要求使用-lm标記(flag)指示鍊接器搜索數學庫:

cc rect_pol.c --lm

注意,-lm标記在命令行的末尾。因為鍊接器在編譯器編譯C文件後才開始處理。在Linux中使用GCC編譯器可能要這樣寫:

gcc rect_pol.c -lm

2 類型變體

基本的浮點型數學函數接受double類型的參數,并返回double類型的值。當然,也可以把float或long double類型的參數傳遞給這些函數,它們仍然能正常工作,因為這些類型的參數會被轉換成double類型。這樣做很方便,但并不是最好的處理方式。如果不需要雙精度,那麼用float類型的單精度值來計算會更快些。而且把long double類型的值傳遞給double類型的形參會損失精度,形參獲得的值可能不是原來的值。為了解決這些潛在的問題,C标準專門為float類型和long double類型提供了标準函數,即在原函數名後加上f或l後綴。因此,sqrtf()是sqrt()的float版本,sqrtl()是sqrt()的longdouble版本。 利用C11新增的泛型選擇表達式定義一個泛型宏,根據參數類型選擇最合适的數學函數版本。程序清單16.15演示了兩種方法。

Listing 16.15 The generic.c Program

​ //generic.c-- defining generic macros ​ #include <stdio.h> #include <math.h> #define RAD_TO_DEG (180/(4 * atanl(1))) ​ // generic square root function #define SQRT(X) _Generic((X), long double: sqrtl, default: sqrt, float: sqrtf)(X) ​ // generic sine function, angle in degrees #define SIN(X) _Generic((X), long double: sinl((X)/RAD_TO_DEG), default:sin((X)/RAD_TO_DEG), float:sinf((X)/RAD_TO_DEG) ) ​ int main(void) { float x = 45.0f; double xx = 45.0; long double xxx =45.0L; ​ long double y = SQRT(x); long double yy= SQRT(xx); long double yyy = SQRT(xxx); printf("%.17Lfn", y);// matches float printf("%.17Lfn", yy);// matches default printf("%.17Lfn", yyy); // matches long double int i = 45; yy = SQRT(i);// matches default printf("%.17Lfn", yy); yyy= SIN(xxx);// matches long double printf("%.17Lfn", yyy); ​ return 0; }

下面是該程序的輸出:

6.70820379257202148 6.70820393249936942 6.70820393249936909 6.70820393249936942 0.70710678118654752

如上所示,SQRT(i)和SQRT(xx)的返回值相同,因為它們的參數類型分别是int和double,所以隻能與default标簽對應。 有趣的一點是,如何讓Generic宏的行為像一個函數。SIN()的定義也許提供了一個方法:每個帶标号的值都是函數調用,所以Generic表達式的值是一個特定的函數調用,如sinf((X)/RADTODEG),用傳入SIN()的參數替換X。 SQRT()的定義也許更簡潔。Generic表達式的值就是函數名,如sinf。函數的地址可以代替該函數名,所以Generic表達式的值是一個指向函數的指針。然而,緊随整個Generic表達式之後的是(X),函數指針(參數)表示函數指針。因此,這是一個帶指定的參數的函數指針。 簡而言之,對于SIN(),函數調用在泛型選擇表達式内部;而對于SQRT(),先對泛型選擇表達式求值得一個指針,然後通過該指針調用它所指向的函數。

3 tgmath.h庫(C99)

C99标準提供的tgmath.h頭文件中定義了泛型類型宏,其效果與程序清單16.15類似。如果在math.h中為一個函數定義了3種類型(float、double和long double)的版本,那麼tgmath.h文件就創建一個泛型類型宏,與原來double版本的函數名同名。例如,根據提供的參數類型,定義sqrt()宏展開為sqrtf()、sqrt()或sqrtl()函數。換言之,sqrt()宏的行為和程序清單16.15中的SQRT()宏類似。 如果編譯器支持複數運算,就會支持complex.h頭文件,其中聲明了與複數運算相關的函數。例如,聲明有csqrtf()、csqrt()和csqrtl(),這些函數分别返回float complex、double complex和long double complex類型的複數平方根。如果提供這些支持,那麼tgmath.h中的sqrt()宏也能展開為相應的複數平方根函數。 如果包含了tgmath.h,要調用sqrt()函數而不是sqrt()宏,可以用圓括号把被調用的函數名括起來:

#include <tgmath.h> ... float x = 44.0; double y; y = sqrt(x);// invoke macro, hence sqrtf(x) y = (sqrt)(x); // invoke function sqrt()

這樣做沒問題,因為類函數宏的名稱必須用圓括号括起來。圓括号隻會影響操作順序,不會影響括起來的表達式,所以這樣做得到的仍然是函數調用的結果。實際上,在讨論函數指針時提到過,由于C語言奇怪而矛盾的函數指針規則,還可以使用(*sqrt)()的形式來調用sqrt()函數。不借助C标準以外的機制,C11新增的Generic表達式是實現tgmath.h最簡單的方式。

,

更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!

查看全部

相关圖文资讯推荐

热门圖文资讯推荐

网友关注

Copyright 2023-2024 - www.tftnews.com All Rights Reserved