首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >【C语言标准库函数】取整与取余函数:ceil(), floor(), fmod(), 和 modf()

【C语言标准库函数】取整与取余函数:ceil(), floor(), fmod(), 和 modf()

作者头像
用户12001910
发布2026-01-21 15:08:04
发布2026-01-21 15:08:04
340
举报

在C语言的数值计算领域,取整与取余是最基础且高频的操作场景。无论是金融计算中的金额校准、嵌入式开发中的数据精度控制,还是图形学中的坐标转换,都离不开ceil()floor()fmod()modf()这四个标准库函数的支持。


一、函数家族全景概览

C语言标准库(C89及后续标准)在math.h头文件中提供了这四个用于数值处理的核心函数,它们虽同属“数值调整”范畴,但分工明确:ceil()floor()专注于方向型取整,前者向上取整,后者向下取整;fmod()聚焦于浮点数间的取余运算,解决整数取余运算符%的局限性;modf()则擅长“拆分型取整”,将浮点数拆分为整数部分与小数部分。下表先对四个函数进行宏观对比,建立初步认知:

函数名

核心功能

参数类型

返回值类型

关键特点

ceil()

向上取整,返回不小于参数的最小整数

double

double

对正数“入”,对负数“舍”

floor()

向下取整,返回不大于参数的最大整数

double

double

对正数“舍”,对负数“入”

fmod()

计算两个浮点数的余数

double, double

double

余数符号与被除数一致

modf()

拆分浮点数为整数和小数部分

double, double*

double

整数部分通过指针传出,小数部分返回

二、核心函数逐一看

2.1 向上取整:ceil()

①函数原型与核心说明

C标准定义的ceil()原型为:

代码语言:javascript
复制
#include <math.h>
double ceil(double x);

其核心作用是返回不小于输入参数x的最小整数,返回值类型为double(这是为了兼容大数值场景,避免整数溢出)。例如:ceil(1.2)返回2.0,ceil(-1.2)返回-1.0,ceil(3.0)返回3.0。

②实现逻辑(伪代码)

ceil()的实现需区分参数的正负性与整数性,核心逻辑是判断参数是否为整数,若不是则向正无穷方向进1。伪代码如下:

代码语言:javascript
复制
function ceil(double x) {
    // 处理特殊值:NaN返回NaN,正无穷返回正无穷,负无穷返回负无穷
    if (x is NaN or x == +infinity) {
        return x;
    }
    if (x == -infinity) {
        return x;
    }
    
    // 提取x的整数部分(截断小数)
    int integer_part = (int)x;
    
    // 若x为正数且有小数部分,向上取整为integer_part + 1
    if (x > 0.0 && x > (double)integer_part) {
        return (double)(integer_part + 1);
    }
    // 若x为负数且有小数部分(此时x < integer_part,因整数部分是截断的),直接返回integer_part
    else if (x < 0.0 && x < (double)integer_part) {
        return (double)integer_part;
    }
    // 若x为整数,直接返回自身
    else {
        return x;
    }
}

③关键特性与使用场景

核心特性:向正无穷方向取整,与我们日常生活中的“四舍五入”不同,它只关注不小于参数这一规则,不考虑小数部分大小。

典型使用场景:

  • 资源分配计算:例如某任务需要每个线程处理8个数据,计算100个数据需要多少线程时,需用ceil(100.0 / 8.0)得到13个线程(若用整数除法100/8得到12,会导致4个数据未处理)。
  • 金额进位:某些场景下需将金额向上进位到分(如手续费计算),例如ceil(123.456 * 100) / 100可得到123.46。

2.2 向下取整:floor()

①函数原型与核心说明

原型与ceil()对称:

代码语言:javascript
复制
#include <math.h>
double floor(double x);

作用是返回不大于输入参数x的最大整数,返回值同样为double。例如:floor(1.8)返回1.0,floor(-1.8)返回-2.0,floor(5.0)返回5.0。

②实现逻辑(伪代码)

ceil()逻辑对称,核心是向负无穷方向取整:

代码语言:javascript
复制
function floor(double x) {
    // 处理特殊值
    if (x is NaN or x == -infinity) {
        return x;
    }
    if (x == +infinity) {
        return x;
    }
    
    // 提取整数部分(截断小数)
    int integer_part = (int)x;
    
    // 若x为正数且有小数部分,直接返回integer_part(向下取整)
    if (x > 0.0 && x > (double)integer_part) {
        return (double)integer_part;
    }
    // 若x为负数且有小数部分,向下取整为integer_part - 1
    else if (x < 0.0 && x < (double)integer_part) {
        return (double)(integer_part - 1);
    }
    // 整数直接返回
    else {
        return x;
    }
}

③关键特性与使用场景

核心特性:向负无穷方向取整,与ceil()形成互补。需要注意的是,对于正数,floor()的效果等同于截断小数,但对于负数则完全不同(如floor(-1.1)是-2.0,而截断是-1)。

典型使用场景:

  • 时间周期计算:例如计算某事件发生在“第几个小时”,若时间为13.9小时,floor(13.9)可得到13,即第13小时。
  • 数据分段统计:将0-100的分数分为10段(0-10、11-20...),对分数scorefloor(score / 10)可得到段号(如85分对应8号段)。

2.3 浮点数取余:fmod()

① 函数原型与核心说明

C语言中的%运算符仅支持整数取余,而fmod()填补了浮点数取余的空白,原型为:

代码语言:javascript
复制
#include <math.h>
double fmod(double x, double y);

作用是计算x除以y的余数,满足数学关系:x = n * y + fmod(x, y),其中n是整数,且余数的符号与x一致,绝对值小于y的绝对值。例如:fmod(7.5, 2.0)返回1.5,fmod(-7.5, 2.0)返回-1.5,fmod(7.5, -2.0)仍返回1.5。

②实现逻辑(伪代码)

核心逻辑是通过减法或除法找到满足余数规则的结果,需处理除数为0、特殊值等边界情况:

代码语言:javascript
复制
function fmod(double x, double y) {
    // 处理特殊情况:y为0或x为无穷/NaN,返回NaN
    if (y == 0.0 || x is NaN || y is NaN || x == +infinity || x == -infinity) {
        return NaN;
    }
    // 若x为0,返回0.0(符号与x一致)
    if (x == 0.0) {
        return x;
    }
    
    // 计算商的整数部分(向零方向取整)
    double quotient = x / y;
    int n = (int)quotient; // 截断小数,向零取整
    
    // 计算余数:x - n*y
    double remainder = x - n * y;
    
    // 确保余数绝对值小于y的绝对值(处理精度误差)
    while (fabs(remainder) >= fabs(y)) {
        remainder -= (remainder > 0) ? y : -y;
    }
    while (fabs(remainder) < 0) {
        remainder += (remainder > 0) ? y : -y;
    }
    
    return remainder;
}

③关键特性与使用场景

核心特性:余数符号与被除数一致支持浮点数运算,这是与%运算符的核心区别(%余数符号与被除数一致,但仅支持整数)。

典型使用场景:

  • 周期信号处理:例如正弦函数sin(x)周期为,对任意xfmod(x, 2*M_PI)可将其映射到0-2π区间,简化计算。
  • 浮点数合法性校验:例如判断一个浮点数x是否为y的整数倍,可通过fmod(x, y) == 0.0判断(需注意浮点数精度问题)。

2.4 浮点数拆分:modf()

①函数原型与核心说明

与前三个函数不同,modf()的核心是拆分而非取整,原型为:

代码语言:javascript
复制
#include <math.h>
double modf(double x, double *intpart);

作用是将浮点数x拆分为整数部分和小数部分,其中:

  • 整数部分通过指针参数intpart传出,类型为double,且符号与x一致。
  • 小数部分作为函数返回值,符号也与x一致,绝对值范围为[0,1)。

例如:modf(3.14, &intp)会将intp设为3.0,返回0.14;modf(-3.14, &intp)会将intp设为-3.0,返回-0.14。

②实现逻辑(伪代码

核心逻辑是提取整数部分并计算小数部分,需处理特殊值和精度问题:

代码语言:javascript
复制
function modf(double x, double *intpart) {
    // 处理特殊值:NaN返回NaN,无穷大返回0.0并将intpart设为无穷大
    if (x is NaN) {
        *intpart = NaN;
        return NaN;
    }
    if (x == +infinity) {
        *intpart = +infinity;
        return 0.0;
    }
    if (x == -infinity) {
        *intpart = -infinity;
        return 0.0;
    }
    
    // 提取整数部分(向零方向取整)
    double integer = (double)(int)x;
    
    // 计算小数部分:x - 整数部分
    double fractional = x - integer;
    
    // 处理精度误差:例如0.999999999999可能被识别为1.0
    if (fabs(fractional) >= 1.0) {
        integer += (fractional > 0) ? 1.0 : -1.0;
        fractional = x - integer;
    }
    
    // 传出整数部分,返回小数部分
    *intpart = integer;
    return fractional;
}

③关键特性与使用场景

核心特性:整数与小数部分符号一致拆分后两部分之和等于原数,这是其与手动截断的区别(手动截断可能导致符号不一致)。

典型使用场景:

  • 数值格式化输出:例如将浮点数123.45拆分为整数123和小数45,用于格式化显示“123元45分”。
  • 高精度计算:在需要分别处理整数和小数部分的场景(如科学计算)中,拆分后可单独优化计算精度。

三、函数差异深度对比与实战示例

3.1 核心差异对比表

为了更直观地展示四个函数的差异,下表选取了6个典型输入值,对比其输出结果:

输入值x(y=2.0用于fmod)

ceil(x)

floor(x)

fmod(x, 2.0)

modf(x, &intp):intp/返回值

1.2

2.0

1.0

1.2

1.0 / 0.2

-1.2

-1.0

-2.0

-1.2

-1.0 / -0.2

3.0

3.0

3.0

0.0

3.0 / 0.0

-3.0

-3.0

-3.0

-0.0(等价于0.0)

-3.0 / -0.0

0.0

0.0

0.0

0.0

0.0 / 0.0

1.999999

2.0

1.0

1.999999

1.0 / 0.999999

3.2 综合实战示例代码

下面通过一个数值处理工具的示例,展示四个函数的综合应用。该程序实现以下功能:1)输入一个浮点数和一个除数;2)分别用四个函数处理数值;3)格式化输出结果。

代码语言:javascript
复制
#include <stdio.h>
#include <math.h>
#include <stdlib.h>

int main() {
    double x, y;
    // 输入提示与数据读取
    printf("请输入一个浮点数x:");
    if (scanf("%lf", &x) != 1) {
        printf("输入错误!\n");
        return 1;
    }
    printf("请输入除数y(用于fmod计算):");
    if (scanf("%lf", &y) != 1) {
        printf("输入错误!\n");
        return 1;
    }
    
    // 函数调用
    double ceil_result = ceil(x);
    double floor_result = floor(x);
    double fmod_result = fmod(x, y);
    double intpart, modf_result = modf(x, &intpart);
    
    // 结果输出(格式化)
    printf("\n===== 数值处理结果 =====\n");
    printf("1. ceil(x) 向上取整:%.6f\n", ceil_result);
    printf("2. floor(x) 向下取整:%.6f\n", floor_result);
    printf("3. fmod(x, y) 浮点数取余:%.6f\n", fmod_result);
    printf("4. modf(x) 拆分结果:整数部分=%.6f,小数部分=%.6f\n", intpart, modf_result);
    
    // 附加:验证modf拆分的正确性(两部分之和是否等于原数)
    double sum = intpart + modf_result;
    printf("\n附加验证:整数部分 + 小数部分 = %.6f(原数x=%.6f)\n", sum, x);
    printf("拆分正确性:%s\n", fabs(sum - x) < 1e-9 ? "正确" : "存在精度误差");
    
    return 0;
}

编译与运行说明

由于使用了math.h中的函数,编译时需链接数学库(-lm参数),命令如下:

代码语言:javascript
复制
gcc -o num_process num_process.c -lm
./num_process

运行示例与结果分析

输入x=123.456,y=10.0,运行结果如下:

代码语言:javascript
复制
请输入一个浮点数x:123.456
请输入除数y(用于fmod计算):10.0

===== 数值处理结果 =====
1. ceil(x) 向上取整:124.000000
2. floor(x) 向下取整:123.000000
3. fmod(x, y) 浮点数取余:3.456000
4. modf(x) 拆分结果:整数部分=123.000000,小数部分=0.456000

附加验证:整数部分 + 小数部分 = 123.456000(原数x=123.456000)
拆分正确性:正确

结果分析:四个函数的输出均符合预期,modf()的拆分结果之和与原数完全一致(无精度误差);fmod(123.456, 10.0)返回3.456,符合“余数绝对值小于除数”的规则。

四、使用注意事项与进阶技巧

在实际开发中,若忽视函数的细节特性,容易引发逻辑错误或精度问题。

注意事项1:头文件与编译链接

所有四个函数均定义在math.h头文件中,必须包含该头文件;同时,编译时需链接数学库(-lm参数),否则会出现“未定义引用”错误。这是初学者最常犯的错误之一。

注意事项2:返回值类型为double

四个函数的返回值均为double类型,而非整数类型。若需整数结果,需手动强制转换(如int res = (int)ceil(x)),但需注意转换可能导致的溢出(如x超过int的最大值)。

注意事项3:浮点数精度误差问题

由于浮点数存储特性,计算结果可能存在微小误差,导致判断失效。例如fmod(1.0, 0.1)的结果并非0.0(因0.1无法精确表示),需用fabs(fmod(x,y)) < 1e-9判断是否为0,而非直接用==0.0

注意事项4:特殊值处理逻辑

当输入为NaN、正无穷(+infinity)或负无穷(-infinity)时,函数返回值有明确规则:ceil()/floor()对NaN返回NaN,对正无穷返回正无穷,对负无穷返回负无穷;fmod()对这些值均返回NaN;modf()对NaN返回NaN,对无穷大返回0.0并将整数部分设为无穷大。

注意事项5:fmod()与remainder()的区别

C99标准新增remainder()函数,与fmod()均为浮点数取余,但商的取整方式不同:fmod()商向零取整,remainder()商向最近整数取整(四舍五入)。例如fmod(7.5,2.0)=1.5,而remainder(7.5,2.0)=-0.5

注意事项6:modf()的指针参数不可为NULL

modf()的第二个参数是指针,用于接收整数部分,必须指向有效的double变量,不可传NULL,否则会触发内存访问错误(段错误)。

进阶使用技巧

除基础用法外,结合函数特性可实现更灵活的数值处理需求,以下为两个高频技巧:

技巧1:实现四舍五入功能 C标准库无直接四舍五入函数,可通过ceil()floor()实现:对正数xround(x) = floor(x + 0.5);对负数xround(x) = ceil(x - 0.5)。封装函数示例:

代码语言:javascript
复制
#include <math.h>
// 四舍五入函数,保留n位小数
double round_n(double x, int n) {
    double scale = pow(10, n);
    if (x > 0) {
        return floor(x * scale + 0.5) / scale;
    } else {
        return ceil(x * scale - 0.5) / scale;
    }
}

测试:round_n(123.456,2)=123.46round_n(-123.456,2)=-123.46,符合四舍五入规则。

技巧2:利用fmod()处理周期性任务 在嵌入式系统中,需周期性执行某任务(如每500ms执行一次),可通过fmod()计算系统运行时间与周期的余数,判断是否到达执行时机:

代码语言:javascript
复制
#include <time.h>
#include <math.h>
// 假设获取系统运行时间(单位:ms)
double get_system_time_ms() {
    struct timespec ts;
    clock_gettime(CLOCK_MONOTONIC, &ts);
    return ts.tv_sec * 1000.0 + ts.tv_nsec / 1e6;
}

// 周期性任务检查:余数小于1ms则认为到达周期
int is_task_time(double period) {
    double now = get_system_time_ms();
    return fabs(fmod(now, period)) < 1.0;
}

附: 经典面试题

面试题1:ceil(1.5)、floor(1.5)、ceil(-1.5)、floor(-1.5)返回值?(百度2023C开发笔试题)

答案:依次为2.0、1.0、-1.0、-2.0。

解析:核心是取整方向——ceil()向正无穷取整,故1.5向上到2.0,-1.5向上到-1.0;floor()向负无穷取整,故1.5向下到1.0,-1.5向下到-2.0。需强调返回值为double类型,非整数。

面试题2:fmod(5.5,2)与5.5%2的区别?(腾讯2024后台开发一面题)

答案:前者返回1.5(有效),后者编译报错(无效)。

解析:1. 运算符支持:%仅支持整数参数,传入浮点数5.5会触发“类型不匹配”编译错误;fmod()专为浮点数设计。2. 结果类型:fmod()返回double类型1.5,%若传入5%2返回int类型1。3. 核心差异:%无浮点数运算能力,fmod()完美适配浮点数场景。

面试题3:用modf()拆分123.456,如何获取整数/小数部分?传NULL会怎样?(字节2023嵌入式二面题)

答案:1. 正确用法:定义double int_part变量,通过double frac_part = modf(123.456, &int_part)调用,int_part接收123.0,frac_part得到0.456。 2. 传NULL后果:触发段错误(内存访问违规)。

解析:modf()第二个参数是输出指针,需指向有效内存,NULL为无效地址,访问时触发操作系统内存保护机制。


博主简介 byte轻骑兵,现就职于国内知名科技企业,专注于嵌入式系统研发,深耕 Android、Linux、RTOS、通信协议、AIoT、物联网及 C/C++ 等领域。乐于技术分享与交流,欢迎关注互动! 📌 主页与联系方式

  • CSDN:https://blog.csdn.net/weixin_37800531
  • 知乎:https://www.zhihu.com/people/38-72-36-20-51
  • 微信公众号:嵌入式硬核研究所
  • 邮箱:byteqqb@163.com(技术咨询或合作请备注需求)

⚠️ 版权声明 本文为原创内容,未经授权禁止转载。商业合作或内容授权请联系邮箱并备注来意。


本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-10-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、函数家族全景概览
  • 二、核心函数逐一看
    • 2.1 向上取整:ceil()
    • 2.2 向下取整:floor()
    • 2.3 浮点数取余:fmod()
    • 2.4 浮点数拆分:modf()
  • 三、函数差异深度对比与实战示例
    • 3.1 核心差异对比表
    • 3.2 综合实战示例代码
  • 四、使用注意事项与进阶技巧
  • 附: 经典面试题
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档