asp 强制转换浮点数值_C/C++中浮点数的编码存储

浮点数也称做实型数据(实数),形式上就是数学中的小数。浮点型数据有两种表达方式: 一种是用数字和小数点表示的,如123.456; 另一种是用指数方式表示,如1.2e-6 或1.2E-6(1.2*10-6)。

在计算机中实数是如何存储的呢?主要分为定点实数存储方式和浮点实数存储方式这两种。所谓定点实数,就是约定整数位和小数位的长度,比如用4字节存储实数,我们可以让高两个字节存放整数部分,低两个字节存储小数部分。这样的好处是计算的效率高,缺点是如果我们想存储65536.5,由于整数的表达范围超过了两个字节,用定点存储的方式就无法存储了。

对应地,也有浮点实数存储方式,就是用一部分二进制位存放小数点的位置信息,我们可以称之为”指数域”,其他的数据位用来存储没有小数点的数据和符号,我们可以称之为“数据域”、“符号域”。在访问时取得指数域,与数据域运算后得到真值,如67.625,利用浮点实数存储方式,数据域可以记录为67625,小数点的位置可以记录为10的-3次方。后来引进了浮点协处理器(FPU),专门负责对浮点数的处理,使得对处理实数的效率大大提高,于是浮点实数存储方式也就普及开来,成为了现在主流的实数存储方式。

在C/C++中,使用浮点方式存储实数,用两种数据类型来保存浮点数:float(单精度)和double(双精度)。Float在内存中占用4字节空间,double在内存中占用8个字节空间。Double类型比float类型精度更高。这两种数据在内存中都是以十六进制方式存储,但与整型数据有所不同。

整型数据是将十进制直接转换成二进制保存在内存中,以十六进制方式显示。而浮点类型不是将一个浮点小数直接转换成二进制保存,而是将浮点小数转换成二进制码 后 重新编码,再进行存储。C/c++的浮点数是有符号的。

在C/C++中,将浮点数强制转换成整数时,不会采用数学上的四舍五入方式,而是舍弃掉小数部分。

浮点数的操作不会用到通用寄存器,而是用浮点协处理器的浮点寄存器。

浮点数的编码方式

浮点编码转换采用的是IEEE规定的编码标准,float和double这两种类型数据的转换原理相同,但由于表示的范围不一样,编码方式有些区别。IEEE规定的浮点数编码会将一个浮点数转换为二进制数。以科学计数法划分,将浮点数拆分为3个部分:符号、指数和尾数。

1、float类型的IEEE编码

Float类型在内存中占4个字节(32位)。最高位表示符号:在剩余的31位中,从右到左取8位 用于表示指数,其余用于表示尾数。如图2-2所示:

472dfd48d6af1ab3e7efa1ca28116bea.png

float类型的IEEE编码

1)在进行二进制转换前,需要对单精度(float)浮点数进行科学计数法转换。例如,将float类型的12.25f(f表示为float单精度类型)转换为IEEE编码,需要将12.25f转换成对应的二进制数1100.01,整数部分为1100,小数部分为01。小数点向左移动,每移动1次指数加1,移动到除了符号位的最高位1处,停止移动。这里移动3次。对12.25f进行科学记数法转换后的二进制部分为1.10001,指数部分为3。在IEEE编码中,由于在二进制情况下,最高位始终为1,为一个恒定值,故将其忽略不计。这里是一个正数,所以符号位添0。

12.25f经过IEEE转换后各位的情况:

符号位:0

指数为:十进制 3+127,转换为二进制10000010

尾数位:10001 000000000000000000(当不足23位时,低位补0填充)

由于尾数位中最高位1是恒定值,故省略不计,只要在转换回十进制时加1即可。为什么指数位要加127呢?由于指数可能出现负数,十进制127 可表示二进制数 01111111。IEEE编码方式规定,当指数域小于0111111时为一个负数,反之为正数,因此 指数域加上十进制数 127 表示正数。

12.25f转换后的IEEE编码按二进制拼接为 0 10000010 10001000000000000000000。转换后成十六进制数 0x41440000,内存中以小端进行存储,故为 00 00 44 41。分析结果如图所示:

84e8a8ba8c347bb2072ca7515faf7e14.png

2)上面演示了符号位为正,指数为也为正的情况。那么什么情况下指数位可以为负呢?根据科学记数法,小数点向整数部分移动时,指数做加法。相反,小数点向小数部分移动时,指数需要以0起始做减法。浮点数 -0.125f转换成IEEE编码后,将会是一个符号位为1,指数部分为负的小数。-0.125f经转换后二进制部分为0.001,用科学记数法为1.0,指数为-3。

-0.125f 经过IEEE转码后各位的情况为:

符号位:1

指数位:十进制127+(-3),转换为二进制是 01111100,如果不足8位,则高位补0

尾数位:0000000000000000000000000

-0.125f经转换后的IEEE编码二进制拼接为 1 01111100 0000000000000000000000000。转换后成十六进制为 0xBE000000,内存中显示为 00 00 00 BE。分析结果如图所示:

b5adba01f127ee8a8827cab3fce5caeb.png

3)上面的两个浮点小数部分转换为二进制时都是有穷的,如果小数部分转换为二进制时得到一个无穷值,则会根据尾数部分的长度舍弃多余的部分。单精度浮点数1.3f,小数部分转换为二进制就会产生无穷值,依次转换为0.3、0.6、1.2、0.4、0.8、1.6、1.2、0.4、0.8...,转换后得到的二进制数位1.01001100110011001100110,到第23为时终止,尾数部分无法再保存。

1.3f经过IEEE转换后各位的情况:

符号位:0

指数位:十进制0+127,转换二进制01111111

尾数位:01001100110011001100110

1.3f 转换后的IEEE编码二进制拼接为 0 01111111 01001100110011001100110。转换成十六进制数位 0x3fa66666,内存中显示为 66 66 a6 3f。由于在转换二进制过程中产生了无穷值,舍弃了部分位数,所以进行IEEE编码转换后得到的是一个近似值,存在一定的误差。再将这个IEEE编码值转换成十进制小数,得到的值为1.2516582,四舍五入后为1.3.这也解释了为什么C++ 在比较浮点数值是否为0时,要做一个区间而不是直接进行等值比较。如:

float fTemp = 0.0001f; // 精确范围

if (fFloat >= -fTemp && fFloat <= fTemp)

{

fTemp等于0

}

2.double类型的IEEE编码

前文讲解了单精度浮点类型的IEEE编码。Double类型和float类型大同小异,只是double类型表示的范围更大,占用空间更多,精度更准。

Double 类型占8字节的内存空间,同样最高位也用于表示符号,指数位占11位,剩余的52位用于表示尾数。

在float中,指数位范围用8位表示,加127后用于判断指数符号。在double中,由于扩大了精度,因此指数范围使用11位正数来表示,加上1023来用于指数符号判断。

Double 类型的IEEE编码转换过程和float一样。

3.浮点数指令

浮点数的操作指令和普通数据类型不同,浮点数操作是通过浮点寄存器来实现的,而普通数据使用的是通用寄存器,如eax、edx、ebx等。

浮点寄存器是通过栈结构来实现的,由ST(0)~ST(7)共8个栈空间组成,每个浮点寄存器占8个字节。每次使用浮点寄存器都是先使用St(0),而不能越过ST(0)直接使用ST(1)。浮点寄存器的使用就是压栈、出栈的过程。当ST(0)存在数据时,执行压栈操作,ST(0)中的数据将进入到ST(1)中,如无出栈操作,将顺序地向下压栈,直到将浮点寄存器占满。常用浮点数指令如下所示:IN 表示操作数 入栈。OUT表示操作数出栈。

09505f217e3013fe61998eaa60eed636.png

常用浮点数指令

其他运算指令和普通指令类似,只需在前面加F就行,如 FSUB和FSUBP等。

在使用浮点指令时,都要先利 用ST(0)进行运算。当ST(0)中有值时,便会将ST(0)中的数据顺序向下存放到ST(1)中,然后再将数据放入ST(0)中。如果再次操作ST(0),则会先将ST(1)中的数据放入ST(2)中,然后将ST(0)中的数据放入到ST(1)中,最后才将新的数据存放到ST(0)。以此类推,在八个浮点寄存器都有值的情况下继续向ST(0)存放数据,这时会丢弃ST(7)中的数据信息。

1)下面通过一个简单的例子来了解各个指令的使用流程:

// 浮点数使用

float fFloat = (float)argc;

00401028 fild dword ptr [ebp+8]

//将ebp+8处的整型数据转换成浮点型,并放入ST(0)中,对应变量 argc

0040102B fst dword ptr [ebp-4]

//从ST(0) 中取出数据以浮点编码的方式放入地址ebp-4 中,对应变量 fFloat

printf("%f", fFloat);

0040102E sub esp,8

//这里对esp减 8 操作是由于浮点数作为变参函数的参数时需要转换成双精度浮点值,

//这步操作是 提前准备8字节的栈空间,以便存放double数据。

00401031 fstp qword ptr [esp]

//将ST(0) 中的数据传入esp中,并弹出ST(0)。

00401034 push offset string "%f" (00426020)

00401039 call printf (00401420)

0040103E add esp,0Ch

argc = (int)fFloat;

//将 float类型数据转换成int型

00401041 fld dword ptr [ebp-4]

//将ebp-4处的数据以浮点型压入ST(0)中。

00401044 call __ftol (00401588)

//调用函数 __ftol 进行浮点数转换, __ftol的实现见下文。

00401049 mov dword ptr [ebp+8],eax

printf("%d", argc);

0040104C mov eax,dword ptr [ebp+8]

0040104F push eax

00401050 push offset string "%d" (0042601c)

00401055 call printf (00401420)

0040105A add esp,8

从上面示例中可以发现,float类型的浮点数虽然占4个字节,但都是以8个字节(qword)方式进行处理。当浮点数作为参数时,并不能直接压栈。Push 指令只能传入4字节数据到栈中,这样会丢失4字节数据。这就是为什么使用printf函数以整型方式输出浮点数会产生错误的原因。Printf以整数方式输出时,将对应参数作为4字节数据,按补码方式解释。而真正压入的参数为浮点类型时,数据长度为8字节,需要按浮点编码解释。

2)浮点数作为返回值的情况也是如此,同样需要传递8字节数据,代码如下所示:

float fFloat;

fFloat = GetFloat();

00401058 call @ILT+5(_GetFloat) (0040100a)

//调用GetFloat函数

0040105D fst dword ptr [ebp-4]

//由于浮点数需要特殊处理,浮点数占8个字节,无法使用EAX进行传递

//因此使用 浮点寄存器 ST(0) 作为返回值

printf("%f", fFloat);

00401060 sub esp,8

00401063 fstp qword ptr [esp]

00401066 push offset string "%f" (00426020)

0040106B call printf (00401420)

00401070 add esp,0Ch

//GetFloat 函数

float GetFloat()

{

00401010 push ebp

00401011 mov ebp,esp

00401013 sub esp,40h

00401016 push ebx

00401017 push esi

00401018 push edi

00401019 lea edi,[ebp-40h]

0040101C mov ecx,10h

00401021 mov eax,0CCCCCCCCh

00401026 rep stos dword ptr [edi]

return 12.25f;

00401028 fld dword ptr [string "%d" (0042601c)]

//将浮点数保存在 ST(0)中,在返回值为浮点数的情况下,无法使用EAX

//使用ST(0)作为返回值进行传递。

}

0040102E pop edi

0040102F pop esi

00401030 pop ebx

00401031 mov esp,ebp

00401033 pop ebp

00401034 ret

3)在上面代码中,float型数据被强制转换为int型,编译器通过了__ftol函数实现了转换过程,如下面所示:

__ftol:

00401588 push ebp

00401589 mov ebp,esp

0040158B add esp,0FFFFFFF4h

//保存环境,预留语句变量空间

0040158E wait

0040158F fnstcw word ptr [ebp-2]

00401592 wait

00401593 mov ax,word ptr [ebp-2]

00401597 or ah,0Ch

0040159A mov word ptr [ebp-4],ax

0040159E fldcw word ptr [ebp-4]

//浮点异常检查、CPU与FPU的同步工作

004015A1 fistp qword ptr [ebp-0Ch]

//从ST(0)中取出8字节数据转换成整型并存入到ebp-0ch中

//从ST(0)中弹出

004015A4 fldcw word ptr [ebp-2]

004015A7 mov eax,dword ptr [ebp-0Ch]

//使用eax保存整型数据的低4字节,用于返回

004015AA mov edx,dword ptr [ebp-8]

//使用edx保存整型数据的高4字节,用于返回

004015AD leave

//释放栈空间

004015AE ret

004015AF int 3

————————摘自《C++反汇编与逆向分析技术揭秘》

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

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

相关文章

PaddleNLP实战——信息抽取(InfoExtraction)

[ 文章目录 ]1. 信息抽取任务是什么&#xff1f;2. 基于PaddleNLP的信息抽取任务2.1 训练任务概览2.2 Predicate列表2.3 SPO列表2.4 代码解析1. 信息抽取任务是什么&#xff1f; 在NLP任务中&#xff0c;通常当我们拿到一段文本时&#xff0c;我们希望机器去理解这段文本描述的…

ThinkPad X220i 刷白名单BIOS,改装第三方无线网卡

ThinkPad X220i自带的网卡是REALTEK RTL8188CE&#xff0c;这张卡在Mac下目前是无解的.国外网站有该卡liunx、unix内核的驱动&#xff0c;但还是没有高人编译出来. 不等了,这卡没戏.正好手边有一台Dell E6400,E6400的无线网卡是DELL Wireless 1397 WLAN Mini-Card,具体型号是&a…

C# 离线人脸识别 ArcSoft

人脸识别&比对发展到今天&#xff0c;已经是一个非常成熟的技术了&#xff0c;而且应用在生活的方方面面&#xff0c;比如手机、车站、天网等。虹软人脸识别服务是免费的。最重要的是它还支持离线识别&#xff0c;并且提供Android、iOS、C、C#版SDK&#xff0c;现在已经升级…

【mongoDB运维篇③】replication set复制集

介绍 replicattion set 多台服务器维护相同的数据副本,提高服务器的可用性,总结下来有以下好处: 数据备份与恢复读写分离MongoDB 复制集的结构以及基本概念 正如上图所示&#xff0c;MongoDB 复制集的架构中&#xff0c;主要分为两部分&#xff1a;主节点&#xff08;Primary&a…

c++ long 转 short_C精品编程之——C语言的数据类型、运算符、表达式,精品课程...

在前边的文章分享中&#xff0c;我们已经看到程序中使用的各种变量都应预先加以说明&#xff0c;即先说明&#xff0c;后使用。对变量的说明可以包括三个方面&#xff1a;数据类型存储类型作用域在本课中&#xff0c;我们只介绍数据类型说明。其它说明在以后各章中陆续介绍。所…

李宏毅Reinforcement Learning强化学习入门笔记

文章目录Concepts in Reinforcement LearningDifficulties in RLA3C Method Brief IntroductionPolicy-based Approach - Learn an Actor (Policy Gradient Method)1. Decide Function of Actor Model (NN? ...)2. Decide Goodness of this Function3. Choose the best functi…

《BI项目笔记》数据源视图设置

原文:《BI项目笔记》数据源视图设置目的数据源视图是物理源数据库和分析维度与多维数据集之间的逻辑数据模型。在创建数据源视图时&#xff0c;需要在源数据库中指定包含创建维度和多维数据集所需要的数据表格和视图。BIDS与数据库连接&#xff0c;读取表格和视图定义&#xff…

201521123070 《JAVA程序设计》第13周学习总结

1. 本章学习总结 以你喜欢的方式&#xff08;思维导图、OneNote或其他&#xff09;归纳总结多网络相关内容。 2. 书面作业 Q1. 网络基础 1.1 比较ping www.baidu.com与ping cec.jmu.edu.cn&#xff0c;分析返回结果有何不同&#xff1f;为什么会有这样的不同&#xff1f; 1.2 t…

.NET 7 预览版2 的亮点之 NativeAOT 回顾

.NET 中备受追捧和期待已久的功能NativeAOT终于出现在本周的.NET 7 预览版2中&#xff0c;该项目的工作仍在继续&#xff0c;该版本将 NativeAOT 从实验性的 dotnet/runtimelab repo 中移出合并进入稳定的运行时库 dotnet/runtime repo&#xff0c;但尚未在 dotnet SDK 中添加足…

c语言十佳运动员有奖评选系统_2019年沃德十佳内饰解读

​2019年沃德十佳内饰解读​mp.weixin.qq.com在这个世界上&#xff0c;要判定一件事物的成功与否并不容易&#xff0c;仅靠主观判断远远不够&#xff0c;而是需要能够进行量化判断的标准和成果。正如运动员需要金牌和冠军积淀&#xff0c;导演和演员需要奖项傍身一样&#xff0…

Mybatis——返回类型为 集合嵌套集合 应该如何处理

2019独角兽企业重金招聘Python工程师标准>>> 最近在练习时 遇到了类似于 企鹅里的好友分组功能&#xff0c;使用的持久层框架是mybatis 第一次处理这种关系 记录一下 备忘。。 首先是表结构&#xff1a; <user_group > 好友分组 、 <t_group> 用户与好友…

为什么用 windbg 看 !address 显示出的Free是128T 大小?

总是被朋友问&#xff0c;用 !address -summary 显示出上面的 Free ≈ 128T 到底是什么意思&#xff1f;我的空闲内存不可能有这么大,不管怎么说&#xff0c;先上命令。0:009> !address -summary--- Usage Summary ---------------- RgnCount ----------- Total Size ------…

DeepMind 的马尔可夫决策过程(MDP)课堂笔记

DeepMind Teaching by David Silver 视频课程链接&#xff08;Youtube资源&#xff0c;需梯子&#xff09;&#xff1a;https://youtu.be/lfHX2hHRMVQ 文章目录DeepMind Teaching by David Silver1. 马尔可夫过程&#xff08;Markov Processes&#xff09;2. 马尔可夫回报过程…

深入Java集合学习系列:ConcurrentHashMap之实现细节

http://www.iteye.com/topic/344876 ConcurrentHashMap是Java 5中支持高并发、高吞吐量的线程安全HashMap实现。在这之前我对ConcurrentHashMap只有一些肤浅的理解&#xff0c;仅知道它采用了多个锁&#xff0c;大概也足够了。但是在经过一次惨痛的面试经历之后&#xff0c;我觉…

基于小波变换的信号降噪处理及仿真研究_信号处理方法推荐--1(转载自用,侵删)...

综述作者&#xff1a;aresmiki链接&#xff1a;https://www.zhihu.com/question/23701194/answer/167005497来源&#xff1a;知乎著作权归作者所有。商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处。非平稳信号处理应该是现在信号处理技术最新的也是最热的研究方…

js温故而知新11(AJAX)——学习廖雪峰的js教程

Asynchronous JavaScript and XML&#xff0c;意思就是用JavaScript执行异步网络请求。 如果仔细观察一个Form的提交&#xff0c;你就会发现&#xff0c;一旦用户点击“Submit”按钮&#xff0c;表单开始提交&#xff0c;浏览器就会刷新页面&#xff0c;然后在新页面里告诉你操…

最流行的 .NET 开源项目合集

Github 上有很多优秀的 .NET 开源项目&#xff0c;它们很适合用来学习 .NET 、设计模式和架构。nopCommerce https://github.com/nopSolutions/nopCommercestar: 7k ⭐nopCommerce 是最受欢迎最好的开源电子商务购物车解决方案之一&#xff0c;它基于 ASP.NET Core&#xff…

GFS(Genetic Fuzzy Systems)—— 基于专家先验规则库和遗传算法相结合的智能体决策算法

文章目录1. FIS 系统&#xff08;Fuzzy Inference Systems&#xff09;1.1 什么是 FIS 系统&#xff1f;1.2 使用 FIS 算法的几个步骤2. GFS 系统&#xff08;GA FIS&#xff09;2.1 什么是基因遗传算法&#xff08;GA&#xff09;?2.2 使用GA算法进化FIS规则库在大规模的多智…

3-unit1 IPv6网络的管理

##########IPv6网络的管理#############学习目标了解IPv6管理IPv6##)IPv6简介Internet Protocol Version 6IPv6是IETF&#xff08;和互联网工程任务组&#xff09;设计的用与替代现行版本IP协议的下一代IP协议。IPv6采用128位2进制数码表示##IPv6示意图:##IPv6表示方式为方便操…

Xamarin效果第一篇之时间轴

一直都想找个时间玩玩移动端,中间也去各种的调研快速的方式去开发;过程中还是太浮躁木有沉下心去学习;好早尝试过Flutter,二点让我不爽:1、配置环境好费劲(VS把我惯坏了)&#xff1b;2、套娃的写法真是恶心;来看看酸爽不:因为一直都是C#开发,所以这次再次摸索Xamarin去开发;前面…