走进SEH(Structured Exception Handling)

Windows的异常处理 文章目录Windows的异常处理SEHSEH的终结处理SEH异常处理过滤表达式嵌套使用异常处理和终结处理SEH的实现机制局部展开全局展开VEHSEH和VEH的区别 Windows的异常处理 Windows系统的异常处理有两种: 结构化异常处理(SEHStructured Exception Handling) 向量化异常处理(VEH,Vectored Exception Handling) SEH Windows定义了SEH机制来规范异常处理代码的设计(对程序员)和编译(对编译器)SEH 提供了 阅读详情

	最近手头上工作比较轻松,于是继续充点电。本系列主要从核心编程里面学来,同时外加网上的搜集整理得来。文章主要以应用为主,所以对已经了解的人可能不会有太大帮助,但学习的总结分享出来总会有其用武之地。如发现内容表述有误,请指正。由于篇幅较长,为便于阅读故斩其首分上中下以述之~


前言


        SEH("Structured Exception Handling"),即结构化异常处理,是Windows操作系统提供给程序设计者的强有力的处理程序错误或异常的武器。促使微软将SHE机制引入Windows系统的因素之一是它可以简化操作系统本身的开发工作,操作系统的开发人员使用SHE来让系统更加健壮,而我们可以使用SHE来强化我们的应用程序,其也是微软对C++的扩展。

       SEH主要关键词包括:__try、__finnally、__except、__leave。

       让编译器支持SHE并不是一件简单的任务,为了让其运作起来,编译器需要额外做一些工作。比如在进入和离开异常处理代码块时,需要实现局部展开、全局展开等工作,不同的编译器也会以不同的方式来实现它,但大部分编译器都遵循了Microsoft建议的语法。本次讨论均是以VC++规定的语法。

      SEH和C++标准的异常处理是不同的后面会解释它们之间的关系。除了在语法的表现形式上不同外,值得注意的是在VC++中对C++异常处理支持上其实是利用了编译器和Windows的SEH功能。

      SHE包含了两方面的功能:终止处理(termination handling)和异常处理(exception handling)。

      下面先感性的认识下它们的语法形式:

终止处理异常处理
__try{

//code

}

__finnally{

//终止处理程序

}

__try{

//code

}

__except(//异常过滤程序){

//异常处理程序

}


第一篇  终止处理


      先来看看微软是对try-finally怎么解释的:


The try-finally statement is a Microsoft extension to the C and C++ languages that enables 32-bit target applications toguarantee execution of cleanup code when execution of a block of code is interrupted.Cleanup consists of such tasks as deallocating memory, closing files, and releasing file handles. The try-finally statement is especially useful for routines that have several places where a check is made for an error that could cause premature return from the routine.


      即终止处理就是保证应用程序在一段被保护的代码发生中断后(无论是异常还是其他)还能够执行清理工作,清理工作包括关闭文件、清理内存等。先来简单的看段代码,了解其特性:


void SimpleFinalTest()
{
    int* p = nullptr;
    __try{
        PrintLine(">>__try:");

        p = (int*)malloc(10);
        WaitForSingleObject(hSem, INFINITE);
	
        PrintLine(">>    : begin test;");
        //其他代码,其中可能出现异常
        //... ...
    }
    __finally{
        // 释放分配的内存和信号量
        if (p){
            free(p);
        }
        ReleaseSemaphore(hSem, 1, NULL);

        PrintLine(">>__finally: free memory;");
    }
}


      在上面的代码中,被保护的代码段中动态分配了一块内存,同时请求了一个信号量。当由于一些原因导致后面的代码发生了异常,这样程序也会因为异常中断运行,直接的结果就是可能会导致内存泄露以及信号量永远无法释放。在这里幸运的是__try/__finally能够保证__try中代码执行完后(就算发生异常中断或是其他),均会进入终止处理程序__finally中执行。终止运行程序的用法与好处也就不多说了。

      再来测试下try-finally“保证清理工作的执行”的能力有多大。假如我return或是goto还行吗?      

int FinallyReturnTest()
{
    PrintLine(__FUNCTION__);
    int a = 0;
    __try{
        a = 1;
        PrintLine(">>__try: return test");
        return a;
    }
    __finally{
        a = 2013;
        PrintLine(">>__finally: return 2013.");
        return a;
    }
    return 0;
}


      通过结果可以看出就算是return(以及其他goto等),__finally最后也还会执行。这里面会发生局部展开。在try块中,return时会创建一个临时变量用于保存返回结果,然后局部展开进入finally代码块执行,return时会将“2013”写入返回结果的临时变量中,因此最终结果是2013,而不是1.


局部展开


      局部展开(local unwind):指某个函数中的__try块中的代码因为执行了return,break,goto,continue等指令使得指令流要提前流出__try块,这时编译器就会生成一些代码以跳转执行__finally块中的代码,以保证__finally语义的正确性,执行完__finally里面的代码后程序指针会回到原来的位置(除非__finally程序块存在return等导致函数提前结束)继续执行,这个过程好像是在__try的return(等)处展开了__finally的代码。

void local_unwind()
{
    PrintLine(__FUNCTION__);
    __try{
        PrintLine(">>local_unwind:: in __try.");    //(1)
        goto DO_GOTO;                  //(2)执行跳转,但先局部展开
        PrintLine(">>local_unwind:: continu.");
    }
    __finally{
        PrintLine(">>local_unwind:: in __finally.");  //(3)局部展开
    }

DO_GOTO:
    {
        PrintLine(">>local_unwind:: excute goto code."); //(4)最后执行跳转
    }
}

       可以看出局部展开会先执行finally中的语句,然后再执行goto跳转的代码段。

 

注:

“不管在何种情况下,在离开当前的作用域时,finally块区域内的代码都将会被执行到”,这是核心法则。

      当然,这也不完全是绝对的法则,假如__try语句或是外部出现了ExitProcess/ExitThread等终止线程或进程时,以及其他的一些异常导致SHE链的中断,终止处理程序也不会得到执行。

注:

在__try/__finally结构中,因为局部展开会产生额外的开销,所以在__try/__finally中应该避免使用return/goto等语句。

当需要提前退出__try块时请使用关键词__leave代替return等,__leave会直接将代码跳转至__try的结尾,这种情况下将不会产生额外的开销。

      这就是__leave关键字的作用,也许大家在编程时很少使用它。但是请注意,如果你的程序中,尤其在那些业务特别复杂的函数模块中,既采用了SEH机制来保证程序的可靠性,同时代码中又拥有大量的goto语句和return语句的话,那么你的源代码编译出来的二进制程序将是十分糟糕的,不仅十分庞大,而且效率也受很大影响。此时,建议不妨多用__leave关键字来提高程序的性能。



AbnormalTermination函数


Finally块的执行总是由以下3中状况引起的:

(1)      正常流出__try语句进入__finally;

(2)      被保护代码发生异常中断而导致的全局展开进入__finally;

(3)      由于return、goto等语句导致的局部展开进入__finally;

 

      内在函数AbnormalTermination()函数可以判断是由哪种方式进入__finally块中的。该函数只能在__finally里面调用,当情况(1)的时候返回FALSE,其他两种非正常情况返回TRUE。

      内在函数是由编译器所识别并处理的特殊函数,编译器会为这个函数生成内联代码,比如memecpy等。



第二篇  异常处理程序与软件异常


         在程序的设计中,难免会碰到访问非法内存地址以及除数为0等异常错误,CPU负责捕获,一旦侦测到这些错误行为,它会抛出相应的异常,由CPU抛出的异常都是硬件异常;同样,操作系统和应用程序也可以抛出异常,这些异常通常被称为软件异常。语法结构为:

异常处理

__try{

//code

}

__except(//异常过滤程序){

//异常处理程序

}

      该结构和C++标准里面的try-catch有些类似,但catch 和 except 有一点不同: catch关键字后面往往接受一个函数参数一样,可以是各种类型的异常数据对象;但是__except关键字则不同,它后面跟的却是一个表达式(亦称为异常过滤程序),该段只能为表达式或函数调用,返回值必须是以下3种之一:

#define EXCEPTION_EXECUTE_HANDLER       1  // 执行异常处理程序,触发全局展开

#define EXCEPTION_CONTINUE_SEARCH       0  // 不执行,继续查找外层(上一级)的try块

#define EXCEPTION_CONTINUE_EXECUTION  -1  // 不会触发全局展开,继续返回异常处重新执行


      注意:一个__try块后面只能跟一个__finally或者__except,不能为多个,而且二者不能同时存在。但是__try/__finally倒是可以喝__try/__except互相嵌套。

 

      先简单看一个例子来了解其用法与特性:

void ExceptTestFunc()
{
    __try{
        PrintLine(">>ExceptTestFunc:: in __try.");        // (1)
        SubTestFunc();
        PrintLine(">>ExceptTestFunc:: return from __try.");   // 因为异常永远不会执行到
        return;
    }
    __except(PrintLine(">>ExceptTestFunc: except filter, excute."), EXCEPTION_EXECUTE_HANDLER){
        PrintLine(">>ExceptTestFunc:: in __except, handle exception.");
    }
}

void SubTestFunc()
{
    __try{
        PrintLine(">>SubTestFunc:: in __try.");
        int* p = nullptr;
           PrintLine(">>SubTestFunc:: exception occur.");
        *p = 5;             // 此处发生异常
    }
    __except(PrintLine(">>SubTestFunc: except filter, don't excute."), EXCEPTION_CONTINUE_SEARCH){
        PrintLine(">>SubTestFunc:: in __except, handle exception.");      //因为不处理所以也执行不到
    }
}

      假如没有try-except块,上面的代码由于访问不合法内存会直接导致应用程序的终止,但是SHE能够帮助我们捕获异常并允许我们处理它而不是直接的导致程序的终止。根据上面的结果我们很容易分析该程序的流程。__except只有在发生异常后才会进入判断并执行,否则该代码永远也不会进入,这点跟__finally是完全不同的,__finlly是几乎在所有情况下均会进入执行。

      注:在__try/__finally块中我们不建议使用return、goto等,这样会导致触发局部展开程序性能损失或增加代码量;但是在__try/__except中则不会产生局部展开这样的额外开销,(因为局部展开就是为了执行__finally,这个解释还是很给力的吧…),但还是不建议使用return等,因为这会导致代码逻辑变乱。虽然这里不会产生局部展开,但确会发生全局展开。


全局展开


      当异常过滤程序的计算结果为EXCEPTION_EXECUTE_HANDLER时,系统必须执行全局展开。全局展开导致执行其内部嵌套的尚未完成的__try/__finally块。简单的说就是发生异常后,先找到外层的能够处理该异常的__try/__except块,然后再由内向外执行还未完成的__finally程序块。

      说再多都不如看代码更直接,看个更典型的例子:

void ExceptTestFunc()
{
    __try{
        PrintLine(">>ExceptTestFunc:: in __try.");       //(1)
        SubTestFunc();           // 进入异常函数
        PrintLine(">>ExceptTestFunc:: return from __try.");  //异常导致此处不会执行
        return;
    }
    __except(PrintLine(">>ExceptTestFunc: except filter, excute."), EXCEPTION_EXECUTE_HANDLER  //(5)){
        PrintLine(">>ExceptTestFunc:: in __except, handle exception.");   //(7)全局展开最后执行异常处理
    }
    PrintLine(">>ExceptTestFunc:: end.");    //(8)程序不会终止,函数执行结束
}
void SubTestFunc()
{
    __try{
        __try{
            PrintLine(">>SubTestFunc:: in __try.");       //(2)
            int* p = nullptr;
            PrintLine(">>SubTestFunc:: exception occur.");  //(3)
            *p = 5;                          //此处发生异常
        }
        __finally{
            PrintLine(">>SubTestFunc:: in __finally, excute cleanup."); //(6)全局展开执行finally
        }
    }
    __except(PrintLine(">>SubTestFunc: except filter, don't excute."), EXCEPTION_CONTINUE_SEARCH  //(4))
    {
        PrintLine(">>SubTestFunc:: in __except, handle exception.");
    }

    PrintLine(">>SubTestFunc:: end.");
}

void SubTestFunc()
{
    __try{
        PrintLine(">>SubTestFunc:: in __try.");        int* p = nullptr;
        PrintLine(">>SubTestFunc:: exception occur.");
        *p = 5; 
    }
    __finally{
        PrintLine(">>SubTestFunc:: in __finally, excute cleanup.");   
    }

    PrintLine(">>SubTestFunc:: end.");
}


      注意代码中标示出的执行流程。这一切顺序说明了发生异常后先执行异常过滤程序找到EXCEPTION_EXECUTE_HANDLER处(异常处理程序入口),然后再全局展开执行尚未执行的__finally程序,最后进入异常处理程序。

      如下图为全局展开的流程:



停止全局展开


      当全局展开发生后,若某一个__finally块中存在return语句,则会导致后续的全局展开终止,即程序会像什么异常都没发生过一样继续执行。看下面一个例子:

void SubTestFunc()
{
    __try{
        PrintLine(">>SubTestFunc:: in __try.");
        int* p = nullptr;
        PrintLine(">>SubTestFunc:: exception occur.");
        *p = 5;                          // 此处发生异常,进入全局展开
    }
    __finally{
        PrintLine(">>SubTestFunc:: in __finally, excute cleanup."); 
        return;          // 全局展开终止
    }
    PrintLine(">>SubTestFunc:: end.");
}

void ExceptTestFunc()
{
    __try{
        PrintLine(">>ExceptTestFunc:: in __try.");
        SubTestFunc();      //发生异常
        PrintLine(">>ExceptTestFunc:: continu after exception happened.");  //异常后本不该执行
    }
    __except(PrintLine(">>ExceptTestFunc: except filter, excute."), EXCEPTION_EXECUTE_HANDLER){
        PrintLine(">>ExceptTestFunc:: in __except, handle exception.");
    }
    PrintLine(">>ExceptTestFunc:: end.");
}


      结果发现全局展开被中断了,在发生异常后面的语句被执行了,SubTestFunc()函数被当做正常返回而结束。很显然这会带来很严重的后果,因为有可能后面的程序会因为异常而发生未知的错误,程序执行变得无法控制,这也是我们为什么强调不在__finally内部使用return等语句的原因。如果在__try-finally块内使用return等语句,编译器也会产生warning:

warning C4532: 'return' : jump out of __finally block has undefined behavior during termination handling。



EXCEPTION_CONTINUE_EXECUTION


      其他两个异常过滤返回值很容易理解,上面的例子中也有所涉及,而EXCEPTION_CONTINUE_EXECUTION的用法却是很危险的。举个例子:

LONG Filter(int**p)
{
    if (*p == nullptr)
    {
        *p = new int;                   // 重新分配内存
    }
    PrintLine(">>ExceptTestFunc: except filter, continu excute.");
    return EXCEPTION_CONTINUE_EXECUTION;
}
void SubTestFunc()
{
    int* p = nullptr;
    _try{
        __try{
            PrintLine(">>SubTestFunc:: in __try.");
            PrintLine(">>SubTestFunc:: exception occur.");
            *p = 5;                  // 异常发生,进入异常过滤程序Filter
            PrintLine(">>SubTestFunc:: continu");
        }
        __finally{
            PrintLine(">>SubTestFunc:: in __finally, excute cleanup.");
            cout<<*p<<endl;
        }
    }
    __except(Filter(&p)){
        PrintLine(">>SubTestFunc:: in __except.");    //不会进入异常处理程序,该局不会执行
    }
}


     该段程序大意是在发生异常后,进入Filter过滤程序,过滤程序尝试修复该异常(重新给指针p分配内存),然后返回异常处执行。从逻辑上感觉应该可以行的通,程序能够完成目的,但结果往往会出乎我们的意料。

      运行结果:

      这说明程序修复异常后还是会发生异常。我们调试下看看编译器是怎么完成该过程的:

      这说明过滤程序确实按照期望完成了p指针的重新分配内存,可为什么还会继续发生异常呢?这就追溯到异常的发生处:




原来编译器现将*p的传到寄存器ecx中(即为null),然后再向该寄存器中代表的地址赋值,因而导致访问非法内存抛出异常。在filter过滤函数返回后仍返回了该语句(而不是重新将*p内容传到ecx中),这样当然还会发生异常,因为改变后的p实际并没有生效。这也就解释了为什么会出现上面的结果。

      总之,应该慎用EXCEPTION_CONTINUE_EXECUTION。合理的运用SHE可以写出高效的代码。

 

GetExceptionCode函数

该函数能够返回所捕获的异常为何种异常,其必须在__except之后的括号内部使用,甚至不能在上面例子中的Filter函数中调用,否则会出现编译错误。具体用法以及返回值请参考msdn。

GetExceptionInformation()

略。


软件异常


之前我们讨论的都是硬件异常,有的时候我们希望在应用程序代码里面强制抛出一个自己定义的异常(可能仅仅是逻辑上的不合法),然后运用异常机制来捕获并处理。

比如传统上,我们通过函数的返回值来标示函数的失败,然后不断地将返回值向调用者一层一层往外抛。这种传播导致代码很难写也很难维护。而若是函数在失败时不是返回错误码,而是直接抛出异常,这样就可以利用SHE机制省略了很多的错误检测代码,程序的效率也会更高。

Microsoft为我们提供了一个接口函数用以完成这个功能:RaiseException。

void WINAPI RaiseException(
  _In_  DWORD dwExceptionCode,    //异常标示符,请遵守微软定义的规范
  _In_  DWORD dwExceptionFlags,   //0或EXCEPTION_NONCONTINUABLE
  _In_  DWORD nNumberOfArguments,      //参数个数,lpArguments为null时忽略
  _In_  const ULONG_PTR *lpArguments   //通常为NULL
);

dwExceptionFlags表示当发生这个异常后,程序是否还能够继续执行下去,即异常过滤程序是否可以返回EXCEPTION_CONTINUE_EXECUTION,过滤程序返回值为EXCEPTION_CONTINUE_EXECUTION则会导致再次抛出新的异常EXCEPTION_NONCONTINUABLE_EXCEPTION。

具体用法见示例:

#define MY_EXCEPTION 0xE0000001
void SubTestFunc()
{
    __try{
        PrintLine(">>SubTestFunc:: in __try.");
        int cnt = 5;
        while (1) {
            cnt--;
            if (cnt == 0) {.
PrintLine(">>SubTestFunc:: soft exception occur.");
                RaiseException(MY_EXCEPTION, 0, 0, NULL);        //抛出一个软件异常
            }
        }
        PrintLine(">>SubTestFunc:: continu");
    }
    __finally{
        PrintLine(">>SubTestFunc:: in __finally, excute cleanup.");
    }
    PrintLine(">>SubTestFunc:: end.");
}

void ExceptTestFunc()
{
    int nCode = 0;
    bool bbb;
    __try{
        PrintLine(">>ExceptTestFunc:: in __try.");
        SubTestFunc();
        PrintLine(">>ExceptTestFunc:: continu after exception happened.");
    }
    __except((GetExceptionCode() == MY_EXCEPTION) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH){
        PrintLine(">>ExceptTestFunc::in __except, myexception find, handle exception.");
    }
    PrintLine(">>ExceptTestFunc:: end.");
}

以上示例展示了自定义软件异常的用法,相信大家已经基本明白这个流程是怎么实现的了。软件异常的用法有很多,比如我们可能想给系统的时间日志发送通知消息,一旦程序中的某个函数发生了问题,我们便可以抛出软件异常,并在异常处理处写入到事件的日志中。我们还可能需要利用软件异常来传递应用程序的严重错误信息。



为什么浮点数会搞出Infinity和NAN两种类型?浮点数的底层原理?IEEE 754标准揭秘? NAN全称是NotaNumber,常在浮点数运算中使用,首次引入NaN的是1985年的IEEE 754浮点数标准。如除以零、求负数的平方根等错误的操作,会返回NAN. 例如可以求一个负数的平方根,结果用isnan函数(来自math.h)返回真。 阅读详情

相关推荐

六自由度机器人(机械臂)运动学建模及运动规划系列(四)——轨迹规划

本篇介绍了六轴机器人关节空间以及笛卡尔空间轨迹规划的简单方法,并给出了一种几何解法,以简化规划过程,并提供了简单的Matlab轨迹规划函数介绍。

weixin_47849087的博客 4万+

深入解析结构化异常处理(SEH) - by Matt Pietrek

深入解析结构化异常处理(SEH) - by Matt Pietrek

dvlinker的技术专栏 5万+

RT-DETR改进策略【独家融合改进】| SPD-Conv+PPA 再次提升模型针对小目标的特征提取能力

SPD-Conv是一种新的 CNN 构建模块,用于替代传统 CNN 架构中使用的步长卷积(strided convolution)和池化(pooling)层,它由空间到深度(Space-to-depth,SPD)层和非步长卷积(non - strided convolution)层组成。

Limiiiing的博客 1114

SEH的介绍及实战

根据我们上节的异常讲解中,我们说过了SEH(Structured Exception Handler)是在无调试器接手的情况下,系统会遍历SE链,然后寻找相应的SEH函数来处理异常。 首先稍微复习下我们的TEB kd> dt _teb ntdll!_TEB +0x000 NtTib : _NT_TIB +0x01c EnvironmentPointer : ...

For Geek 1万+

[原创]windows-SEH详解

SEH是window操作系统默认的异常处理机制,逆向分析中,SEH除了基本的异常处理功能外,还大量用于反调试程序(这里SEH时保存在栈中的,漏洞利用的时候会用到) 1.SEH SEH是windows操作系统异常处理机制,在程序源代码中使用__try,__except,__finally关键字来具体实现。 2.OS异常处理的办法 2.1正常运行时候的异常处理方法 进程运行过程中若发生异常,OS会委托进程进行处理。若进程代码中存在具体的异常处理(如SEH异常处理器)...

whl0071的专栏 4820

【逆向工程核心原理:SEH

reverse

qq_42363151的博客 1808

IEEE754详解(最详细简单有趣味的介绍)

IEEE754IEEE754标准: 一、浮点数在内存中的存储方式1、什么是IEEE754标准2、32位单精度浮点数在内存中的存储方式3、实例: 表示十进制浮点数20.5IEEE754标准: 二, 32位浮点数的取值范围1、wiki中, 32位浮点数的取值范围2、前置概念3、计算方法4、补充IEEE754标准: 三、为什么说32位浮点数的精度是"7位有效数"1、先说结论2、在讨论之前...3、理解角度1: 从"间隔"的角度理解1. 铺垫2. 32位浮点数的间隔3. 32位浮点数的间隔表4. 32位浮点数的精度

gao_zhennan的博客 10万+

IEEE-754单精度浮点类型详解(完结篇)

本文主要以单精度浮点类型为例,向大家介绍IEEE-754标准下的浮点数如何存储,以及精度丢失等内容。文章中参考了很多比较知名的博客内容,但绝无抄袭,主要内容是由作者本人对IEEE-754的一些个人理解,希望能给大家带来帮助。...

weixin_45500205的博客 3万+

Javascript IEEE754标准的浮点数二进制表示、浮点数运算及Js为啥0.1+0.2!=-0.3

之前写过一篇“谈谈JavaScript的算数运算、浮点数二进制表示舍入误差及比较、类型转换和变量声明提前问题”,当时主要是阐述浮点数运算产生的舍入误差及js类型转换和变量申明提前问题,所以只是略微提及了js中浮点数二进制表示问题。现在回过头来看,如果不掌握IEEE754标准的浮点数二进制表示,浮点数运算原理,是不会彻底理解js浮点数运算的舍入误差及类似0.1+0.2!=0.3浮点数比较问题。本文讲述了按IEEE754标准浮点数二进制如何表示,浮点数加法运算原理,0.1+0.2的完整计算演示。

chuangxin的专栏 3565

IEEE754标准原文,及浮点数在线转换

原文外网链接 IEEE754.PDF (berkeley.edu)http://people.eecs.berkeley.edu/~wkahan/ieee754status/IEEE754.PDF 百度网盘分享链接 链接:https://pan.baidu.com/s/1KMaDNi04Q0ZzlNkxkONZ9w 提取码:1fwc 一些题外话: 我们常用的编程语言例如C#其采用的float也是采用此标准。 stm32的keil编程也是采用此标准。 有时候我们需要判断一...

anlog的专栏 1万+

IEEE754标准: 浮点数在内存中的存储方式

java基础

GEFEICHEN的博客 4841

IEEE 754标准

由于不同机器所选用的基数、尾数位长度和阶码位长度不同,因此对浮点数的表示有较大差别,这不利于软件在不同计算机之间的移植。为此,美国IEEE(电器及电子工程师协会)提出了一个从系统角度支持浮点数的表示方法,称为IEEE754标准(IEEE,1985),当今流行的计算机几乎都采用了这一标准。 Java中浮点数,既float和double,都是采用的IEEE754标准。无论在java python javaScript里面都存在 1 + 2 != 3 问题,这个问题的产生根源在于计算存储数字是二进制,对无限循环

包磊磊的博客 4924

四字节十六进制转化为单精度IEEE 754 浮点数

要将给定的字节序列解析为 IEEE 754 单精度浮点数(32位,4字节),我们首先需要了解 IEEE 754 单精度浮点数的格式,并按照它的规则进行转换。IEEE 754 单精度浮点数由 32 位(4 字节)组成,其结构如下:最终公式为: [ \text{值} = (-1)^\text{S} \times (1.M) \times 2^{E - 127} ]给定的字节序列是: 我们需要每 4 个字节解析为一个单精度浮点数。以下是解析步骤:分组: 每 4 字节为一组(以十六进制表示): 逐个字节提取并转

weixin_44360797的博客 4301

计算机组成原理:IEEE 754标准

本文深度解析IEEE 754浮点数标准,介绍了其诞生背景(解决硬件厂商间的兼容性问题)、核心内容(单/双精度存储格式、规范化表示、特殊值处理)和运算规则(加减乘除及舍入方法)。作为计算机科学领域的基础规范,IEEE 754广泛应用于科学计算、图形渲染等领域,并得到编程语言和硬件的全面支持。该标准通过统一浮点数表示方式,确保了计算的精确性和跨平台一致性,对现代计算技术发展至关重要。

梁辰兴的博客 3291

MATLAB开发-NUM2IEEE:理解IEEE浮点数表示

本文还有配套的精品资源,点击获取 简介:MATLAB中的"NUM2IEEE"函数将数值转换为IEEE双精度浮点数表示,帮助理解数值计算的底层机制。它遵循IEEE 754标准,定义了双精度浮点数的64位二进制表示,包括符号位、指数部分和尾数部分。函数实现包括符号提取、规范化、指数计算、尾数转换和组合结果等步骤。虽然MATLAB提供了内置函数,但"NUM2IEEE"作为教学和研...

weixin_42437253的博客 1037

计算机基础——IEEE754标准的浮点数的转化

      IEEE754是现在公认的、最广泛使用的浮点数转换运算标准,为许多CPU与浮点运算器所采用。这个标准定义了表示浮点数的格式(包括负零-0)与反常值(denormal number)),一些特殊数值(无穷(Inf)与非数值(NaN)),以及这些数值的“浮点数运算符”;它也指明了四种数值舍入规则和五种例外状况(包括例外发生的时机与处理方式)。       IEEE 754规定了四种表示浮...

Joshua Lee的博客 3万+

IEEE754标准

IEEE754标准

mfnyq的博客 3807

IEEE754-2008 标准详解():浮点数据的分类

IEEE754-2008 标准详解():浮点数据的分类 本文为原创文章,转载请注明出处,并注明转载自“黄邦勇帅(原名:黄勇) 本文是对《C++语法详解》一书相关章节的增补,以增强读者对浮点数的理解,《C++语法详解》网盘地址:https://pan.baidu.com/s/1dIxLMN5b91zpJN2sZv1MNg 有兴趣的读者可参阅本人所著《C++语法详解》一书,电子工业出版社出版,该书语法示例短小精悍,对查阅C++知识点相当方便,并对语法原理进行了透彻、深入详细的讲解,可确保读者彻底弄懂C++的原

黄邦勇帅的博客 7338

IEEE 754 浮点数标准 java float double

并不是所有十进制小数都能用有限的二进制小数表示。计算机存储浮点数的位数有限,需要截断无限循环小数。这导致了一些十进制小数在转换为二进制表示时只能近似表示,结果就是精度丢失。这在浮点数运算中是一个常见的问题,因此需要在数值计算中考虑误差并进行必要的处理。好的,我再详细解释一下十进制小数转换为二进制的方法,确保每一步都清晰明了。这种方法的优势在于简单、直观且直接对应二进制数的位权,使得我们能够通过重复乘法和取整的过程精确地确定二进制小数的每一位。

jupiter_888的博客 946

【机器视觉工具箱】机器视觉工具箱,集成了相机,镜头和光源选型,视觉飞拍选型,旋转中心计算,modbus/网口/串口等机器视觉常用的功能模块,非常实用,视觉工程师的必备小软件

机器视觉工具箱,集成了以下功能1.工业相机选型模块,两种类型相机选型功能。2.工业镜头选型模块,包含焦距计算,视野计算,工作距离计算,景深计算,光圈与进光孔径计算,距离计算功能。显示有图示,让你选型一目了然。3.工业光源选型模块,包含光源结构,色环,打光总理论,视场对比,选型思路等功能。4.视觉飞拍计算模块,计算两种形式下对应的曝光值和速度,避免产生图像拖影。5.旋转中心计算模块,包含三点计算旋转中心,两点计算旋转中心,计算绕选转中心之后的点,用于项目的定位计算验证。6.文件或小程序调用模块,可以通过修改根目录下的文件,将自己常用的文件进行管理和打开,非常实用!资源介绍请查阅:https://blog.csdn.net/m0_37302966/article/details/149910562更多视觉框架资源:https://blog.csdn.net/m0_37302966/article/details/146583453

上一篇: LearnVSXNow! #8 插曲-regpkg.exe命令
下一篇: 跟我一起学Windows界面封装(一) 之 基础篇:Win32 API
古沐风
博客等级 码龄16年 26粉丝 11原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值