【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

相关文章

2. c#从不同cs的文件调用函数

1.文件目录如下: 2. Program.cs文件的主函数如下 using System;using System.Collections.Generic;using System.Linq;using System.Threading.Tasks;using System.Windows.Forms;namespace datasAnalysis{internal static

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

浅谈主机加固,六种有效的主机加固方法

在数字化时代,数据的价值不言而喻,但随之而来的安全威胁也日益严峻。从勒索病毒到内部泄露,企业的数据安全面临着前所未有的挑战。为了应对这些挑战,一种全新的主机加固解决方案应运而生。 MCK主机加固解决方案,采用先进的安全容器中间件技术,构建起一套内核级的纵深立体防护体系。这一体系突破了传统安全防护的局限,即使在管理员权限被恶意利用的情况下,也能确保服务器的安全稳定运行。 普适主机加固措施:

webm怎么转换成mp4?这几种方法超多人在用!

webm怎么转换成mp4?WebM作为一种新兴的视频编码格式,近年来逐渐进入大众视野,其背后承载着诸多优势,但同时也伴随着不容忽视的局限性,首要挑战在于其兼容性边界,尽管WebM已广泛适应于众多网站与软件平台,但在特定应用环境或老旧设备上,其兼容难题依旧凸显,为用户体验带来不便,再者,WebM格式的非普适性也体现在编辑流程上,由于它并非行业内的通用标准,编辑过程中可能会遭遇格式不兼容的障碍,导致操

透彻!驯服大型语言模型(LLMs)的五种方法,及具体方法选择思路

引言 随着时间的发展,大型语言模型不再停留在演示阶段而是逐步面向生产系统的应用,随着人们期望的不断增加,目标也发生了巨大的变化。在短短的几个月的时间里,人们对大模型的认识已经从对其zero-shot能力感到惊讶,转变为考虑改进模型质量、提高模型可用性。 「大语言模型(LLMs)其实就是利用高容量的模型架构(例如Transformer)对海量的、多种多样的数据分布进行建模得到,它包含了大量的先验

【北交大信息所AI-Max2】使用方法

BJTU信息所集群AI_MAX2使用方法 使用的前提是预约到相应的算力卡,拥有登录权限的账号密码,一般为导师组共用一个。 有浏览器、ssh工具就可以。 1.新建集群Terminal 浏览器登陆10.126.62.75 (如果是1集群把75改成66) 交互式开发 执行器选Terminal 密码随便设一个(需记住) 工作空间:私有数据、全部文件 加速器选GeForce_RTX_2080_Ti

【VUE】跨域问题的概念,以及解决方法。

目录 1.跨域概念 2.解决方法 2.1 配置网络请求代理 2.2 使用@CrossOrigin 注解 2.3 通过配置文件实现跨域 2.4 添加 CorsWebFilter 来解决跨域问题 1.跨域概念 跨域问题是由于浏览器实施了同源策略,该策略要求请求的域名、协议和端口必须与提供资源的服务相同。如果不相同,则需要服务器显式地允许这种跨域请求。一般在springbo

AI(文生语音)-TTS 技术线路探索学习:从拼接式参数化方法到Tacotron端到端输出

AI(文生语音)-TTS 技术线路探索学习:从拼接式参数化方法到Tacotron端到端输出 在数字化时代,文本到语音(Text-to-Speech, TTS)技术已成为人机交互的关键桥梁,无论是为视障人士提供辅助阅读,还是为智能助手注入声音的灵魂,TTS 技术都扮演着至关重要的角色。从最初的拼接式方法到参数化技术,再到现今的深度学习解决方案,TTS 技术经历了一段长足的进步。这篇文章将带您穿越时

模版方法模式template method

学习笔记,原文链接 https://refactoringguru.cn/design-patterns/template-method 超类中定义了一个算法的框架, 允许子类在不修改结构的情况下重写算法的特定步骤。 上层接口有默认实现的方法和子类需要自己实现的方法

使用JS/Jquery获得父窗口的几个方法(笔记)

<pre name="code" class="javascript">取父窗口的元素方法:$(selector, window.parent.document);那么你取父窗口的父窗口的元素就可以用:$(selector, window.parent.parent.document);如题: $(selector, window.top.document);//获得顶级窗口里面的元素 $(