为什么truncate函数(四舍五入)是x+0.4999997而不是+0.5?

news2024/11/25 23:33:38

目录

float的不精确表示

+0.5的舍入方法

该方法的漏洞

0.4999997f舍入的结果错误

以+0.4999997f改进舍入方法

可以用0.49999996、0.49999998或者0.49999999替换0.49999997吗?


在做舍入函数研究时,发现函数中实现四舍五入的trunc函数大概采用的逻辑是floor(x+0.4999997),而不是floor(x+0.5),下面对这个数值进行研究说明。

float的不精确表示

我们都知道二进制在有限的位上表示浮点值并不是每个都精确的,就像在有限的位数上,十进制不能准确地表示1/3。二进制在实数上能表示的数据分布大概规律如下:

越接近0的值表示得越精确,越大的数表示得越不精确,这是前提。

+0.5的舍入方法

舍入函数中truncate实现数学上的四舍五入,广泛被接受的方法是将x+0.5后并向下舍入(向下舍入对于非负数,小数置0;对于负数,小数置0后减1),这种做法对计算机来说,比判断x是否大于等于【小于等于x的最大整数与大于等于x的最大整数的中值】这个判断要简单得多。

该方法的漏洞

因为浮点表示不精确的特性,当2^22 < |x|时,单精度float浮点的IEEE 745(目前计算机普遍使用的标识浮点的格式)能表示的只有整数,即两个可表示的浮点之间间隔最小为1,并且间隔会随x的增大而增大。

在2^22 < |x| < 2^23时,间隔正好为1。

此时,假设有float值x=2^22+1.0,对其进行四舍五入的舍入预期值为x本身,但实际舍入会执行floor(x+0.5),但x+0.5这个值并不能用float精确表示,系统会对其进行舍入后再表示,此处的舍入是中值向偶数舍入的就近舍入,x+0.5正好是2^22+1.0与2^22+2.0的中值,向偶舍入则会舍入为2^22+2.0,对2^22+2.0执行floor依然是2^22+2.0,得到的结果与预期值不符。这种错误的结果会在2^22 < |x| < 2^23范围内所有的奇数值上发生(但对于更大绝对值的x则不会发生,因为最小间隔大于1,任何数字+0.5后依然达不到与上一个可精确表示数字的中值,所以x+0.5被实际表示为x,floor(x)=x,结果正确)。

0.4999997f舍入的结果错误

如何处理这个范围内的错误结果呢?

让我们考虑一个特殊的值x=0.4999997f,对x执行truncate,发现结果=1.0而不是0.0,但x明显更接近1.0。

这个错误同样是因为浮点表示不精确引起的,float以 0.4999999701976776123046875来不精确地表示0.4999997:

当这个值加上0.5时,数学上是0.9999999701976776123046875,但float同样不能精确地表示这个数,它能精确表示的下一个数是:

能精确表示的上一个数是1:

这个值正好是两者的中值:(0.9999999701976776123046875+1)/2=0.4999999701976776123046875

根据向偶舍入的就近舍入原则,系统以1.0来不精确地表示这个数:

所以就是floor(1.0)结果为1.0。

你可能会想,为什么float能精确表示0.4999999701976776123046875却不能精确表示0.9999999701976776123046875呢?

因为上面提到的,越靠近0表达地越精确。

为什么0.9999999701976776123046875正好落在能精确表达的两个数的中间呢?

因为float能精确表示的数是有规律的,假设0.49999997附近的最小间隔为a,比0.49999997大的数间隔会慢慢变成2a,4a……,0.99999997附近的最小间隔就是2a,效果如下: 

所以对0.4999997执行四舍五入的结果为1.0。

以+0.4999997f改进舍入方法

这个数字却可以用来避免+0.5的舍入方法的漏洞,因为当2^22 < |x| < 2^23时,x+0.49999997达不到与上一个可精确表示数的中值(即x+0.5),所以x+0.49999997会就近舍入到x,floor(x)=x,结果正确,不会有+0.5的漏洞。

可以用0.49999996、0.49999998或者0.49999999替换0.49999997吗?

首先,0.49999996与0.49999998在float中的表示与0.49999997一样,所以在表示这一步就与0.49999997一样了,即可以用96与98替代97的。而0.49999999表示为0.5,显然会存在和+0.5的舍入方式一样的漏洞。

参考:

Harder than it looks: rounding float to nearest integer, part 1

IEEE-754 Floating Point Converter

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

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

相关文章

LVS负载均衡-----DR模式

目录 1.DR模式原理 2.DR 模式的特点&#xff1a; 3.LVS-DR中的ARP问题 问题原因&#xff1a; 解决方法&#xff1a; 问题二&#xff1a;返回报文时源地址使用VIP&#xff0c;导致网关设备的ARP缓存表紊乱 解决方法&#xff1a; 4.DR模式 LVS负载均衡群集部署 1.配置负…

python进程线程问题

参考链接&#xff1a; 代码可去原博文复制&#xff1a; python缩短大量数据处理时间-进程池pool()和map() 一 定义与解释 1、对于python代码&#xff0c;多线程其实是个假的&#xff0c;因为每次计算的时候&#xff0c;实质上只有一个线程计算。使用多线程时&#xff0c;是几…

【天池题解】题解:CAAI-BDSC2023社交图谱链接预测(任务一:社交图谱小样本场景链接预测)

【天池题解】题解&#xff1a;CAAI-BDSC2023社交图谱链接预测&#xff08;任务一&#xff1a;社交图谱小样本场景链接预测&#xff09; 文章目录 【天池题解】题解&#xff1a;CAAI-BDSC2023社交图谱链接预测&#xff08;任务一&#xff1a;社交图谱小样本场景链接预测&#xf…

中介者模式的学习与使用

1、中介者模式的学习 中介者模式是一种行为型设计模式&#xff0c;它通过引入一个中介者对象来解耦一组对象之间的交互。中介者模式促进了对象之间的松耦合&#xff0c;使得它们可以独立地进行通信&#xff0c;而不需要直接相互引用。   在中介者模式中&#xff0c;有以下几个…

获取文件大小并转换单位

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

【工具使用】Notepad++如何使用正则表达式同时搜索多个字符串

一&#xff0c;简介 在调试代码定位问题的时候&#xff0c;由于log打印比较多&#xff0c;故需要使用NotePad文本编辑器进行搜索&#xff0c;并且需要同时搜索多个字符串&#xff0c;本文介绍如何同时搜索多个字符串&#xff0c;供参考。 二&#xff0c;操作步骤 2.1 搜索设…

90、基于STM32单片机数字频率计频率检测配NE555脉冲发生器设计(程序+原理图+PCB源文件+参考论文+硬件设计资料+元器件清单等)

单片机主芯片选择方案 方案一&#xff1a;AT89C51是美国ATMEL公司生产的低电压&#xff0c;高性能CMOS型8位单片机&#xff0c;器件采用ATMEL公司的高密度、非易失性存储技术生产&#xff0c;兼容标准MCS-51指令系统&#xff0c;片内置通用8位中央处理器(CPU)和Flash存储单元&a…

如何基于 ESP-IDF SDK 为应用工程添加自定义的 menuconfig 编译条件设置?

在工程的 main 目录下添加 Kconfig.projbuild 文件对需要添加自定义设置的参数进行定义在应用代码中使用自定义设置的参数编写 Kconfig.projbuild 文件 在工程的 main 目录下添加 Kconfig.projbuild 文件 接下来会基于 esp-idf/examples/peripherals/uart/uart_async_rxtxtask…

Python黑魔法揭秘:超强公共操作符和推导式的编程技巧

文章目录 前言公共操作1.运算符1&#xff09; 合并操作2&#xff09;* 复制操作in / not in 判断数据在 / 不在容器中 公共方法len()del / del()max()min()range()enumerate() 容器类型转换1&#xff09;list() 将某个序列转化为列表tuple() 将某个序列转换为元组set() 将某个序…

OpenCloudOS社区开源,助力软件开发

早前红帽宣布限制源代码访问性的政策&#xff0c;并解释说RHEL相关源码仅通过CentOS Stream公开&#xff0c;付费客户和合作伙伴可通过Red Hat Customer Portal访问到源代码&#xff0c;由此也导致非客户获取源码越来越麻烦&#xff0c; 据了解&#xff0c;CentOS是红帽发行的…

效率为王!项目管理软件如何帮你提高效率?

对于任何商业组织及其客户来说&#xff0c;低效率的员工和浪费的时间都是一个问题。团队成员懈怠会对团队的整体效率产生负面影响。除了巨大的财务挫折之外&#xff0c;这些问题的混合还导致多次延误、工作质量差和客户不满意。 但有了正确的项目管理软件&#xff0c;企业就可…

Netty各组件执行流程及原理

目录 一、netty执行流程 二、EventLoop 1、事件循环对象EventLoop 2、事件循环对象组EventLoopGroup 3、io任务 4、分工细化 5、handler执行中如何换的group 三、Channel 1、常用方法 2、channelFuture 3、为什么要异步 四、Future & Promise 1、jdk future …

Linux13.文件(上)

1.文件 内容 属性。 2.在程序中创建一个文件&#xff0c;文件是进程创建的(跑起来才有)&#xff0c;默认路径是进程的工作路径。 3. C语言中字符串以\0结尾&#xff0c;文件不是。 4.指令 >文件 :清空文件。 5.理解cat指令的实现方式。 6.使用位操作传递宏标记位 7.操…

C语言经典案例-2

今天继续给大家分享C语言学习的经典练手案例 记录在校学习第三天&#xff0c;继续加油 今日案例来源&#xff1a;csdn社区每日一练 1.商品优惠计算机 商品优惠计算器 使用if语句编程实现输入购货金额&#xff0c;输出实际付款金额。购货折扣率如下&#xff1a; 购货金额≤500元…

skywalking linux安装部署

SkyWalking APM tar 下载 结合自己的es版本下载对应的tar 地址&#xff1a;https://archive.apache.org/dist/skywalking/ 由于我使用的是es7所以下载对应版本 拷贝对应链接使用wget下载 wget https://archive.apache.org/dist/skywalking/8.7.0/apache-skywalking-apm-es7…

Neon入门以及常用函数

什么是neon&#xff0c; neon是arm架构下的一个simd的一种方案&#xff0c; 本质上是一些可以用于simd的寄存器 具体参考官方的图&#xff1a; 官方的图说明对于arm开发板&#xff0c; 有16个128bit的寄存器&#xff0c;或者也可以看作是32个64bit的寄存器 列一下常用的neon…

从零开发短视频电商 使用nimbus-jose-jwt进行对称签名和非对称签名的JWT实现

文章目录 什么是JSON Web Token何时使用JSON Web TokenJSON Web Token的结构是什么头部&#xff08;Header&#xff09;负载&#xff08;Payload&#xff09;签名&#xff08;Signature&#xff09;拼接起来 如何使用JSON Web Token工具库依赖流程对称签名非对称签名 总结 JWT的…

超实用攻略!GPT能玩的这么6,你居然还不知道?

开篇 自古以来,智者皆知学无止境,而在我们身边,正有一款奠基于这个原则的AI机器人—ChatGPT,他擅长从网络上学习各种知识,然后把这些知识用在他的对话中。没错,它就是天马行空的闲话家,无所不谈的取经者。可你知道怎样让它更加符合你的使用需求,适应你的工作节奏么?哦…

Qt Quick系列(9)—初识画布

&#x1f680;作者&#xff1a;CAccept &#x1f382;专栏&#xff1a;Qt Quick 文章目录 前言代码示例1源码关键知识点 代码示例2源码关键知识点 总结 前言 画布元素的基本思想是使用上下文2D对象&#xff08;ctx&#xff09;渲染路径。上下文2D对象包含必要的图形功能&…

一文读懂FPC(12)- FPC的阻抗控制

FPC系列文章目录 1.什么是FPC 2.什么是R-FPC 3&#xff0c;FPC的基材 4.FPC基材压延铜和电解铜的区别 5&#xff0c;FPC的辅材 6&#xff0c;FPC常见的四种类型 7&#xff0c;FPC的生产流程简介 8&#xff0c;R-FPC的生产流程简介 9&#xff0c;FPC的发展及应用 10&a…