c++算法| 排序 | 99% 程序员必踩的浮点数 + 排序坑

github热门推荐 › c++算法| 排序 | 99% 程序员必踩的浮点数 + 排序坑 (第 73 章)
文章工具

浮点数不能直接比大小 + 排序的隐藏技巧 / 致命坑点,一篇彻底讲透,看完直接避开 80% 的 BUG。

C++ 片段需补全 main() 函数才能运行在实训台打开 →
<span leaf=""><span class="code-snippet__meta">#</span><span class="code-snippet__meta"><span class="code-snippet__keyword">include</span></span><span class="code-snippet__meta"> </span><span class="code-snippet__meta"><span class="code-snippet__string"><iostream></span></span></span></code><code><span leaf=""><span class="code-snippet__meta">#</span><span class="code-snippet__meta"><span class="code-snippet__keyword">include</span></span><span class="code-snippet__meta"> </span><span class="code-snippet__meta"><span class="code-snippet__string"><vector></span></span></span></code><code><span leaf=""><span class="code-snippet__meta">#</span><span class="code-snippet__meta"><span class="code-snippet__keyword">include</span></span><span class="code-snippet__meta"> </span><span class="code-snippet__meta"><span class="code-snippet__string"><algorithm></span></span></span></code><code><span leaf=""><span class="code-snippet__meta">#</span><span class="code-snippet__meta"><span class="code-snippet__keyword">include</span></span><span class="code-snippet__meta"> </span><span class="code-snippet__meta"><span class="code-snippet__string"><cmath></span></span></span></code><code><span leaf=""><span class="code-snippet__keyword">using</span> <span class="code-snippet__keyword">namespace</span> std;</span></code><code><span leaf=""><span class="code-snippet__comment">// ==================== 核心1:浮点数比较(排序必用!)====================</span></span></code><code><span leaf=""><span class="code-snippet__comment">// 原理:float是二进制近似存储,直接==>/</=必出错</span></span></code><code><span leaf=""><span class="code-snippet__comment">// 正确方案:放大转整数比较(无精度损耗,排序最稳定)</span></span></code><code><span leaf=""><span class="code-snippet__type">const</span> <span class="code-snippet__type">int</span> SCALE = <span class="code-snippet__number">1000000</span>; <span class="code-snippet__comment">// 放大系数,支持小数点后6位精度</span></span></code><code><span leaf=""><span class="code-snippet__function"><span class="code-snippet__type">bool</span></span><span class="code-snippet__function"> </span><span class="code-snippet__function"><span class="code-snippet__title">floatCompare</span></span><span class="code-snippet__function"><span class="code-snippet__params">(</span></span><span class="code-snippet__function"><span class="code-snippet__params"><span class="code-snippet__type">float</span></span></span><span class="code-snippet__function"><span class="code-snippet__params"> a, </span></span><span class="code-snippet__function"><span class="code-snippet__params"><span class="code-snippet__type">float</span></span></span><span class="code-snippet__function"><span class="code-snippet__params"> b)</span></span><span class="code-snippet__function"> </span>{</span></code><code><span leaf="">    <span class="code-snippet__type">long</span> <span class="code-snippet__type">long</span> na = <span class="code-snippet__built_in">llround</span>(a * SCALE);</span></code><code><span leaf="">    <span class="code-snippet__type">long</span> <span class="code-snippet__type">long</span> nb = <span class="code-snippet__built_in">llround</span>(b * SCALE);</span></code><code><span leaf="">    <span class="code-snippet__keyword">return</span> na < nb; <span class="code-snippet__comment">// 升序</span></span></code><code><span leaf="">}</span></code><code><span leaf=""><span class="code-snippet__comment">// ==================== 核心2:自定义排序(结构体/对象排序)====================</span></span></code><code><span leaf=""><span class="code-snippet__keyword">struct</span> <span class="code-snippet__title">Student</span> {</span></code><code><span leaf="">    <span class="code-snippet__type">int</span> id;</span></code><code><span leaf="">    <span class="code-snippet__type">float</span> score; <span class="code-snippet__comment">// 浮点数分数!坑点密集区</span></span></code><code><span leaf="">};</span></code><code><span leaf=""><span class="code-snippet__comment">// 排序规则:分数升序,分数相同则id升序</span></span></code><code><span leaf=""><span class="code-snippet__function"><span class="code-snippet__type">bool</span></span><span class="code-snippet__function"> </span><span class="code-snippet__function"><span class="code-snippet__title">studentSort</span></span><span class="code-snippet__function"><span class="code-snippet__params">(</span></span><span class="code-snippet__function"><span class="code-snippet__params"><span class="code-snippet__type">const</span></span></span><span class="code-snippet__function"><span class="code-snippet__params"> Student& s1, </span></span><span class="code-snippet__function"><span class="code-snippet__params"><span class="code-snippet__type">const</span></span></span><span class="code-snippet__function"><span class="code-snippet__params"> Student& s2)</span></span><span class="code-snippet__function"> </span>{</span></code><code><span leaf="">    <span class="code-snippet__type">long</span> <span class="code-snippet__type">long</span> n1 = <span class="code-snippet__built_in">llround</span>(s<span class="code-snippet__number">1.</span>score * SCALE);</span></code><code><span leaf="">    <span class="code-snippet__type">long</span> <span class="code-snippet__type">long</span> n2 = <span class="code-snippet__built_in">llround</span>(s<span class="code-snippet__number">2.</span>score * SCALE);</span></code><code><span leaf="">    <span class="code-snippet__keyword">if</span> (n1 != n2) <span class="code-snippet__keyword">return</span> n1 < n2;</span></code><code><span leaf="">    <span class="code-snippet__keyword">return</span> s<span class="code-snippet__number">1.</span>id < s<span class="code-snippet__number">2.</span>id;</span></code><code><span leaf="">}</span></code><code><span leaf=""><span class="code-snippet__function"><span class="code-snippet__type">int</span></span><span class="code-snippet__function"> </span><span class="code-snippet__function"><span class="code-snippet__title">main</span></span><span class="code-snippet__function"><span class="code-snippet__params">()</span></span><span class="code-snippet__function"> </span>{</span></code><code><span leaf="">    <span class="code-snippet__comment">// ==================== 坑1:浮点数直接比较错误 ====================</span></span></code><code><span leaf="">    cout << <span class="code-snippet__string">"===== 浮点数直接比较错误演示 ====="</span> << endl;</span></code><code><span leaf="">    <span class="code-snippet__type">float</span> a = <span class="code-snippet__number">0.1f</span> + <span class="code-snippet__number">0.2f</span>, b = <span class="code-snippet__number">0.3f</span>;</span></code><code><span leaf="">    cout << <span class="code-snippet__string">"0.1+0.2 = "</span> << a << <span class="code-snippet__string">", 0.3 = "</span> << b << endl;</span></code><code><span leaf="">    cout << <span class="code-snippet__string">"直接==结果:"</span> << (a == b ? <span class="code-snippet__string">"相等"</span> : <span class="code-snippet__string">"不相等"</span>) << endl << endl;</span></code><code><span leaf="">    <span class="code-snippet__comment">// ==================== 正确:浮点数放大转整数比较 ====================</span></span></code><code><span leaf="">    cout << <span class="code-snippet__string">"===== 正确:放大转整数比较 ====="</span> << endl;</span></code><code><span leaf="">    cout << <span class="code-snippet__string">"放大后比较结果:"</span> << (<span class="code-snippet__built_in">llround</span>(a*SCALE) == <span class="code-snippet__built_in">llround</span>(b*SCALE) ? <span class="code-snippet__string">"相等"</span> : <span class="code-snippet__string">"不相等"</span>) << endl << endl;</span></code><code><span leaf="">    <span class="code-snippet__comment">// ==================== 坑2:浮点数直接排序错误 ====================</span></span></code><code><span leaf="">    cout << <span class="code-snippet__string">"===== 浮点数直接排序(错误)====="</span> << endl;</span></code><code><span leaf="">    vector<<span class="code-snippet__type">float</span>> arr = {<span class="code-snippet__number">0.3</span>, <span class="code-snippet__number">0.1+0.2</span>, <span class="code-snippet__number">0.5</span>, <span class="code-snippet__number">0.2</span>};</span></code><code><span leaf="">    <span class="code-snippet__built_in">sort</span>(arr.<span class="code-snippet__built_in">begin</span>(), arr.<span class="code-snippet__built_in">end</span>()); <span class="code-snippet__comment">// 直接用默认比较,精度丢失导致乱序</span></span></code><code><span leaf="">    <span class="code-snippet__keyword">for</span> (<span class="code-snippet__keyword">auto</span> f : arr) cout << f << <span class="code-snippet__string">" "</span>;</span></code><code><span leaf="">    cout << endl << endl;</span></code><code><span leaf="">    <span class="code-snippet__comment">// ==================== 正确:浮点数专用排序 ====================</span></span></code><code><span leaf="">    cout << <span class="code-snippet__string">"===== 浮点数放大转整数排序(正确)====="</span> << endl;</span></code><code><span leaf="">    <span class="code-snippet__built_in">sort</span>(arr.<span class="code-snippet__built_in">begin</span>(), arr.<span class="code-snippet__built_in">end</span>(), floatCompare);</span></code><code><span leaf="">    <span class="code-snippet__keyword">for</span> (<span class="code-snippet__keyword">auto</span> f : arr) cout << f << <span class="code-snippet__string">" "</span>;</span></code><code><span leaf="">    cout << endl << endl;</span></code><code><span leaf="">    <span class="code-snippet__comment">// ==================== 实战:结构体+浮点数 综合排序 ====================</span></span></code><code><span leaf="">    cout << <span class="code-snippet__string">"===== 结构体+浮点数 综合排序 ====="</span> << endl;</span></code><code><span leaf="">    vector<Student> stus = {{<span class="code-snippet__number">1</span>, <span class="code-snippet__number">90.5f</span>}, {<span class="code-snippet__number">2</span>, <span class="code-snippet__number">85.3f</span>}, {<span class="code-snippet__number">3</span>, <span class="code-snippet__number">90.5f</span>}, {<span class="code-snippet__number">4</span>, <span class="code-snippet__number">92.1f</span>}};</span></code><code><span leaf="">    <span class="code-snippet__built_in">sort</span>(stus.<span class="code-snippet__built_in">begin</span>(), stus.<span class="code-snippet__built_in">end</span>(), studentSort);</span></code><code><span leaf="">    <span class="code-snippet__keyword">for</span> (<span class="code-snippet__keyword">auto</span>& s : stus) cout << <span class="code-snippet__string">"id:"</span> << s.id << <span class="code-snippet__string">" 分数:"</span> << s.score << endl;</span></code><code><span leaf="">    <span class="code-snippet__comment">// ==================== 排序隐藏坑点总结 ====================</span></span></code><code><span leaf="">    cout << <span class="code-snippet__string">"n===== 排序必背坑点 ====="</span> << endl;</span></code><code><span leaf="">    cout << <span class="code-snippet__string">"1. 整数排序:直接用sort,无坑n"</span>;</span></code><code><span leaf="">    cout << <span class="code-snippet__string">"2. 浮点数排序:禁止直接比,必须放大转整数n"</span>;</span></code><code><span leaf="">    cout << <span class="code-snippet__string">"3. 字符串排序:默认按ASCII,中文需指定编码n"</span>;</span></code><code><span leaf="">    cout << <span class="code-snippet__string">"4. 结构体排序:必须写自定义比较函数,禁止省略n"</span>;</span></code><code><span leaf="">    cout << <span class="code-snippet__string">"5. 稳定性:stable_sort()保留相等元素顺序,sort()不保证n"</span>;</span></code><code><span leaf="">    cout << <span class="code-snippet__string">"6. 降序排序:直接用greater<>()或自定义函数n"</span>;</span></code><code><span leaf="">    <span class="code-snippet__keyword">return</span> <span class="code-snippet__number">0</span>;</span></code><code><span leaf="">}</span>

一、浮点数比较 / 排序:所有程序员必踩坑

为什么不能直接比?

  • float/double 是二进制近似存储
    ,无法精确表示 0.1、0.2 这类小数
  • 0.1 + 0.2
     计算结果 ≠ 0.3,直接比较会出现逻辑错误
  • 排序时直接用默认规则,会出现明明相等却排乱的问题

唯一工程正确方案:放大系数转整数比较

  • 放大系数 1000000 → 支持小数点后 6 位精度
  • 用 llround() 四舍五入,避免截断误差
  • 转成 long long 后可安全使用 == > <
  • 排序、判断、逻辑判断全部通用


二、C++ 排序全技巧

1. 基础排序


C++ 片段需补全 main() 函数才能运行在实训台打开 →
<span leaf="">vector<<span class="code-snippet__type">int</span>> v = {<span class="code-snippet__number">3</span>,<span class="code-snippet__number">1</span>,<span class="code-snippet__number">2</span>};</span></code><code><span leaf=""><span class="code-snippet__built_in">sort</span>(v.<span class="code-snippet__built_in">begin</span>(), v.<span class="code-snippet__built_in">end</span>());         <span class="code-snippet__comment">// 升序(默认)</span></span></code><code><span leaf=""><span class="code-snippet__built_in">sort</span>(v.<span class="code-snippet__built_in">begin</span>(), v.<span class="code-snippet__built_in">end</span>(), <span class="code-snippet__built_in">greater</span><<span class="code-snippet__type">int</span>>()); <span class="code-snippet__comment">// 降序</span></span>
2. 稳定排序(保留相等元素原始顺序)
stable_sort(v.begin(), v.end());

3. 字符串排序

  • 默认按 ASCII 码 排序
  • 中文需用 locale 或自定义编码比较

4. 结构体 / 对象排序

  • 必须写自定义比较函数
  • 含浮点数必须放大转整数再比

5. 部分排序(只排前 N 个)

partial_sort(v.begin(), v.begin()+3, v.end());
6. 查找第 N 大元素(O (n) 效率)
nth_element(v.begin(), v.begin()+2, v.end());

三、排序 10 大致命坑点

  1. 浮点数直接 == 比较
     → 必错
  2. 浮点数直接排序
     → 乱序
  3. 比较函数返回 = 或 >= → 崩溃 / 死循环
  4. 排序函数传值而非传引用 → 效率暴跌
  5. 字符串默认排序中文乱序
  6. 忽略稳定性要求用了 sort 而非 stable_sort
  7. 容器越界、空容器直接排序
  8. 自定义排序规则矛盾(双向返回 true)
  9. 大数排序溢出(int 改 long long)
  10. 未包含 <algorithm> 头文件


四、一句话背会核心规则

  1. 整数随便排
  2. 浮点数禁止直接比,必须放大转整数
  3. 结构体必须自定义排序
  4. 比较函数只返回 <或>
  5. 稳定排序用 stable_sort


⚠️ 版权声明

本文为免费文章,欢迎转载。转载请注明出处:一木科技(https://www.pingquan.vip/2026/05/31/c%e7%ae%97%e6%b3%95-%e6%8e%92%e5%ba%8f-99-%e7%a8%8b%e5%ba%8f%e5%91%98%e5%bf%85%e8%b8%a9%e7%9a%84%e6%b5%ae%e7%82%b9%e6%95%b0-%e6%8e%92%e5%ba%8f%e5%9d%91/),并保留原文链接。