【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

相关文章

JAVA中整型数组、字符串数组、整型数和字符串 的创建与转换的方法

《JAVA中整型数组、字符串数组、整型数和字符串的创建与转换的方法》本文介绍了Java中字符串、字符数组和整型数组的创建方法,以及它们之间的转换方法,还详细讲解了字符串中的一些常用方法,如index... 目录一、字符串、字符数组和整型数组的创建1、字符串的创建方法1.1 通过引用字符数组来创建字符串1.2

Java调用Python代码的几种方法小结

《Java调用Python代码的几种方法小结》Python语言有丰富的系统管理、数据处理、统计类软件包,因此从java应用中调用Python代码的需求很常见、实用,本文介绍几种方法从java调用Pyt... 目录引言Java core使用ProcessBuilder使用Java脚本引擎总结引言python

Apache Tomcat服务器版本号隐藏的几种方法

《ApacheTomcat服务器版本号隐藏的几种方法》本文主要介绍了ApacheTomcat服务器版本号隐藏的几种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需... 目录1. 隐藏HTTP响应头中的Server信息编辑 server.XML 文件2. 修China编程改错误

Java中switch-case结构的使用方法举例详解

《Java中switch-case结构的使用方法举例详解》:本文主要介绍Java中switch-case结构使用的相关资料,switch-case结构是Java中处理多个分支条件的一种有效方式,它... 目录前言一、switch-case结构的基本语法二、使用示例三、注意事项四、总结前言对于Java初学者

使用Python实现大文件切片上传及断点续传的方法

《使用Python实现大文件切片上传及断点续传的方法》本文介绍了使用Python实现大文件切片上传及断点续传的方法,包括功能模块划分(获取上传文件接口状态、临时文件夹状态信息、切片上传、切片合并)、整... 目录概要整体架构流程技术细节获取上传文件状态接口获取临时文件夹状态信息接口切片上传功能文件合并功能小

Oracle Expdp按条件导出指定表数据的方法实例

《OracleExpdp按条件导出指定表数据的方法实例》:本文主要介绍Oracle的expdp数据泵方式导出特定机构和时间范围的数据,并通过parfile文件进行条件限制和配置,文中通过代码介绍... 目录1.场景描述 2.方案分析3.实验验证 3.1 parfile文件3.2 expdp命令导出4.总结

更改docker默认数据目录的方法步骤

《更改docker默认数据目录的方法步骤》本文主要介绍了更改docker默认数据目录的方法步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一... 目录1.查看docker是否存在并停止该服务2.挂载镜像并安装rsync便于备份3.取消挂载备份和迁

JavaScript DOM操作与事件处理方法

《JavaScriptDOM操作与事件处理方法》本文通过一系列代码片段,详细介绍了如何使用JavaScript进行DOM操作、事件处理、属性操作、内容操作、尺寸和位置获取,以及实现简单的动画效果,涵... 目录前言1. 类名操作代码片段代码解析2. 属性操作代码片段代码解析3. 内容操作代码片段代码解析4.

SpringBoot3集成swagger文档的使用方法

《SpringBoot3集成swagger文档的使用方法》本文介绍了Swagger的诞生背景、主要功能以及如何在SpringBoot3中集成Swagger文档,Swagger可以帮助自动生成API文档... 目录一、前言1. API 文档自动生成2. 交互式 API 测试3. API 设计和开发协作二、使用

python忽略warnings的几种方法

《python忽略warnings的几种方法》本文主要介绍了几种在Python忽略警告信息的方法,,可以使用Python内置的警告控制机制来抑制特定类型的警告,下面就来介绍一下,感兴趣的可以了解一下... 目录方法 1: 使用 warnings 模块过滤特定类型和消息内容的警告方法 2: 使用 warnin