【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

相关文章

SpringBoot日志配置SLF4J和Logback的方法实现

《SpringBoot日志配置SLF4J和Logback的方法实现》日志记录是不可或缺的一部分,本文主要介绍了SpringBoot日志配置SLF4J和Logback的方法实现,文中通过示例代码介绍的非... 目录一、前言二、案例一:初识日志三、案例二:使用Lombok输出日志四、案例三:配置Logback一

Python实现无痛修改第三方库源码的方法详解

《Python实现无痛修改第三方库源码的方法详解》很多时候,我们下载的第三方库是不会有需求不满足的情况,但也有极少的情况,第三方库没有兼顾到需求,本文将介绍几个修改源码的操作,大家可以根据需求进行选择... 目录需求不符合模拟示例 1. 修改源文件2. 继承修改3. 猴子补丁4. 追踪局部变量需求不符合很

mysql出现ERROR 2003 (HY000): Can‘t connect to MySQL server on ‘localhost‘ (10061)的解决方法

《mysql出现ERROR2003(HY000):Can‘tconnecttoMySQLserveron‘localhost‘(10061)的解决方法》本文主要介绍了mysql出现... 目录前言:第一步:第二步:第三步:总结:前言:当你想通过命令窗口想打开mysql时候发现提http://www.cpp

Mysql删除几亿条数据表中的部分数据的方法实现

《Mysql删除几亿条数据表中的部分数据的方法实现》在MySQL中删除一个大表中的数据时,需要特别注意操作的性能和对系统的影响,本文主要介绍了Mysql删除几亿条数据表中的部分数据的方法实现,具有一定... 目录1、需求2、方案1. 使用 DELETE 语句分批删除2. 使用 INPLACE ALTER T

MySQL INSERT语句实现当记录不存在时插入的几种方法

《MySQLINSERT语句实现当记录不存在时插入的几种方法》MySQL的INSERT语句是用于向数据库表中插入新记录的关键命令,下面:本文主要介绍MySQLINSERT语句实现当记录不存在时... 目录使用 INSERT IGNORE使用 ON DUPLICATE KEY UPDATE使用 REPLACE

CentOS 7部署主域名服务器 DNS的方法

《CentOS7部署主域名服务器DNS的方法》文章详细介绍了在CentOS7上部署主域名服务器DNS的步骤,包括安装BIND服务、配置DNS服务、添加域名区域、创建区域文件、配置反向解析、检查配置... 目录1. 安装 BIND 服务和工具2.  配置 BIND 服务3 . 添加你的域名区域配置4.创建区域

mss32.dll文件丢失怎么办? 电脑提示mss32.dll丢失的多种修复方法

《mss32.dll文件丢失怎么办?电脑提示mss32.dll丢失的多种修复方法》最近,很多电脑用户可能遇到了mss32.dll文件丢失的问题,导致一些应用程序无法正常启动,那么,如何修复这个问题呢... 在电脑常年累月的使用过程中,偶尔会遇到一些问题令人头疼。像是某个程序尝试运行时,系统突然弹出一个错误提

电脑提示找不到openal32.dll文件怎么办? openal32.dll丢失完美修复方法

《电脑提示找不到openal32.dll文件怎么办?openal32.dll丢失完美修复方法》openal32.dll是一种重要的系统文件,当它丢失时,会给我们的电脑带来很大的困扰,很多人都曾经遇到... 在使用电脑过程中,我们常常会遇到一些.dll文件丢失的问题,而openal32.dll的丢失是其中比较

python中字符串拼接的几种方法及优缺点对比详解

《python中字符串拼接的几种方法及优缺点对比详解》在Python中,字符串拼接是常见的操作,Python提供了多种方法来拼接字符串,每种方法有其优缺点和适用场景,以下是几种常见的字符串拼接方法,需... 目录1. 使用 + 运算符示例:优缺点:2. 使用&nbsjsp;join() 方法示例:优缺点:3

Mysql中深分页的五种常用方法整理

《Mysql中深分页的五种常用方法整理》在数据量非常大的情况下,深分页查询则变得很常见,这篇文章为大家整理了5个常用的方法,文中的示例代码讲解详细,大家可以根据自己的需求进行选择... 目录方案一:延迟关联 (Deferred Join)方案二:有序唯一键分页 (Cursor-based Paginatio