【ICS】CS:APP3e Homework 2.97 关于整型转单精度浮点数的方法讨论

2024-03-01 20:38

本文主要是介绍【ICS】CS:APP3e Homework 2.97 关于整型转单精度浮点数的方法讨论,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

CS:APP3e Homework 2.97 关于整型转单精度浮点数的方法讨论

  • 关于整型转单精度浮点数的方法讨论
    • CS:APP原题
    • 题目分析
    • 实现代码
    • 原理解释
    • 其他方法
    • 启示与思考

关于整型转单精度浮点数的方法讨论

CS:APP即著名的计算机系统书籍《Computer Systems: A Programmer’s Perspective》(《深入理解计算机系统》),本篇博客基于其第三版第二章课后题2.97,讨论基于移位操作的整型转单精度浮点数底层实现,提供了笔者自己的原创代码以及网络上一些现有的实现代码。

CS:APP原题

2.97 遵循位级浮点编码规则, 实现具有如下原型的函数:
/*Compute (float)i */
float_bits float_i2f(int i);
对于整数i,这个函数计算(float) i 的位级表示。
测试你的函数,对参数f可以取的所有223个值求值,将结果与你使用机器的浮点运算得到的结果相比较。

题目分析

作为该章课后作业的最后一题,以及书中标注的四星难度,这个题还是需要花费一些时间才能做出来的。题目要求读者自己通过一些逻辑、位运算这样较为底层的操作实现C语言中从整型到单精度浮点型类型转换,而不能使用浮点数据类型、运算或者常数。其他限制这里不再列举,具体参照原书。
C语言中从整型至单精度浮点数的强制类型转换,并非像无符号数和补码转换位不变,直接映射,而是转换成对应的值。
所以,对于两者的转换,我们能够采用的方法就是根据IEEE754标准中浮点数的构成原则与整数补码的位关系找到规律,从而进行映射。
关于IEEE754标准及补码的二进制知识,本文不再赘述。

实现代码

这里首先给出笔者经过一段时间研究与测试得到的最终代码。

#include <stdio.h>typedef unsigned float_bits;/* Compute (float)i */
float_bits float_i2f(int i);
float u2f(unsigned x);int main()
{int i = 0;printf("If you want to stop, just type 0.\n");do{printf("Please input an integer number:");scanf("%d",&i);printf("%g\t%g\n",(float)i,u2f(float_i2f(i)));}while(i!=0);return 0;
}float_bits float_i2f(int i)
{if (i == 0)return 0;/*因为无符号数和浮点数均可用0x00000000表示0,所以当整数i为0时可直接返回*/unsigned s = i>>31<<31;/*通过左移和右移使除符号位外均为0,以便最后的或操作*/if ((s>>31) == 1)i = ~(i-1);/*若输入的整数为负数,只需要单独讨论其符号位。因为对于浮点数来讲相反数的其它位是相同的,所以我们先把其按照补码规则转化为其相反数(正数),然后就可以和正数同样处理,最后通过或操作可以保证其符号位*/unsigned m_bits = 0;//补码除去第一位后的有效位数(原因参照浮点数规格化数的表示)unsigned e;//阶码unsigned m = (unsigned)i;//未经处理的尾数unsigned isLow = 0;//确定尾数长短位置的Flagint j = 0;for (j = 0; j<32; j++){if (i>>(31-j)){m_bits = 32-j;break;}}/*确定补码的有效位数,通过循环移位,直到移位后结果不是0,从而确定其有效长度,忽略前面的0位。如果是负数,因为之前我们已经将其最高位通过转为为对应相反数,故不会因为最高位而影响有效位数。 */m_bits--;m = m<<(32-m_bits)>>(32-m_bits);/*根据规格化数构成方法排除第一位影响*/e = (m_bits+127)<<23;/*根据规格化数构成方法确定阶码*/if (m_bits <23)isLow = 1;if (isLow)m = m <<(23-m_bits);elsem = m >>(m_bits-23);/*对于单精度浮点数,符号位1位,阶码位8位,尾数位23位,以此作为参照选择左移右移,得到最终正确位置的尾数*/return s|e|m;/*通过或操作,合并符号位、阶码、尾数*/
}float u2f(unsigned x)
{return *(float*)&x;/*位不变的将无符号数转化为对应单精度浮点数*/
}

原理解释

其实这个题的原理,书中已经给出。
《深入理解计算机系统 第三版》P82
理解了这个,我们只需要把这个原理“翻译”成C语言代码。简单的说就是获取对应的尾数以及阶码,并注意一下对于正负的讨论。归根到底还是考察对IEEE754标准的理解。
具体的解释可以参照给出的代码注释。

其他方法

笔者在起初做题时尝试去参考网上一些方法,后来觉得尝试去理解其他人的方法不如自己试试能否实现。当然,笔者的代码中也有一些现成思路的影子。尽管还没有研究明白,但这里引用下网上其他的方法,供大家参考。
方法一:

#include <stdio.h>
#include <limits.h>typedef unsigned float_bits;float_bits float_i2f(int i);
unsigned bits_length(int x);
unsigned bits_mask(unsigned x);
float u2f(unsigned x);int main()
{int i = 123;printf("%f\t%f\n", (float)i, u2f(float_i2f(i)));i = -123;printf("%f\t%f\n", (float)i, u2f(float_i2f(i)));i = 0;printf("%f\t%f\n", (float)i, u2f(float_i2f(i)));i = (~0);printf("%f\t%f\n", (float)i, u2f(float_i2f(i)));i = (1 << 31);printf("%f\t%f\n", (float)i, u2f(float_i2f(i)));return 0;
}float_bits float_i2f(int i)
{unsigned sign, exp, frac, bias;bias = 127;if (i == 0) return 0;if (i == INT_MIN) { // -1sign = 1;exp = 31 + bias; frac = 0; // -1是整数,没有小数部分return sign << 31 | exp << 23 | frac;}sign = i > 0 ? 0 : 1;if (i < 0)i = -i;unsigned bits_num = bits_length(i);unsigned fbits_num = bits_num - 1;unsigned fbits;exp = bias + fbits_num;fbits = i & bits_mask(1 << fbits_num - 1);if (fbits_num <= 23)frac = fbits << (23 - fbits_num);else {unsigned offset = fbits_num - 23;frac = fbits >> offset;unsigned round_mid = 1 << (offset - 1);unsigned round_part = fbits & bits_mask(1 << offset - 1);if (round_part > round_mid)++frac;else if (round_part == round_mid) {if (frac & 0x1)++frac;}}return sign << 31 | exp << 23 | frac;
}unsigned bits_length(int x)
{unsigned ux = (unsigned) x;unsigned count = 0;while (ux > 0) {ux >>= 1;++count;} return count;
}unsigned bits_mask(unsigned x)
{x |= x >> 1;x |= x >> 2;x |= x >> 4;x |= x >> 8;x |= x >> 16;return x;
}float u2f(unsigned x)
{return *(float *)&x;
}

原文地址:https://www.cnblogs.com/chritran-dlay/p/9279184.html

方法二:

/** float-i2f.c*/
#include <stdio.h>
#include <assert.h>
#include <limits.h>
#include "float-i2f.h"/** Assume i > 0* calculate i's bit length** e.g.* 0x3 => 2* 0xFF => 8* 0x80 => 8*/
int bits_length(int i) {if ((i & INT_MIN) != 0) {return 32;}unsigned u = (unsigned)i;int length = 0;while (u >= (1<<length)) {length++;}return length;
}/** generate mask* 00000...(32-l) 11111....(l)** e.g.* 3  => 0x00000007* 16 => 0x0000FFFF*/
unsigned bits_mask(int l) {return (unsigned) -1 >> (32-l);
}/** Compute (float) i*/
float_bits float_i2f(int i) {unsigned sig, exp, frac, rest, exp_sig /* except sig */, round_part;unsigned bits, fbits;unsigned bias = 0x7F;if (i == 0) {sig = 0;exp = 0;frac = 0;return sig << 31 | exp << 23 | frac;}if (i == INT_MIN) {sig = 1;exp = bias + 31;frac = 0;return sig << 31 | exp << 23 | frac;}sig = 0;/* 2's complatation */if (i < 0) {sig = 1;i = -i;}bits = bits_length(i);fbits = bits - 1;exp = bias + fbits;rest = i & bits_mask(fbits);if (fbits <= 23) {frac = rest << (23 - fbits);exp_sig = exp << 23 | frac;} else {int offset = fbits - 23;int round_mid = 1 << (offset - 1);round_part = rest & bits_mask(offset);frac = rest >> offset;exp_sig = exp << 23 | frac;/* round to even */if (round_part < round_mid) {/* nothing */} else if (round_part > round_mid) {exp_sig += 1;} else {/* round_part == round_mid */if ((frac & 0x1) == 1) {/* round to even */exp_sig += 1;}}}return sig << 31 | exp_sig;
}

原文地址:https://dreamanddead.gitbooks.io/csapp-3e-solutions/chapter2/2.97.html

启示与思考

这段时间在学习计算机系统的相关知识,涉及到一些底层二进制实现原理,包括一些整数、浮点数编码与转换。本题就是一个典型的例子,涉及到许多相关知识。尽管笔者的代码很多地方写的还是比较幼稚,但是在一定程度上帮助笔者加深对于计算机底层编码实现的一些理解。
具体到这个题,我们要学会利用书中给出的原理,尝试去自己实现,并灵活运用各种位及逻辑操作,善作总结,这样有助于我们更好的理解书中内容。

这篇关于【ICS】CS:APP3e Homework 2.97 关于整型转单精度浮点数的方法讨论的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


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

相关文章

Nginx 413修改上传文件大小限制的方法详解

《Nginx413修改上传文件大小限制的方法详解》在使用Nginx作为Web服务器时,有时会遇到客户端尝试上传大文件时返回​​413RequestEntityTooLarge​​... 目录1. 理解 ​​413 Request Entity Too Large​​ 错误2. 修改 Nginx 配置2.1

使用@Cacheable注解Redis时Redis宕机或其他原因连不上继续调用原方法的解决方案

《使用@Cacheable注解Redis时Redis宕机或其他原因连不上继续调用原方法的解决方案》在SpringBoot应用中,我们经常使用​​@Cacheable​​注解来缓存数据,以提高应用的性能... 目录@Cacheable注解Redis时,Redis宕机或其他原因连不上,继续调用原方法的解决方案1

sql语句字段截取方法

《sql语句字段截取方法》在MySQL中,使用SUBSTRING函数可以实现字段截取,下面给大家分享sql语句字段截取方法,感兴趣的朋友一起看看吧... 目录sql语句字段截取sql 截取表中指定字段sql语句字段截取1、在mysql中,使用SUBSTRING函数可以实现字段截取。例如,要截取一个字符串字

JAVA数组中五种常见排序方法整理汇总

《JAVA数组中五种常见排序方法整理汇总》本文给大家分享五种常用的Java数组排序方法整理,每种方法结合示例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧... 目录前言:法一:Arrays.sort()法二:冒泡排序法三:选择排序法四:反转排序法五:直接插入排序前言:几种常用的Java数组排序

Python将字符串转换为小写字母的几种常用方法

《Python将字符串转换为小写字母的几种常用方法》:本文主要介绍Python中将字符串大写字母转小写的四种方法:lower()方法简洁高效,手动ASCII转换灵活可控,str.translate... 目录一、使用内置方法 lower()(最简单)二、手动遍历 + ASCII 码转换三、使用 str.tr

Python处理超大规模数据的4大方法详解

《Python处理超大规模数据的4大方法详解》在数据的奇妙世界里,数据量就像滚雪球一样,越变越大,从最初的GB级别的小数据堆,逐渐演变成TB级别的数据大山,所以本文我们就来看看Python处理... 目录1. Mars:数据处理界的 “变形金刚”2. Dask:分布式计算的 “指挥家”3. CuPy:GPU

Java中的StringUtils.isBlank()方法解读

《Java中的StringUtils.isBlank()方法解读》:本文主要介绍Java中的StringUtils.isBlank()方法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑... 目录所在库及依赖引入方法签名方法功能示例代码代码解释与其他方法的对比总结StringUtils.isBl

CentOS7增加Swap空间的两种方法

《CentOS7增加Swap空间的两种方法》当服务器物理内存不足时,增加Swap空间可以作为虚拟内存使用,帮助系统处理内存压力,本文给大家介绍了CentOS7增加Swap空间的两种方法:创建新的Swa... 目录在Centos 7上增加Swap空间的方法方法一:创建新的Swap文件(推荐)方法二:调整Sww

QT6中绘制UI的两种方法详解与示例代码

《QT6中绘制UI的两种方法详解与示例代码》Qt6提供了两种主要的UI绘制技术:​​QML(QtMeta-ObjectLanguage)​​和​​C++Widgets​​,这两种技术各有优势,适用于不... 目录一、QML 技术详解1.1 QML 简介1.2 QML 的核心概念1.3 QML 示例:简单按钮

Oracle 通过 ROWID 批量更新表的方法

《Oracle通过ROWID批量更新表的方法》在Oracle数据库中,使用ROWID进行批量更新是一种高效的更新方法,因为它直接定位到物理行位置,避免了通过索引查找的开销,下面给大家介绍Orac... 目录oracle 通过 ROWID 批量更新表ROWID 基本概念性能优化建议性能UoTrFPH优化建议注