关于浮点数的四舍五入问题

news2024/9/24 21:25:18

最近有关注到,在C/C++中,对于浮点数的四舍五入,与实际的有一些出入,我打算今天总结一下,并解释一下这是为啥,

好了,下面进入正题,都是干货哦,认真看完,留下你的赞,哈哈,

首先,说到四舍五入,就首先要想到取整函数,但是今天不说这些取整函数哦,就是来解释一下一些结果与不符和常理的结果。

四舍五入,大于5,就进位,小于5,就舍去,绝大多数的问题出在了5上面,这个5究竟特殊在哪里呢?

1,精度丢失

首先咱们来看一段代码

#include <stdio.h>

int main() {
    double a = 80.845;
    float b = 80.845f;
    printf("a = %.2lf\n",a);
    printf("b = %.2f\n",b);
    
    return 0;
}

这段代码的意思是,分别对80.845这个数保留两位小数打印,那结果都应该是80.85。对,按理来说就应该是这个答案。那我们运行一下看一下

好的,我们看到了答案,b的答案是正常进行四舍五入了的,a是80.84,这是没有进行进位吗,

聪明的小伙伴已经注意到了,a是double类型的,b是float类型的,哈哈哈,问题是不是在这里呢。对的,问题就出在这里,因为double用8个字节进行存储数据,float是用4个字节存储数据,是不是他们在存储数据的时候出现了问题呢,咱们打印出来试一下

当我们打印20个小数位的时候,差距就出来了。(float类型的有效数据是7位,double是16位,其他的均是误差,可忽略,我们测试用的数据都是在有效范围内的)

我们发现,double中存储80.845的时候,存储的是80.84499999999999886313,float在存储80.845的时候,存储的是80.84500122070312500000,

没错,就是存储的锅,因为存储的数不是80.845,是80.844...,所以才没有进位,

那为什么会是这种结果呢,我们知道浮点数在内存中存储的时候,也是存储的二进制位,对于整数的二进制位我们都很熟悉,那么小数部分的二进制怎么表示呢,

//从二进制的权值表示不难推断:
//    0      0      0      0     .    0       0      0      0
//    2^3    2^2    2^1    2^0        2^-1    2^-2   2^-3   2^-4

是这样表示的,所以我们要表示一个浮点数的小数部分的话,就需要去凑,举个例子

double a = 0.875;
// 0.875 : 0.5 + 0.25 + 0.125
//         2^-1  2^-2    2^-3
//所以用二进制表示就是 0.111

看到这里大概就懂了,因为有些数是凑不出来的,比如说0.3,所以就产生了误差,但是计算机在尽力给我们凑了,保证误差尽可能的小,

那有的小伙伴就开始说了,是不是以后要判断一个数是否会四舍五入,就多打印几位出来,是不是就可以知道是不是会进位了,

我只能说大部分情况是可以的,但是我这篇文章还没完呢,还有一个标题呢,接着往下看

2,银行家舍入法

银行家舍入法,又叫做四舍六入五凑偶法,是由IEEE 754标准规定的浮点数取整算法,我们先来解释一下是什么意思

先来看一段代码

#include <stdio.h>

int main() {
    double c = 80.25;
    float d = 80.25;
    printf("c = %.1lf\n",c);
    printf("c = %.20lf\n",c);
    printf("d = %.1f\n",d);
    printf("d = %.20f\n",d);
    return 0;
}

代码很简单,就是对于80.25,保留一位小数打印,因为经过上面的解释,我们知道0.25是可以被精确表示的,不会存在精度丢失的情况,我们执行来看看

好的,我们看到80.25不管是double还是float类型都可以精确保存,没有精度丢失,但是还是没有进位,这是为啥

这就是银行家舍入算法,来解释一下:

银行家舍入法只有在被舍入位的值是5的时候,并且,舍入位的后面没有任何数据的时候,才会触发,这个时候会判断进位之后的那一位是否是偶数,如果是偶数,就进位,如果不是偶数,就选择不进位,可以理解成趋偶性。

80.25保留一位小数就刚好符合这两个条件。

为了验证,我改了两个数据,

当数据是80.25001,内存中存储的这个数的二进制位5的后面还有数据,不符和银行家舍入法,所以就会进位,

当数据是80.75的时候,这个时候,也是可以精确表示的,没有精度丢失,舍入位是5,并且5之后没有其他数据,这个时候就会,触发银行家舍入法,趋偶性,就会选择进位,

结语

最后,我在gcc上跑完之后,又去vs2022上验证了一下,都是一样的,所以大胆食用,

关于这个算法,我自己的看法就是,为什么选择趋偶呢,是因为大多数偶数是不会发生精度丢失的,可以避免很多系统性误差,

我也查了很多资料,关于银行家舍入法。结果显示,银行家舍入法旨在减小舍入误差,以确保结果更接近数学期望,而且,金融和会计领域通常要求精确的数值计算,而银行家舍入法在这些领域中是常见的舍入方式。

最后的最后,希望我的这篇文章可以帮助到看了文章的你,

欢迎大家评论点赞转发,如果有什么错误,可以辛苦私信指出(麻烦了)!

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

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

相关文章

非常好用的Mac清理工具CleanMyMac X 4.14.7 如何取消您对CleanMyMac X的年度订购

CleanMyMac X 4.14.7是Mac平台上的一款非常著名同时非常好用的Mac清理工具。全方位扫描您的Mac系统&#xff0c;让垃圾无处藏身&#xff0c;您只需要轻松单击2次鼠标左键即可清理数G的垃圾&#xff0c;就这么简单。瞬间提升您Mac速度。 CleanMyMac X 4.14.7下载地址&#xff1a…

Linux Mii management/mdio子系统分析之三 mii_bus注册、注销及其驱动开发流程

&#xff08;转载&#xff09;原文链接&#xff1a;https://blog.csdn.net/u014044624/article/details/123303174 本篇是mii management/mdio模块分析的第三篇文章&#xff0c;本章我们主要介绍mii-bus的注册与注销接口。在前面的介绍中也已经说过&#xff0c;我们可以将mii-b…

如何增加服务器的高并发

随着互联网的快速发展和普及&#xff0c;越来越多的应用程序需要支持高并发的请求处理。在这种情况下增加服务器的高并发能力成为了一个热门的话题。下面简单的介绍如果提高服务器的高并发能力。 负载均衡 是把请求分发到多个服务器上&#xff0c;来实现请求的平衡和分担。负…

compose 实验

cd /opt mkdir compose_nginx cd compose_nginx mkdir nginx cd nginx/ 此时顺便将nginx安装包拖进来 vim Dockerfile mkdir /opt/compose_nginx/wwwroot echo "<h1>this is test web</h1>" > /opt/compose_nginx/wwwroot/index.html docker netw…

如何配置mybatisplus基础环境?

1.在pom文件&#xff08;都加上吧&#xff0c;以防万一&#xff09; 2.若当初有mybatis的依赖&#xff0c;要删除 3.在Mapper接口加上"extends BaseMapper<实体类型>" 4.更改yml文件内容 别名扫描包&#xff1a;是指实体类型 5.添加"extends ServiceIm…

SQL语句详解四-DQL(数据查询语言-约束)

约束 概述&#xff1a;对表中的数据进行限定&#xff0c;保证数据的正确性&#xff0c;有效性和完整性。 约束分类 约束关键字约束意思primary key主键约束not null非空约束unique唯一约束foreign key外键约束 例子&#xff1a;sname varchar(40) not null, – 代表 sname 这…

【C语言】指针知识点笔记(2)

目录 一、野指针 二、assert断言 三、指针的使用和传址调用 四、数组名的理解 五、使用指针访问数组 一、野指针 二、assert断言 三、指针的使用和传址调用 四、数组名的理解 五、使用指针访问数组

Web 服务器渗透测试清单

Web 服务器渗透测试在三个重要类别下进行&#xff1a;身份、分析和报告漏洞&#xff0c;例如身份验证弱点、配置错误和协议关系漏洞。 1. “进行一系列有条不紊且可重复的测试”是测试网络服务器是否能够解决所有不同应用程序漏洞的最佳方法。 2.“收集尽可能多的信息”关于…

AtCoder Beginner Contest 336 G. 16 Integers(图计数 欧拉路径转欧拉回路 矩阵树定理 best定理)

题目 给16个非负整数&#xff0c;x[i∈(0,1)][j∈(0,1)][k∈(0,1)][l∈(0,1)] 求长为n3的01串的方案数&#xff0c;满足长度为4的ijkl&#xff08;2*2*2*2&#xff0c;16种情况&#xff09;串恰为x[i][j][k][l]个 答案对998244353取模 思路来源 https://www.cnblogs.com/tz…

多线程并发与并行

&#x1f4d1;前言 本文主要是【并发与并行】——并发与并行的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是听风与他&#x1f947; ☁️博客首页&#xff1a;CSDN主页听风与他 &#x1f304;每日一句&…

03 顺序表

目录 线性表顺序表练习 线性表(Linear list)是n个具有相同特性的数据元素的有限序列。线性表是一种在实际中广泛使用的数据结构&#xff0c;常见的线性表&#xff1a;顺序表、链表、栈、队列、字符串。。。 线性表在逻辑上时线性结构&#xff0c;是连续的一条直线。但在物理结…

【PostgreSQL内核学习(二十一)—— 执行器(InitPlan)】

执行器&#xff08;InitPlan&#xff09; 概述InitPlan 函数代码段解释ExecInitNode 函数 总结 声明&#xff1a;本文的部分内容参考了他人的文章。在编写过程中&#xff0c;我们尊重他人的知识产权和学术成果&#xff0c;力求遵循合理使用原则&#xff0c;并在适用的情况下注明…

力扣每日一练(24-1-16)

我一开始想到的是&#xff0c;如果数字相同则加一。 然而&#xff0c;对了一点点&#xff0c;而已。 高手的方法不是普通人在几分钟内能想得出来的&#xff0c;hh 继续补充&#xff1a; 如果数字不同则减一&#xff0c;如果计数到达了0&#xff0c;则更新数字&#xff0c;最…

【极光系列】springboot集成redis

【极光系列】springboot集成redis tips&#xff1a;主要用于快速搭建环境以及部署项目入门 gitee地址 直接下载源码可用 https://gitee.com/shawsongyue/aurora.git模块&#xff1a;aurora_rediswindow安装redis安装步骤 1.下载资源包 直接下载解压&#xff1a;https://pa…

PHP项目如何自动化测试

开发和测试 测试和开发具有同等重要的作用 从一开始&#xff0c;测试和开发就是相向而行的。测试是开发团队的一支独立的、重要的支柱力量。 测试要具备独立性 独立分析业务需求&#xff0c;独立配置测试环境&#xff0c;独立编写测试脚本&#xff0c;独立开发测试工具。没有…

华硕原厂系统天选5Pro原厂Win11系统恢复安装过程方法

华硕原厂系统天选5Pro原厂Win11系统恢复安装过程方法 华硕原厂系统枪神8/枪神8plus原厂Win11系统恢复安装过程方法 还是老规矩&#xff0c;分3种安装方法 远程恢复安装&#xff1a;https://pan.baidu.com/s/166gtt2okmMmuPUL1Fo3Gpg?pwdm64f 提取码:m64f 支持型号&#x…

new Handler(getMainLooper())与new Handler()的区别

Handler 在Android中是一种消息处理机制。 new Handler(); 创建handler对象&#xff0c;常用在已经初始化了 Looper 的线程中调用这个构造函数&#xff08;即非主线程&#xff09;&#xff0c;如果感觉不好理解&#xff0c;可以把Handler handler new Handler() 理解为常用在…

Vue3中使用自定义指令

一&#xff0c;自定义指令&#xff1a; 应用场景&#xff1a;禁用按钮多次点击 1.vue2 a. src/libs/preventClick.js import Vue from vue const preventClick Vue.directive(preventClick, {inserted: function (el, binding) {el.addEventListener(click, () > {if (!el…

MySQL多表查询(改进版)

1.创建student和score表 mysql> CREATE TABLE student (-> id INT(10) NOT NULL UNIQUE PRIMARY KEY ,-> name VARCHAR(20) NOT NULL ,-> sex VARCHAR(4) ,-> birth YEAR,-> department VARCHAR(20) ,-> address VARCHAR(50)-> ); Query O…

C#用double.TryParse(String, Double)方法将字符串类型数字转换为数值类型

目录 一、定义 二、实例 命名空间: System 程序集: System.Runtime.dll 一、定义 将数字的字符串表示形式转换为它的等效双精度浮点数。 一个指示转换是否成功的返回值。 public static bool TryParse (string? s, out double result…