C++---向上取整

C++---向上取整

1. 什么是向上取整(Ceiling)

向上取整(Ceiling)是数学中的一个基本概念,表示取大于或等于某个数的最小整数
数学符号是:⌈x⌉\lceil x \rceil⌈x⌉

例子:

  • ⌈2.3⌉=3\lceil 2.3 \rceil = 3⌈2.3⌉=3
  • ⌈2.0⌉=2\lceil 2.0 \rceil = 2⌈2.0⌉=2
  • ⌈−2.3⌉=−2\lceil -2.3 \rceil = -2⌈−2.3⌉=−2 (注意不是 -3,因为 -2 比 -2.3 大)
  • ⌈−2.0⌉=−2\lceil -2.0 \rceil = -2⌈−2.0⌉=−2
对比:向下取整(floor):取小于或等于 x 的最大整数四舍五入(round):根据小数部分决定向上或向下取整

2. C++ 中的除法默认是“向零截断”

在 C++ 中,两个整数相除 / 会执行向零截断(truncate towards zero):

  • 正数:等价于向下取整
  • 负数:等价于向上取整
cout <<7/3<< endl;// 2(7/3=2.333,向零截断) cout <<-7/3<< endl;// -2(不是 -3)

这意味着:

  • 直接用 / 不能直接得到数学上的向上取整结果
  • 需要额外处理才能实现真正的向上取整

3. 实现向上取整的三种主流方法

3.1 使用 <cmath> 中的 std::ceil

C++ 标准库提供了 ceil 函数,定义在 <cmath> 中:

#include<iostream>#include<cmath>usingnamespace std;intmain(){double x =7.0/3.0; cout <<ceil(x)<< endl;// 3double y =-7.0/3.0; cout <<ceil(y)<< endl;// -2}

特点

  • 支持浮点数,能正确处理负数
  • 缺点:需要先转换成浮点类型,否则整数除法已经截断
  • 有精度误差风险:ceil(8.999999999) 可能得到 8 而不是 9

3.2 整数公式法(适用于正数)

公式:⌈ab⌉=a+b−1b(a,b>0)\lceil \frac{a}{b} \rceil = \frac{a + b - 1}{b} \quad (a, b > 0)⌈ba​⌉=ba+b−1​(a,b>0)

原理

  • 如果 a 能被 b 整除,那么 a + b - 1 除以 b 仍是 a / b
  • 如果不能整除,a + b - 1 会把分子推到下一个能整除的区间

例子:

int a =7, b =3; cout <<(a + b -1)/ b << endl;// (7+3-1)/3 = 9/3 = 3 ✅int a2 =6, b2 =3; cout <<(a2 + b2 -1)/ b2 << endl;// (6+3-1)/3 = 8/3 = 2 ✅

适用范围

  • a、b 均为正数
  • 对于负数,需要额外调整

3.3 位运算优化法(b 是 2 的幂)

如果 b 是 2 的幂,可以用位运算代替除法,效率更高:
⌈a2k⌉=(a+(1<<k)−1)>>k\lceil \frac{a}{2^k} \rceil = (a + (1 << k) - 1) >> k⌈2ka​⌉=(a+(1<<k)−1)>>k

例子:

int a =17, k =3;// b = 8int ceil_div =(a +(1<< k)-1)>> k;// (17 + 8 - 1) >> 3 = 24 >> 3 = 3 ✅

原理

  • (1 << k) - 1 生成 k 个 1 的掩码
  • 加法后右移 k 位相当于整除 2^k

4. 边界情况处理

4.1 a 或 b 为 0

  • 如果 b = 0:无意义(除零错误)
  • 如果 a = 0:向上取整结果为 0

4.2 负数处理

通用安全公式(支持负数):

intceil_div(int a,int b){if(b ==0)throwruntime_error("Division by zero");if((a >0&& b >0)||(a <0&& b <0)){// 同号:用 (a + b - 1) / breturn(a + b -1)/ b;}else{// 异号:直接除(向零截断等价于向上取整)return a / b;}}

5. 性能对比

方法优点缺点适用场景
std::ceil简单,支持负数浮点精度风险不要求极致性能
(a + b - 1) / b整数运算,无精度问题仅适用于正数算法竞赛、正数场景
位运算速度快仅适用于 b 是 2 的幂内存对齐、性能敏感

6. 实战应用场景

6.1 分页计算

int total_items =100;int page_size =10;int pages =(total_items + page_size -1)/ page_size;// 10 页

6.2 数组分块

int n =17;int block_size =5;int blocks =(n + block_size -1)/ block_size;// 4 块

6.3 二分查找中的边界计算

LeetCode 2300:

longlong t =(success + i -1)/ i -1;

这里 (success + i - 1) / i 就是对 success / i 向上取整。


7. 完整代码示例

#include<iostream>#include<cmath>#include<stdexcept>usingnamespace std;// 通用向上取整函数(支持负数)intceil_div(int a,int b){if(b ==0)throwruntime_error("Division by zero");if((a >0&& b >0)||(a <0&& b <0)){return(a + b -1)/ b;}else{return a / b;}}// 位运算优化版(b 是 2 的幂)intceil_pow2(int a,int k){return(a +(1<< k)-1)>> k;}intmain(){// 测试正数 cout <<ceil_div(7,3)<< endl;// 3 cout <<ceil_div(6,3)<< endl;// 2// 测试负数 cout <<ceil_div(-7,3)<< endl;// -2 cout <<ceil_div(7,-3)<< endl;// -2 cout <<ceil_div(-7,-3)<< endl;// 3// 测试位运算版本 cout <<ceil_pow2(17,3)<< endl;// 3 (17/8=2.125)// 测试标准库函数 cout <<ceil(7.0/3.0)<< endl;// 3 cout <<ceil(-7.0/3.0)<< endl;// -2return0;}

8. 总结与建议

  1. 正数场景
    • (a + b - 1) / b 最安全高效
    • 如果 b 是 2 的幂,用位运算 (a + (1 << k) - 1) >> k 更快
  2. 负数场景
    • 用通用公式 ceil_div(a, b)
    • 或直接用 std::ceil((double)a / b)
  3. 性能敏感场景
    • 优先考虑位运算(如果适用)
    • 否则用整数公式法
  4. 精度敏感场景
    • 避免直接用浮点数计算,优先整数公式

生活本来就很精彩。只不过有人没发现自己是作者,没发现他们可以按自己的想法创作。 —约翰·史特列奇

Read more

【C++笔记】STL详解:vector容器的使用

【C++笔记】STL详解:vector容器的使用

前言:         本文在介绍STL框架基础上,进一步讲解了迭代器、auto关键字和范围for循环的使用方法,接下来我们将重点探讨vector类的常用接口及其应用。          一、vector容器的简介             C++ 的 vector 是标准模板库(STL)中最核心且实用的容器之一,其与固定大小的传统数组(如 int arr[10])不同,vector 克服了数组的局限性,它不需要预先确定大小,并且可以动态调整容量。          简单理解为:vector是可变的、经过封装函数功能的数组。                  核心优势:          ①动态扩容:您不需要一开始就告诉它要存多少数据。当空间不够时,它会在底层自动帮您寻找一块更大的内存,把数据搬过去。          ②内存安全:它负责自己内存的分配和释放,大大减少了手动 new 和 delete 带来的内存泄漏风险。          ③功能丰富:它自带了大量现成的工具函数,比如:获取大小、清空数据、在尾部添加数据等。

By Ne0inhk
【C++】模板的两大特性

【C++】模板的两大特性

文章目录 * 前言 * 1. 关于 typename 的使用场景 * 2. 模板的分离编译问题 * 2.1 简述程序编译链接的过程 * 2.1.1 预处理 * 2.1.2 编译 * 2.1.3汇编 * 2.1.4 链接 * 2.2 模板分离编译为什么会链接报错 * 2.2.1 什么是分离编译 * 2.2.2 模板分离编译存在的问题 * 3. 解决办法 前言 本文探讨了C++模板编程中的两个关键问题。第一部分介绍了typename在模板中的特殊使用场景,指出当模板参数访问内嵌类型时必须使用typename关键字来消除编译器歧义。第二部分分析了模板分离编译导致链接错误的原因,通过对比普通函数和模板函数的编译链接过程,解释了模板定义必须放在头文件中才能被实例化的原理。文章结合代码示例和编译链接过程图解,帮助读者理解模板编译机制和常见错误的解决方法。 1.

By Ne0inhk
C++ 继承入门(下):友元、静态成员与菱形继承的底层逻辑

C++ 继承入门(下):友元、静态成员与菱形继承的底层逻辑

🔥小叶-duck:个人主页 ❄️个人专栏:《Data-Structure-Learning》 《C++入门到进阶&自我学习过程记录》《算法题讲解指南》--从优选到贪心 ✨未择之路,不须回头 已择之路,纵是荆棘遍野,亦作花海遨游 目录 前言 一. 友元 —— 友元关系不可继承   1、错误版本   2、正确版本 二. 静态成员 —— 继承体系中静态成员的共享性 三. 多继承及菱形继承问题:本质特点与解决方案   1、单继承与多继承模型   2、菱形继承:虚继承解决“数据冗余”与“二义性”     2.1 菱形继承出现的坑(解决二义性问题)     2.2 虚继承:彻底解决菱形继承问题     3、多继承中指针偏移问题 友元,静态成员,

By Ne0inhk
Re:从零开始的 C++ STL篇(七)二叉搜索树增删查操作系统讲解(含代码)+key/key-value场景联合分析

Re:从零开始的 C++ STL篇(七)二叉搜索树增删查操作系统讲解(含代码)+key/key-value场景联合分析

◆ 博主名称: 晓此方-ZEEKLOG博客大家好,欢迎来到晓此方的博客。⭐️C++系列个人专栏: 主题曲:C++程序设计⭐️ 踏破千山志未空,拨开云雾见晴虹。 人生何必叹萧瑟,心在凌霄第一峰 0.1概要&序論 这里是「此方」,好久不见。 今天我们要学习的是二叉搜索树。它是在普通二叉树的基础上加入特定约束,从而具备了高效的搜索能力。虽然这种结构能够支持高效的插入、删除与查找操作,但其性能背后也隐藏着潜在的 效率风险 。同时,在 key 与 key-value 两种不同的应用场景 下,二叉搜索树的设计与实现方式也会产生不同的变化。这里是「此方」。让我们现在开始吧! 前情提要,没有系统学习过一般二叉树的小伙伴直接看这篇文章可能会有些吃力,此方在这里留一个传送门:Re:从零开始的链式二叉树:建树、遍历、计数、查找、判全、销毁全链路实现与底层剖析 一,二叉搜索树的概念

By Ne0inhk