浮点数格式化小探究

news/2024/11/5 9:19:48/文章来源:https://www.cnblogs.com/cherishui/p/18527183

在最近的工作中,遇到一个浮点数格式化问题,蛮有意思的,是之前所没遇到过的知识点,在此整理总结。

问题描述

一句话描述问题,将一个3位小数的浮点数,格式化为2位小数的,是什么样的舍入规则?一般想着的是四舍五入,但实际不是,具体如何,看如下程序。

测试代码如下:


void test_float_format()
{const int nBufSize = 32;char szBuf[nBufSize] = { 0 };float d1 = 10.564;	// 10.505 10.515 10.525float d2 = 10.565;float d3 = 10.566;	// sprintf_s(szBuf, nBufSize, "%.2f", d1);printf("d1: %s\n", szBuf);memset(szBuf, 0, nBufSize);sprintf_s(szBuf, nBufSize, "%.2f", d2);printf("d2: %s\n", szBuf);memset(szBuf, 0, nBufSize);sprintf_s(szBuf, nBufSize, "%.2f", d3);printf("d3: %s\n", szBuf);memset(szBuf, 0, nBufSize);
}

image.png

上面的第二个输出比较怪异,按照数学上4舍5入规则,应该输出10.57的,实际上却是10.56,经过其他验证,发现以4结尾的,格式化时都舍入,6结尾的都进位。当为5结尾时,测试结果如下图所示:

image.png

上述测试程序在Windows和Linux环境上的结果都是如此。

出现上面这种情况,是我不理解的,当结尾小数为5时,不同类型的舍入情况还不一样,这是为什么呢?

在编码上,有以下几点要注意:

  1. 一个小数值,默认为double类型,除非结尾增加f后缀,改为float类型,否则编译器会提示如下错误:

    image.png

  2. double类型占用8个字节,有15位有效数字;float类型占用4个字节,有7位有效数字。还有一种 long double 类型,通常占据12个字节,精度不低于double类型,这种用的较少。

  3. 在涉及到浮点数计算时,优先使用 double类型。

浮点数存储原理

由于浮点数使用固定字节,能表示的数值精度有限,将无穷多个浮点数映射到有效浮点范围时,会引入舍入误差。

具体来说,就是当某个浮点数的准确数值,二进制化后,落在某两个二进制浮点数数值范围之间时,如何处理就是个问题。

对此,IEEE 754 arithmetic and rounding规定了4种舍入规则:

1. Round to nearest: 四舍五入到Frac最接近的偶数位> The system chooses the nearer of the two possible outputs. If the correct answer is exactly halfway between	 
> the two, the system chooses the output where the least significant bit of Frac is zero. This behavior
> (round-to-even) prevents various undesirable effects.> This is the default mode when an application starts up. It is the only mode supported by the ordinary
> floating-point libraries. Hardware floating-point environments and the enhanced floating-point libraries
> support all four rounding modes.从两个可能的输出中选择较近的output。如果正确答案正好介于两者之间,则选择 Frac 的最低有效位为零的输出。 
2. Round up 向正无穷大舍入选择两个可能的输出中较大的一个,称为 round toward +
3. Round down 向负无穷大舍入选择两个可能的输出中较小的一个
4. Round toward zero 朝零舍入,称为 round toward -选择两个可能的输出中,更接近0的那一个,称为 round toward 0

C语言的浮点库默认为采用模式1,可通过 fesetround 函数来设置舍入模式。

针对模式1的理解,在进行舍入处理时,系统会选择与真实值最靠近的浮点数来表示。比如将3位小数(eg:10.564)格式化为2位,它会在10.5710.56中进行判断,10.56410.57相差0.006,与10.56相差0.004,取相差值较小的为准,因此取 10.56
这种方法对小数位小于等于4或大于等于6的情况是OK的,现在考虑小数尾位为5的情况。

10.565格式化为2位小数,有10.5610.57两种选择,两者距离一样,按照上述规范要求,此时应选择Frac最低位为0的那个数。

10.56的二进制表达如下:

image.png

10.57的二进制表达如下:
image.png

一个浮点数在IEEE 754标准中,由三部分组成:

  • sign 位于最高位的符号位,表示正负号,0正1负,占 1bit。
  • exponent 位于中间的指数位,表示大小范围, float占8位,double占11位。
  • fraction 位于最低位的有效数,表示精度范围,float占23位,double占52位。

10.57的最低位有效数值为0,因此,在舍入保持2位小数时,取10.57。反复看了规范说明,规范里面针对的好像是1位小数,舍入为整数的场景。针对多位小数的情况,没有说明,搞不清楚为什么和实际输出的不一样。

工程规避

如果想要在工程中规避这种不确定的舍入,可以手动增加偏移值,使得格式化结果4舍5入的数学认知。比如你要将3位小数格式化为2位,可以加上 0.0005 偏移。

扩大下,如果要对N位小数的原始数据进行格式化,使其满足4舍5入,可加上N+1位的,结尾为5的小数,这样可满足4舍5入规则。

同样的3位浮点数,保留2位有效数字,不同数值范围、不同存储格式的舍入表现不一样,很令人疑惑。

如有知道详情的,请不吝赐教。

参考链接:

  1. https://trekhleb.dev/blog/2021/binary-floating-point/
  2. https://bartaz.github.io/ieee754-visualization/
  3. 将十进制转换为任意形式
  4. IEEE 574学习计算器

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【教程】使用 eac3to 编码高清音频

原文转自GPW,感谢原文作者对此文的撰写,本文略有修改!修改如下:删除:改进 24 bit 音频流的文件大小相关URL首先,你需要下载 eac3to。如果你想要编码 AAC,那么你还需要 QAAC 和 AppleApplicationSupport。eac3to 是一款命令行工具,你可以通过 Windows 10 的命令提示符运…

PbootCMS 修改域名授权提示信息

PbootCMS程序上传到服务器后,如果没有到官网获取域名授权码会提示未授权的相关提示信息,但是有时候我们是给客户使用,并不想客户看到此信息,那么怎么办呢? 其实官方已经预制了免费的解决方案,只需要在网站根目录下新建一个 `sn.html` 的文件,里面编写自己的提示信息,比…

帝国CMS密码忘记重置方法

如果你忘记了帝国CMS的后台管理密码,可以通过以下步骤进行重置:备份数据库:在进行任何数据库操作之前,请确保备份当前的数据库,以防止数据丢失。登录数据库:使用数据库管理工具(如phpMyAdmin)登录到你的数据库。找到用户表:寻找名为 phome_enewsuser 的表,这是存储管…

织梦dede上传图片提示缺少图像源文件地址

检查 uploads 文件夹权限确保 uploads 文件夹有写入权限。检查 php.ini 配置确保 upload_tmp_dir 有自定义路径,并且该路径有写入权限。 如果没有自定义路径,可以尝试切换PHP版本。检查上传文件大小限制确保上传的图片文件大小不超过 php.ini 中 post_max_size 和 upload_max…

开源自托管数据管理工具全面指南

探索最佳开源自托管数据管理工具,如 NocoBase,Airflow,Singer 等。在大数据时代,企业和组织面临着海量的数据挑战。随着应用程序复杂性的提高以及用户需求不断演变,开发团队需要高效地处理大量数据,以便快速做出决策。然而,在众多信息中,如何识别并有效利用那些对决策至…

数据安全秘籍:500强企业的经典传输案例大揭秘

很多企业都会有数据安全建设的烦恼,不知道从何开始,哪里又是建设重点?那不妨借鉴一下500强企业的做法,它们在数据安全建设方面通常采取多层次的策略,具体包括以下几个方面: 风险评估与管理:定期进行全面的风险评估,识别数据安全风险,制定相应的管理策略。 安全政策与标…

异源数据同步 → DataX 同步启动后如何手动终止?

开心一刻 刚刚和老婆吵架,气到不行,想离婚女儿突然站出来劝解道:难道你们就不能打一顿孩子消消气,非要闹离婚吗?我和老婆同时看向女儿,各自挽起了衣袖女儿补充道:弟弟那么小,打他,他又不会记仇需求背景 项目基于 DataX 来实现异源之间的数据离线同步,我对 Datax 进行…

开源的 API 学习平台「GitHub 热点速览」

前有 5 万颗星标的开源项目 HTTPie 因误操作导致 Star 清零(2022 年),上周知名开源项目 Elasticsearch 也经历了 Star 一夜清零的事件。这些事故的原因均是管理员误将开源项目从公开状态转为私有状态所导致。为避免类似事件再次发生,GitHub 已在转为私有的功能处增加了两次…

一文夯实垃圾收集的理论基础

如何判断一个引用是否存活 引用计数法 给对象中添加一个引用计数器,每当有一个地方引用它,计数器就加 1;当引用失效,计数器就减 1;任何时候计数器为 0 的对象就是不可能再被使用的。 优点:可即刻回收垃圾,当对象计数为0时,会立刻回收; 弊端:循环引用时,两个对象的计…

c++实现livox-mid70/360采集、保存点云数据

c++实现livox-mid70/360采集、保存点云数据void PointCloudCallback(uint32_t handle, const uint8_t dev_type, LivoxLidarEthernetPacket* data, void* client_data) {if (data == nullptr) {return;}if (data->data_type == kLivoxLidarCartesianCoordinateHighData) {Li…