zoukankan      html  css  js  c++  java
  • Floating-Point Numbers, UVa11809

    文章末尾附上英文题目
    这道题在“紫书”的第三章,难度应该不大(水题),可是做了好久……

    题目大意

    计算机用阶码-尾数的方式保存浮点数。
    浮点数
    如图,尾数(Mantissa)有8位,阶码(exponent)有6位,可以表示的最大浮点数为0.111111111221111112。(文中所有数的下标表示进制,如前面的数是二进制)
    这里尾数0.111111111为9位。因为12m<1,所以二进制表示m时总是0.1的形式,计算机表示时,把最前面不变的0.1部分省略,只表示变化的部分,所以实际计算时,比图中的尾数多了一位。
    我们可以看到,数在计算机中是以二进制的形式保存的,因此需要把二进制0.111111111221111112转换成十进制0.9980468751026310,换成科学技术法形式表示9.20535763834529410101810
    我们需要根据这个最大浮点数(9.2053576383452941018),求出尾数和阶码的位数。

    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
    ", i, j);
        }
        return 0;
    }
    

    求解步骤

    打表

    0M<91E30,了解到可以打表先存起来,后面输入时直接查表即可。

    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]);
            }
    }
    
    • 把对应位数的二进制me转换成十进制
    • 计算m时,对应位数i的十进制m计算步骤
      21+22++21i=121i
    • 计算e时,对应位数j的十进制e计算步骤
      20+21++2j1=2j1
    • 对于e的计算,我们可以用位运算
    (1<<j) -1
    • m2e=A10B,不过直接计算目测会溢出(能优化就优化吧)。我们采用“对数法”(姑且这么叫吧),即等式两边取对数,得

      log10m+elog102=log10A+Blog1010=log10A+B

    • 0<log10A<1,对log10m+elog102取整数部分,即是BA=10(log10A+B)B=10(log10m+elog102)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);
    • 因为是A10B的形式,所以我们应限定1A<10,然而题目中只说0<A<10。因此我们应该再处理一下
    if(a < 1){
        a*=10;
        b-=1;
    }

    查表

    • 在表中查找,其中关于a的精度,根据290.001953,我们限定精度为0.0001即可。
    if(b == B[i][j] && fabs(a - A[i][j]) < 0.0001)

    英文题目

    题目-1
    题目-2
    题目-3

  • 相关阅读:
    adb常用命令和工具
    playwright学习记录
    vue,element-ui表格,多个单元格值可修改(点击聚焦后变成input,失去焦点请求保存)
    vue,element-ui表格,合并单元格,如果需要合并的数据隔行,需要重新排列数组
    cas-5.3.x接入REST登录认证,移动端登录解决方案
    企业级cas5.3登录页面修改
    cas实现单点登录mysql,oracle双版本
    Mycat实现MySQL主从复制和读写分离(双主双从)
    IDEA安装插件后默认存放的位置
    值得推荐的Idea十几大优秀插件
  • 原文地址:https://www.cnblogs.com/Genesis2018/p/9079817.html
Copyright © 2011-2022 走看看