C++ round 函数笔记 (适用于算法竞赛)

news2025/4/25 17:54:40

在算法竞赛中,处理浮点数并将其转换为整数是常见的需求,round 函数是标准库提供的用于执行“四舍五入”到最近整数的工具。理解其工作方式和潜在问题对于避免错误至关重要。

1. 基本用法

头文件

要使用 round 函数,需要包含 <cmath> 头文件 (C++ 风格) 或 <math.h> (C 风格)。推荐使用 <cmath>

#include <cmath>
#include <iostream>

int main() {
    double num1 = 3.14;
    double num2 = 3.75;
    double num3 = -2.3;
    double num4 = -2.8;
    double num5 = 4.5;
    double num6 = -4.5;

    std::cout << "round(" << num1 << ") = " << std::round(num1) << std::endl; // 输出 3.0
    std::cout << "round(" << num2 << ") = " << std::round(num2) << std::endl; // 输出 4.0
    std::cout << "round(" << num3 << ") = " << std::round(num3) << std::endl; // 输出 -2.0
    std::cout << "round(" << num4 << ") = " << std::round(num4) << std::endl; // 输出 -3.0
    std::cout << "round(" << num5 << ") = " << std::round(num5) << std::endl; // 输出 5.0
    std::cout << "round(" << num6 << ") = " << std::round(num6) << std::endl; // 输出 -5.0

    return 0;
}

函数签名

round 函数有多个重载版本,接受不同的浮点类型:

double round(double x);
float roundf(float x);       // C++11 起通常也有 round(float)
long double roundl(long double x); // C++11 起通常也有 round(long double)

在 C++ 中,通常可以直接使用 std::round(),编译器会根据参数类型自动选择合适的版本。

2. 核心规则:四舍五入到最近整数

round 函数将浮点数 x 舍入到最接近的整数值。

关键点:中间值处理 (Handling Halves)

  • 当小数部分恰好为 0.5 时,round 函数遵循**“远离零”**的原则进行舍入。
    • round(2.5) 结果是 3.0
    • round(-2.5) 结果是 -3.0
  • 这与某些其他编程语言或场景下的“四舍五入到偶数”(Round half to even / Banker’s rounding)不同。

3. 返回值类型

非常重要: round 函数的返回值类型仍然是浮点类型 (double, float, long double),即使它表示的是一个整数值。

如果你需要一个真正的整数类型 (int, long long 等),你需要进行显式类型转换

#include <cmath>
#include <iostream>

int main() {
    double val = 9.8;
    double rounded_val_double = std::round(val); // rounded_val_double 是 10.0 (double类型)

    // 显式转换为整数类型
    int rounded_val_int = static_cast<int>(rounded_val_double);       // 推荐的C++风格转换
    long long rounded_val_ll = static_cast<long long>(std::round(1e10 + 0.3)); // 处理可能较大的数

    std::cout << "Rounded (double): " << rounded_val_double << std::endl;
    std::cout << "Rounded (int): " << rounded_val_int << std::endl;
    std::cout << "Rounded (long long): " << rounded_val_ll << std::endl; // 输出 10000000000

    // 注意潜在的溢出问题
    double large_val = 3e9; // 3 * 10^9
    int overflow_int = static_cast<int>(std::round(large_val));
    // 上一行可能导致整数溢出,结果未定义或取决于实现
    std::cout << "Potential overflow: " << overflow_int << std::endl;

    return 0;
}

4. 注意事项与竞赛技巧

a. 浮点数精度问题 (Floating-Point Precision Issues)

  • 这是在算法竞赛中使用浮点数(包括 round)时最需要注意的问题。
  • 计算机存储浮点数存在误差。一个看似是 x.5 的数,在内存中可能是 x.4999999999999999x.5000000000000001
  • 这会导致 round 的结果与预期不符。例如,你期望 round(a / b * c) 得到某个整数,但由于中间计算的精度损失,结果可能差一点点,导致 round 结果错误。

竞赛建议:

  1. 尽量避免浮点数: 如果问题可以通过全程使用整数算术解决,优先选择整数。例如,比较 a/bc/d 时,转换为比较 a*dc*b (注意溢出)。
  2. 使用 Epsilon (eps): 在判断浮点数相等或比较大小时,引入一个极小值 eps (如 1e-81e-9)。例如,判断 x 是否接近整数 n,可以用 fabs(x - n) < eps
  3. 谨慎使用 round 只有在确实需要四舍五入,并且能接受微小误差带来的潜在风险时使用。

b. 手动实现整数“四舍五入”

有时,为了完全避免浮点误差,或者需要对整数运算结果进行类似四舍五入的操作,可以手动实现:

  • 对于非负数 x (int)(x + 0.5)(long long)(x + 0.5)
  • 对于负数 x (int)(x - 0.5)(long long)(x - 0.5)
#include <iostream>
#include <vector> // 仅用于演示

// 简化的手动四舍五入到整数 (int)
int manual_round_int(double x) {
    if (x >= 0.0) {
        return static_cast<int>(x + 0.5);
    } else {
        return static_cast<int>(x - 0.5);
    }
}

// 更通用的手动四舍五入到长整型 (long long)
long long manual_round_ll(double x) {
     if (x >= 0.0) {
        return static_cast<long long>(x + 0.5);
    } else {
        return static_cast<long long>(x - 0.5);
    }
}

int main() {
    std::cout << "Manual round(3.7): " << manual_round_int(3.7) << std::endl;   // 4
    std::cout << "Manual round(3.2): " << manual_round_int(3.2) << std::endl;   // 3
    std::cout << "Manual round(3.5): " << manual_round_int(3.5) << std::endl;   // 4
    std::cout << "Manual round(-3.7): " << manual_round_int(-3.7) << std::endl; // -4
    std::cout << "Manual round(-3.2): " << manual_round_int(-3.2) << std::endl; // -3
    std::cout << "Manual round(-3.5): " << manual_round_int(-3.5) << std::endl; // -4

    // 示例:整数除法的四舍五入
    int a = 10, b = 3;
    // int result = a / b; // 结果是 3 (整数截断)
    // 要实现 a/b 的四舍五入结果:
    int rounded_div = static_cast<int>(static_cast<double>(a) / b + 0.5); // 先转double再加0.5
    // 或者,更推荐的整数算术方法(避免浮点):
    // rounded_div = (a + b / 2) / b; // 对于正数 a, b
    std::cout << "Rounded division 10/3: " << rounded_div << std::endl; // 输出 3 (这里10/3=3.33, round是3)
    
    a = 11; b = 3; // 11/3 = 3.66...
    rounded_div = static_cast<int>(static_cast<double>(a) / b + 0.5);
    std::cout << "Rounded division 11/3: " << rounded_div << std::endl; // 输出 4


    return 0;
}

注意: 手动加 0.5 的方法仍然依赖于浮点数 x 的精度。如果 x 本身由于计算误差变成了 y.49999...x + 0.5 可能会小于 y + 1.0,导致转换后仍为 y 而不是预期的 y+1。只有当你知道输入 x 是精确的,或者这种微小误差不影响结果时,它才是可靠的。

c. 考虑 ceil, floor, trunc

<cmath> 还提供了其他相关的取整函数:

  • ceil(x): 向上取整 (Ceiling)。返回 >= x 的最小整数值 (仍为浮点类型)。
  • floor(x): 向下取整 (Floor)。返回 <= x 的最大整数值 (仍为浮点类型)。
  • trunc(x): 向零取整 (Truncate)。直接截断小数部分,取整数部分 (仍为浮点类型)。等价于 (double)((int)x) (如果 xint 范围内且非负)。

根据具体需求选择合适的函数。

d. 输出格式化

有时,你可能只是为了输出一个四舍五入的整数,并不需要在程序内部使用这个整数值。可以使用 printf 的格式化功能:

#include <cstdio> // for printf
#include <cmath>

int main() {
    double val = 9.8;
    double val2 = 9.4;
    double val3 = -9.8;

    printf("%.0f\n", val);  // 输出 10 (%.0f 通常执行四舍五入,规则类似 round)
    printf("%.0f\n", val2); // 输出 9
    printf("%.0f\n", val3); // 输出 -10

    return 0;
}

注意: printf%.0f 的具体舍入规则(尤其是对 0.5 的处理)可能依赖于 C++ 标准库的实现,但通常是“远离零”。最好通过测试确认。

5. 总结

  • std::round 用于将浮点数四舍五入到最接近的整数(中间值 0.5 远离零),返回浮点类型
  • 在竞赛中要高度警惕浮点数精度问题round 结果的影响。
  • 如果需要整数结果,必须进行显式类型转换 (static_cast<int>, static_cast<long long>),并注意溢出
  • 优先考虑全程整数算术,如果可能的话。
  • 了解 ceil, floor, trunc 作为替代方案。
  • 了解 printf("%.0f") 可以用于格式化输出。
  • 手动实现 (int)(x + 0.5) (非负) / (int)(x - 0.5) (负) 是避免调用库函数的一种方式,但仍受输入 x 精度影响。

在竞赛中,选择哪种方法取决于问题的具体要求、数据范围以及对精度的要求。理解各种方法的优缺点和潜在陷阱是取得好成绩的关键。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2342605.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

单例模式与消费者生产者模型,以及线程池的基本认识与模拟实现

前言 今天我们就来讲讲什么是单例模式与线程池的相关知识&#xff0c;这两个内容也是我们多线程中比较重要的内容。其次单例模式也是我们常见设计模式。 单例模式 那么什么是单例模式呢&#xff1f;上面说到的设计模式又是什么&#xff1f; 其实单例模式就是设计模式的一种。…

STM32配置系统时钟

1、STM32配置系统时钟的步骤 1、系统时钟配置步骤 先配置系统时钟&#xff0c;后面的总线才能使用时钟频率 2、外设时钟使能和失能 STM32为了低功耗&#xff0c;一开始是关闭了所有的外设的时钟&#xff0c;所以外设想要工作&#xff0c;首先就要打开时钟&#xff0c;所以后面…

React 与 Vue:两大前端框架的深度对比

在前端开发领域&#xff0c;React 和 Vue 无疑是当下最受欢迎的两大框架。它们各自拥有独特的优势和特点&#xff0c;吸引了大量开发者。无论是初学者还是经验丰富的工程师&#xff0c;选择 React 还是 Vue 都是一个常见的问题。本文将从多个角度对 React 和 Vue 进行对比&…

Java24新增特性

Java 24&#xff08;Oracle JDK 24&#xff09;作为Java生态的重要更新&#xff0c;聚焦AI开发支持、后量子安全、性能优化及开发者效率提升&#xff0c;带来20余项新特性和数千项改进。以下是核心特性的分类解析&#xff1a; 一、语言特性增强&#xff1a;简化代码与模式匹配 …

Sentinel源码—6.熔断降级和数据统计的实现一

大纲 1.DegradeSlot实现熔断降级的原理与源码 2.Sentinel数据指标统计的滑动窗口算法 1.DegradeSlot实现熔断降级的原理与源码 (1)熔断降级规则DegradeRule的配置Demo (2)注册熔断降级监听器和加载熔断降级规则 (3)DegradeSlot根据熔断降级规则对请求进行验证 (1)熔断降级…

Volcano 实战快速入门 (一)

一、技术背景 随着大型语言模型&#xff08;LLM&#xff09;的蓬勃发展&#xff0c;其在 Kubernetes (K8s) 环境下的训练和推理对资源调度与管理提出了前所未有的挑战。这些挑战主要源于 LLM 对计算资源&#xff08;尤其是 GPU&#xff09;的巨大需求、分布式任务固有的复杂依…

用交换机连接两台电脑,电脑A读取/写电脑B的数据

1、第一步&#xff0c;打开控制面板中的网络和共享中心&#xff0c;如下图配置&#xff0c;电脑A和电脑B均要配置&#xff1b; 注意&#xff1a;要保证电脑A和电脑B在同一子网掩码下&#xff0c;不同的IP地址&#xff1b; 2、在电脑上同时按‘CommandR’&#xff0c;在弹出的输…

问道数码兽 怀旧剧情回合手游源码搭建教程(反查重优化版)

本文将对"问道数码兽"这一经典卡通风格回合制手游的服务端部署与客户端调整流程进行详细拆解&#xff0c;适用于具备基础 Windows 运维和手游源码调试经验的开发者参考使用。教程以实战为导向&#xff0c;基于原始说明内容重构优化&#xff0c;具备较高的内容查重避重…

WLAN共享给以太网后以太网IP为169.254.xx.xx以及uboot无法使用nfs下载命令的的解决方案

WLAN共享网络给以太网&#xff0c;实际上是把以太网口当作一个路由器&#xff0c;这个路由器的IP是由WLAN给他分配的&#xff0c;169.254.xx.xx是windows设定的ip&#xff0c;当网络接口无法从上一级网络接口获得ip时&#xff0c;该网络接口的ip被设置为169.254 &#xff0c;所…

ROS 快速入门教程03

8.编写Subscriber订阅者节点 8.1 创建订阅者节点 cd catkin_ws/src/ catkin_create_pkg atr_pkg rospy roscpp std_msgs ros::Subscriber sub nh.subscribe(话题名, 缓存队列长度, 回调函数) 回调函数通常在你创建订阅者时定义。一个订阅者会监听一个话题&#xff0c;并在有…

在 macOS 上合并 IntelliJ IDEA 的项目窗口

在使用 IntelliJ IDEA 开发时&#xff0c;可能会打开多个项目窗口&#xff0c;这可能会导致界面变得混乱。为了提高工作效率&#xff0c;可以通过合并项目窗口来简化界面。本文将介绍如何在 macOS 上合并 IntelliJ IDEA 的项目窗口。 操作步骤 打开 IntelliJ IDEA: 启动你的 I…

基于多用户商城系统的行业资源整合模式与商业价值探究

随着电子商务的蓬勃发展&#xff0c;传统的单一商家电商模式逐渐显现出一定的局限性。为了解决商家成本过高、市场竞争激烈等问题&#xff0c;多用户商城系统应运而生&#xff0c;成为一种新型的电商平台模式。通过整合行业资源&#xff0c;这种模式不仅极大地提升了平台和商家…

Three.js + React 实战系列 : 从零搭建 3D 个人主页

可能你对tailiwindcss毫不了解&#xff0c;别紧张&#xff0c;记住我们只是在学习&#xff0c;学习的是作者的思想和技巧&#xff0c;并不是某一行代码。 在之前的几篇文章中&#xff0c;我们已经熟悉了 Three.js 的基本用法&#xff0c;并通过 react-three-fiber 快速构建了一…

如何用大模型技术重塑物流供应链

摘要 在数字化转型加速的背景下&#xff0c;大模型技术凭借其强大的数据分析、逻辑推理和决策优化能力&#xff0c;正成为物流供应链领域的核心驱动力。本文深入探讨大模型如何通过需求预测、智能调度、供应链协同、风险管控等关键环节&#xff0c;推动物流行业从 "经验驱…

【银河麒麟高级服务器操作系统】磁盘只读问题分析

系统环境及配置 系统环境 物理机/虚拟机/云/容器 虚拟机 网络环境 外网/私有网络/无网络 私有网络 硬件环境 机型 KVM Virtual Machine 处理器 Kunpeng-920 内存 32 GiB 整机类型/架构 arm64 固件版本 EFI Development Kit II / OVMF 软件环境 具体操作系统版…

机器视觉的智能手机屏贴合应用

在智能手机制造领域&#xff0c;屏幕贴合工艺堪称"微米级的指尖芭蕾"。作为影响触控灵敏度、显示效果和产品可靠性的关键工序&#xff0c;屏幕贴合精度直接决定了用户体验。传统人工对位方式已无法满足全面屏时代对极窄边框和超高屏占比的严苛要求&#xff0c;而Mast…

AIM Robotics电动胶枪:智能分配,让机器人点胶涂胶精准无误

在现代工业自动化和智能制造领域&#xff0c;精确的液体分配技术正成为提升生产效率和产品质量的重要因素。AIM Robotics作为这一领域的创新者&#xff0c;提供了多种高效、灵活的点胶涂胶分配解决方案。本文将带您了解AIM Robotics的核心技术、产品系列以及在各行业的成功应用…

负环-P3385-P2136

通过选择标签&#xff0c;洛谷刷一个类型的题目还是很方便的 模版题P3385 P3385 【模板】负环 - 洛谷 Tint(input())def bellman(n,edges,sta):INFfloat(inf)d[INF]*(n1)d[sta]0for i in range(n-1):for u,v,w in edges:ncostd[u]wif ncost<d[v]:d[v]ncostfor u,v,w in e…

抖音的逆向工程获取弹幕(websocket和protobuf解析)

目录 声明前言第一节 获取room_id和ttwid值第二节 signture值逆向python 实现signature第三节 Websocket实现长链接请求protubuf反序列化pushFrame反序列化Response解压和反序列化消息体Message解析应答ack参考博客声明 本文章中所有内容仅供学习交流使用,不用于其他任何目的…

WPF 图片文本按钮 自定义按钮

效果 上面图片,下面文本 样式 <!-- 图片文本按钮样式 --> <Style x:Key="ImageTextButtonStyle" TargetType="Button"><Setter Property="Background" Value="Transparent"/><Setter Property="BorderTh…