1 sqrt()、pow()、sin() 等常用數學函式
<math.h> 提供了許多常用數學函式:sqrt()(開根號)、pow(x,y)(x 的 y 次方)、sin()(正弦)等。使用前記得先 #include <math.h>。
#include <stdio.h>
#include <math.h>
int main(void) {
double a = 3, b = 4;
double c = sqrt(pow(a, 2) + pow(b, 2)); // 斜邊 = √(a² + b²)
printf("%.1f\n", c); // 5.0
return 0;
}
與其自己手刻開根號、算三角函數的演算法,不如直接呼叫 <math.h> 這本現成的「公式手冊」——pow(x, y) 算x的y次方、sqrt(x) 算平方根,兩者組合起來就能直接照數學公式寫出畢氏定理,不需要重新發明輪子。
2 定義域(domain)錯誤:對負數開根號
使用數學函式時要留意定義域(domain)問題——例如對負數開根號在數學上無定義(沒有實數解),執行時會產生 domain error,即使程式語法完全正確,也會在執行期出錯。
#include <math.h>
float x = 6.28, y;
while (x > 0) {
y = sqrt(sin(x)); // 若 sin(x) < 0,這裡就會發生 domain error
x -= 0.1;
}
| x(弧度)範圍 | sin(x) 的正負號 |
|---|---|
| (0, π) 約(0, 3.1416) | 正 |
| (π, 2π) 約(3.1416, 6.2832) | 負 |
正弦函數 sin(x) 一個完整週期是 2π≈6.2832,其中 (π, 2π)(約3.14~6.28)這段範圍的函數值是負數。如果迴圈裡 x 的值(弧度)剛好落在這個區間,sin(x) 算出負值,緊接著的 sqrt() 就會嘗試對負數開根號,觸發 domain error。
3 fabs()/floor()/ceil():絕對值與捨去進位
這三個函式都要 #include <math.h>,分別對應「絕對值」、「無條件捨去」、「無條件進位」,跟四捨五入(需自己用 +0.5 後取整數部分)是不同的觀念。
#include <stdio.h>
#include <math.h>
int main(void) {
printf("%.1f\n", fabs(-7.5)); // 7.5,取絕對值
printf("%.1f\n", floor(3.7)); // 3.0,無條件捨去到整數
printf("%.1f\n", ceil(3.2)); // 4.0,無條件進位到整數
return 0;
}
| 函式 | 作用 | 方向 |
|---|---|---|
fabs(x) | 取絕對值 | 結果必為正數(或0) |
floor(x) | 無條件捨去 | 永遠往「較小」(負無窮)方向捨去 |
ceil(x) | 無條件進位 | 永遠往「較大」(正無窮)方向進位 |
對負數而言,floor(-2.3) 是-3(比-2.3更小、更負的方向),不是直覺以為的-2;ceil(-2.3) 才是-2(比-2.3更大的方向)。floor/ceil 永遠是照「數線上的方向」(左邊變小、右邊變大)判斷,不是「捨去小數部分讓數字更靠近0」。
4 fmin()/fmax():比較並回傳較小/較大值
fmin(x, y) 回傳兩個浮點數中較小的那個;fmax(x, y) 回傳較大的那個。常跟 pow()、sqrt()、exp() 等函式組合出現在同一個運算式裡。
#include <math.h>
int X1=10, X2=10000, Y=3;
printf("%6.2f", fmin(pow(X1, Y), sqrt(X2)));
// pow(X1,Y) = 10³ = 1000
// sqrt(X2) = sqrt(10000) = 100
// fmin(1000, 100) = 100 → 輸出 "100.00"
遇到 fmin(pow(X1,Y), sqrt(X2)) 這種巢狀函式呼叫,不要試圖一眼看穿最終結果,先把每一個「最內層」的函式單獨算出數值(pow(X1,Y)=?、sqrt(X2)=?),再把算出來的數值代入最外層的 fmin() 或 fmax() 比較,一步一步來就不容易出錯。
5 勘根定理(Intermediate Value Theorem):判斷區間內是否存在函數的解
判斷一個連續函數在區間 [m, n] 內是否存在零點(f(x)=0的解),數學上常用勘根定理:只要區間兩端點的函數值 f(m) 與 f(n) 異號(相乘小於等於0),該區間內就必定存在一個解。
#include <math.h>
double f(double x) { return x - 2; } // 假設的連續函數
if (f(1) * f(3) <= 0) {
printf("[1,3] 區間內存在 f(x)=0 的解\n"); // 因 f(1)=-1, f(3)=1,異號 → 成立
}
兩個數異號(一正一負)的數學表示法,就是「兩數相乘會得到負數(或其中一個為0時得到0)」——這正是勘根定理判斷式要用乘法的原因。相加或相減都無法可靠地反映「兩數是否異號」這件事:例如一正一負相加,結果可能是正也可能是負,取決於誰的絕對值比較大,不能保證一定小於等於0。
6 常考陷阱總複習:型態、精度與定義域的交互影響
數學函式庫題目常把「浮點數精度」、「格式化輸出」、「定義域」這幾個觀念揉合在一起考。整理三個檢查重點:
| 檢查重點 | 常見陷阱 |
|---|---|
| 定義域 | 對負數 sqrt()、對超出範圍的 x 用不適用的函式 |
| 格式化輸出 | %.3f 這類精度指定符只規定小數位數,不影響整數部分位數 |
| 多層函式呼叫 | 要由內往外逐步計算,不要跳過中間步驟直接猜結果 |
「開根號先看正負,算函式先算裡面」——看到 sqrt() 先確認裡面的運算式會不會是負數;看到多層函式呼叫,先由最內層開始一步步算出中間值,再往外代入。
7 111~115 歷屆統測真題全選項深度剖析(共 3 題)
以下收錄統測專業科目(二)近年全部 3 題數學函式庫與應用題真題。每題均提供高解析度原始考題掃描圖、互動選項按鈕與所有選項逐項深入解析:
float a=1, b=0, m=-11, n=12;
float f(float x){
return(a*x+b);
}
int main(){
float x;
unsigned char found=0;
scanf("%f",&a); scanf("%f",&b);
scanf("%f",&m); scanf("%f",&n);
if( ?? <=0 )
found = 1;
printf("found=%d\n", found);
}
🔍 四個選項逐項深度剖析
f(m)*f(n) <= 0,選 (A)。-10+1=-9小於0;若f(m)=10, f(n)=1,兩數同號(都是正,區間內未必有解),相加卻是11大於0。但也可能出現兩個都是負數的情況相加同樣小於0,無法用加法可靠地分辨兩數是否異號。% 是取餘數運算子,只能用在整數型態上,f(m)、f(n)是浮點數(float)運算的結果,對浮點數使用 % 在C語言中無法通過編譯(型態不符),而且取餘數的結果跟「兩數是否異號」也沒有直接的數學關聯。#include <math.h>
float x=6.28, y;
int i;
while(x>0){
y=sqrt(sin(x));
printf("square root of sin(%f) = %f\n", x, y);
x-=0.1;
}
🔍 四個選項逐項深度剖析
sin(x) 在(π, 2π)區間(約3.1416~6.2832)為負值,x的初始值6.28正好落在此區間,sqrt() 對負數開根號屬於數學定義域錯誤(domain error),選 (A)。// double fmin(double x, double y) 比較並回傳較小值
// double fmax(double x, double y) 比較並回傳較大值
// double pow(double x, double y) 計算x的y次方
// double exp(double x) 計算e的x次方
// double sqrt(double x) 計算x的平方根
// 已知 int X1=10, X2=10000, Y=3;
printf("%6.2f", fmin(pow(X1, Y), sqrt(X2)));
🔍 四個選項逐項深度剖析
pow(X1,Y)=10³=1000;exp(Y)=e³≈20.09。fmin(1000, 20.09) 會取兩者中較小的,也就是20.09,格式化輸出會是"20.09",並不是100.00。pow(X1,Y)=10³=1000;sqrt(X2)=sqrt(10000)=100。fmin(1000,100) 取兩者中較小的,也就是100,格式化輸出為"100.00",符合題目要求,選 (B)。pow(X1,Y)=1000;exp(Y)≈20.09。fmax(1000,20.09) 會取兩者中較大的,也就是1000,格式化輸出會是"1000.00",並不是100.00。pow(X1,Y)=1000;sqrt(X2)=100。fmax(1000,100) 會取兩者中較大的,也就是1000,格式化輸出會是"1000.00",並不是100.00——這個選項的兩個函式參數(pow,sqrt)其實跟正確答案(B)一模一樣,但用fmax(取最大值)取代了應該用的fmin(取最小值),一字之差導致結果完全不同。8 高職段考與模擬精選實戰題(5 題全選項解析)
透過以下 5 題精選模擬試題,全面檢驗你對常用數學函式、定義域錯誤、fmin/fmax與勘根定理的掌握度:
double a = 6, b = 8;
double c = sqrt(pow(a,2) + pow(b,2));
printf("%.1f", c);
🔍 四個選項逐項深度剖析
pow(a,2)=6²=36;pow(b,2)=8²=64;相加得100;sqrt(100)=10。格式化輸出"10.0",選 (A)。a+b=6+8=14 直接當成答案,完全跳過了pow()平方跟sqrt()開根號這兩個關鍵運算步驟。pow(a,2)+pow(b,2)=36+64=100 這個中間值,但忘記了外層還有一個 sqrt() 要對這個中間值開根號,100本身還不是最終答案,還需要再開一次根號變成10。#include <math.h>
double x = -9;
double y = sqrt(x);
printf("%.1f", y);
🔍 四個選項逐項深度剖析
sqrt() 開根號在數學上沒有實數解,這是定義域錯誤(domain error),屬於執行期才會發生的錯誤,選 (A)。#include <math.h>
printf("%.0f %.0f %.0f", floor(-2.3), ceil(-2.3), fabs(-2.3));
🔍 四個選項逐項深度剖析
floor(-2.3) 無條件捨去到「較小」的整數(往負無窮方向捨去),結果是-3.0;ceil(-2.3) 無條件進位到「較大」的整數(往正無窮方向進位),結果是-2.0;fabs(-2.3) 取絕對值,結果是2.3,用 %.0f 四捨五入到整數位是2。輸出"-3 -2 2",選 (A)。fabs(-2.3) 算錯了——fabs是取絕對值,結果必定是正數(或0),2.3的絕對值是2.3,不會維持負數-2.3,也不會是-2。floor(-2.3) 算錯了,誤以為floor對負數的捨去方向跟對正數一樣「捨去小數部分、只留整數部分朝0靠近」(也就是變成-2),但floor對負數的正確行為是「朝負無窮方向捨去」,結果應該是比-2.3更小的-3,不是-2。#include <math.h>
double p = pow(2, 5);
double s = sqrt(49);
printf("%.1f %.1f", fmin(p, s), fmax(p, s));
🔍 四個選項逐項深度剖析
p=pow(2,5)=2⁵=32;s=sqrt(49)=7。fmin(32,7) 取較小的,得7;fmax(32,7) 取較大的,得32。依printf引數順序輸出"7.0 32.0",選 (A)。#include <math.h>
double f(double x) { return x*x - 4; } // f(x)=x²-4
int main(void) {
double m = 0, n = 3;
if (f(m) * f(n) <= 0)
printf("區間內存在解");
else
printf("區間內可能不存在解");
return 0;
}
🔍 四個選項逐項深度剖析
f(0)*f(3)=-4×5=-20,小於等於0,判斷式成立,依勘根定理,[0,3]這個區間內必定存在至少一個f(x)=0的解(實際上x=2就是一個解,2確實落在[0,3]區間內),印出"區間內存在解",選 (A)。f(0)*f(3) 的正負號。f(0)=-4為負,f(3)=5為正,兩者相乘必為負數(小於等於0),判斷式 <=0 成立,應該執行if分支印出「存在解」,不會是else分支。9 考前衝刺 10 秒速記口訣 & 核心知識檢核清單
sqrt()開根號、pow(x,y)算x的y次方、sin()正弦,都需要#include <math.h>。
sqrt()對負數會產生domain error;先確認裡面的運算式會不會變成負數。
floor永遠往負無窮(更小)方向捨去;ceil永遠往正無窮(更大)方向進位。
fmin取較小值、fmax取較大值,一字之差結果可能完全不同。
兩端點函數值f(m)、f(n)異號 ⇔ f(m)*f(n)<=0 ⇔ 區間內必有解,只有乘法能可靠判斷異號。
先算出最內層函式的數值,再一步步代入外層函式,不要跳步驟直接猜答案。