首页 > 其他分享 >浮点数格式化小探究

浮点数格式化小探究

时间:2024-11-05 09:08:32浏览次数:3  
标签:舍入 格式化 浮点数 float 探究 小数 szBuf

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

问题描述

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

测试代码如下:


void test_float_format()
{
	const int nBufSize = 32;
	char szBuf[nBufSize] = { 0 };
	float d1 = 10.564;	// 10.505 10.515 10.525
	float 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学习计算器

标签:舍入,格式化,浮点数,float,探究,小数,szBuf
From: https://www.cnblogs.com/cherishui/p/18527183

相关文章

  • 《C语言程序设计现代方法》note-2 格式化输入/输出 运算符和表达式
    助记提要转换说明的格式;scanf处理输入的过程;除法操作的注意事项;运算符的结合性;不在子表达式中输入操作数的原因;表达式允许用作语句的问题;3章格式化输入/输出3.1printf函数printf函数用来显示格式串的内容,并在该串中指定位置插入要显示的值。printf(格式串,表达式1,......
  • 鸿蒙Next应用国际化:时间与日期格式化
    本文旨在深入探讨华为鸿蒙HarmonyOSNext系统(截止目前API12)在应用国际化中时间与日期格式化方面的技术细节,基于实际开发实践进行总结。主要作为技术分享与交流载体,难免错漏,欢迎各位同仁提出宝贵意见和问题,以便共同进步。本文为原创内容,任何形式的转载必须注明出处及原作者。在......
  • C语言浮点数实现之实例(二)
            上一章《C语言浮点数实现(一)》给大家讲解了浮点数的由来、组成以及由于浮点数导致的世界级重大事故,以提高大家对学习浮点数积极性,了解浮点数的重要性。虽说大多数场景基本上不会在意这些细节,但是难免会遇见少数场景哈!例如:某迪的底盘团队就遇见过,如果没记错的话,......
  • 个人资源网站更新---格式化工厂
    博主个人博客博主个人资源网站博主个人资源网站备用个人资源网站数据更新…工具名称:格式化工厂简介:格式工厂(FormatFactory)是一款功能全面的格式转换软件,支持转换几乎所有主流的多媒体文件格式,包括视频MP4、AVI、3GP、WMV、MKV、VOB、MOV、FLV、SWF、GIF;音频MP......
  • Python格式化字符串
    1.%格式化最早用%进行格式化字符串#%d%s%f格式化字符串name="Max"num=1print("Hello%s,yournumis%d"%(name,num))#HelloMax,yournumis1#也支持字典形式格式化print("Hello[%(name)s],yournumis%(num)d"%{"num":1,"name&q......
  • 使用wxpython开发跨平台桌面应用,对wxpython控件实现类似C#扩展函数处理的探究
    本人之前对C#开发非常喜欢,也从事开发C#开发桌面开发、Web后端、Vue前端应用开发多年,最近一直在研究使用Python,希望能够把C#的一些好的设计模式、开发便利经验引入到Python开发中,很多时候类似的开发方式,可以极大提高我们开发的效率,本篇随笔对wxpython控件实现类似C#扩展函数处理的......
  • NVMe SSD格式化到底做了什么?
     无论是为了防止数据泄露,还是更改硬盘格式,我们通常会进行格式化操作。在Windows或Linuxshell中对SSD进行格式化(format)时,往往需要几分钟甚至几十分钟,这个时间通常与硬盘容量成正比。那么在这段时间里,SSD到底做了些什么操作呢?为更好地理解本文,这里需要介绍一些SSD的工作原理。......
  • java Web 时间LocalDateTime 输出到前端格式化
    格式化时间返回到前端 packagecom.za.edu.config;importcom.fasterxml.jackson.databind.DeserializationFeature;importcom.fasterxml.jackson.databind.ObjectMapper;importcom.fasterxml.jackson.databind.module.SimpleModule;importcom.fasterxml.jackson.datab......
  • 麒麟操作系统中的磁盘分区和格式化如何进行
    ​为确保硬盘资源的最佳利用和数据安全性,麒麟系统下的磁盘操作过程步骤:一、了解麒麟操作系统的磁盘工具;二、如何进行磁盘分区;三、磁盘格式化的步骤;四、注意事项与推荐实践。在麒麟操作系统中进行磁盘分区和格式化是系统管理的基本操作。一、了解麒麟操作系统的磁盘工具麒麟操......
  • 如何恢复U盘里格式化数据?别慌,有带图详细步骤!
    U盘,这个小巧的存储神器,我们几乎天天都在用。但有时候,一不小心手滑,U盘就被格式化了,里面的东西好像全没了,别急,其实数据恢复没那么难。这篇文章就来告诉你,怎么把格式化的U盘里的数据找回来。先来个小科普:格式化U盘,其实就是把U盘里的文件目录清空了,让电脑认不出里面的文件,只要之后......