Floating-Point Numbers, UVa11809
2016-04-27 20:53
260 查看
文章末尾附上英文题目
这道题在“紫书”的第三章,难度应该不大(水题),可是做了好久……
如图,尾数(Mantissa)有8位,阶码(exponent)有6位,可以表示的最大浮点数为0.1111111112∗21111112。(文中所有数的下标表示进制,如前面的数是二进制)
这里尾数0.111111111为9位。因为12≤m<1,所以二进制表示m时总是0.1∗∗∗的形式,计算机表示时,把最前面不变的0.1部分省略,只表示变化的部分,所以实际计算时,比图中的尾数多了一位。
我们可以看到,数在计算机中是以二进制的形式保存的,因此需要把二进制0.1111111112∗21111112转换成十进制0.99804687510∗26310,换成科学技术法形式表示9.20535763834529410∗101810。
我们需要根据这个最大浮点数(9.205357638345294∗1018),求出尾数和阶码的位数。
把对应位数的二进制m与e转换成十进制
计算m时,对应位数i的十进制m计算步骤2−1+2−2+⋯+2−1−i=1−2−1−i
计算e时,对应位数j的十进制e计算步骤20+21+⋯+2j−1=2j−1
对于e的计算,我们可以用位运算
m∗2e=A∗10B,不过直接计算目测会溢出(能优化就优化吧)。我们采用“对数法”(姑且这么叫吧),即等式两边取对数,得log10m+e∗log102=log10A+B∗log1010=log10A+B
0<log10A<1,对log10m+e∗log102取整数部分,即是B,A=10(log10A+B)−B=10(log10m+e∗log102)−B
因为是A∗10B的形式,所以我们应限定1≤A<10,然而题目中只说0<A<10。因此我们应该再处理一下
这道题在“紫书”的第三章,难度应该不大(水题),可是做了好久……
题目大意
计算机用阶码-尾数的方式保存浮点数。如图,尾数(Mantissa)有8位,阶码(exponent)有6位,可以表示的最大浮点数为0.1111111112∗21111112。(文中所有数的下标表示进制,如前面的数是二进制)
这里尾数0.111111111为9位。因为12≤m<1,所以二进制表示m时总是0.1∗∗∗的形式,计算机表示时,把最前面不变的0.1部分省略,只表示变化的部分,所以实际计算时,比图中的尾数多了一位。
我们可以看到,数在计算机中是以二进制的形式保存的,因此需要把二进制0.1111111112∗21111112转换成十进制0.99804687510∗26310,换成科学技术法形式表示9.20535763834529410∗101810。
我们需要根据这个最大浮点数(9.205357638345294∗1018),求出尾数和阶码的位数。
AC代码
如果看源代码能看懂的话,后面的内容可以略去#include <iostream> #include <stdio.h> #include <cmath> #include <string> #include <cstring> #include <stdlib.h> using namespace std; double A[11][31]; long long B[11][31]; void CreateTable() { for(int i=0; i<10; i++) for(int j=1; j<=30; j++) { double m = 1 - pow(2, -1-i); //double e = pow(2, j) - 1; double e = (1<<j) -1; // 用位运算重写上面注释掉的代码 double convert = log10(m) + e*log10(2); B[i][j] = (long long)convert; A[i][j] = pow(10, convert-B[i][j]); } } int main() { CreateTable(); char s[40]; while(cin.getline(s, 40)) { if(strcmp(s, "0e0") == 0) break; char s1[40], s2[40]; // 分离出e前后的数 sscanf(s, "%[^e]", s1); sscanf(s, "%*[^e]e%s", s2); double a; int b; // 字符数组转换成浮点数与整数 sscanf(s1, "%lf", &a); sscanf(s2, "%d", &b); if(a < 1){ a*=10; b-=1; } for(int i=0; i<10; i++) for(int j=1; j<=30; j++) if(b == B[i][j] && fabs(a - A[i][j]) < 0.0001) printf("%d %d\n", i, j); } return 0; }
求解步骤
打表
0≤M<9,1≤E≤30,了解到可以打表先存起来,后面输入时直接查表即可。void CreateTable() { for(int i=0; i<10; i++) for(int j=1; j<=30; j++) { double m = 1 - pow(2, -1-i); //double e = pow(2, j) - 1; double e = (1<<j) -1; // 用位运算重写上面注释掉的代码 double convert = log10(m) + e*log10(2); B[i][j] = (long long)convert; A[i][j] = pow(10, convert-B[i][j]); } }
把对应位数的二进制m与e转换成十进制
计算m时,对应位数i的十进制m计算步骤2−1+2−2+⋯+2−1−i=1−2−1−i
计算e时,对应位数j的十进制e计算步骤20+21+⋯+2j−1=2j−1
对于e的计算,我们可以用位运算
(1<<j) -1
m∗2e=A∗10B,不过直接计算目测会溢出(能优化就优化吧)。我们采用“对数法”(姑且这么叫吧),即等式两边取对数,得log10m+e∗log102=log10A+B∗log1010=log10A+B
0<log10A<1,对log10m+e∗log102取整数部分,即是B,A=10(log10A+B)−B=10(log10m+e∗log102)−B
B[i][j] = (long long)(log10(m) + e*log10(2)); A[i][j] = pow(10, (log10(m) + e*log10(2))-B[i][j]);
处理输入
整行读取,分理处e前后的数。这里我两次用到了sscanf函数,先是配合正则表达式提取出e前后的字符数组,再把字符数组转换成对应的浮点数与整数。(后来发现用strchr函数更方便…)char s1[40], s2[40]; // 分离出e前后的数 sscanf(s, "%[^e]", s1); sscanf(s, "%*[^e]e%s", s2); double a; int b; // 字符数组转换成浮点数与整数 sscanf(s1, "%lf", &a); sscanf(s2, "%d", &b);
因为是A∗10B的形式,所以我们应限定1≤A<10,然而题目中只说0<A<10。因此我们应该再处理一下
if(a < 1){ a*=10; b-=1; }
查表
在表中查找,其中关于a的精度,根据2−9≈0.001953,我们限定精度为0.0001即可。if(b == B[i][j] && fabs(a - A[i][j]) < 0.0001)
英文题目
相关文章推荐
- 关于PHP浮点数你应该知道的(All 'bogus' about the float in PHP)
- 使用 Libki 来管理公共用户访问计算机
- 微型计算机的始祖:Altair 8800
- 通过手机、电脑远程开关机,Windows和linux机手机,电脑相互控制
- C#浮点数的表示和基本运算
- mysql binlog二进制日志详解
- 详解C++编程中对二进制文件的读写操作
- 整理C# 二进制,十进制,十六进制 互转
- php实现用手机关闭计算机(电脑)的方法
- c#二进制逆序方法详解
- JS幻想 读取二进制文件第1/2页
- 使用jscript实现二进制读写脚本代码
- 用IE重起计算机或者关机的示例代码
- C#二进制序列化实例分析
- Javascript正则控制文本框只能输入整数或浮点数
- 一张图告诉你计算机编程语言的发展历史
- JavaScript前端开发之实现二进制读写操作
- PHP函数篇详解十进制、二进制、八进制和十六进制转换函数说明
- javascript 二进制运算技巧解析
- javaScript实现浮点数转十六进制字符