代码之家  ›  专栏  ›  技术社区  ›  ikegami Gilles Quénot

关节手术中常见的关节转换

  •  0
  • ikegami Gilles Quénot  · 技术社区  · 8 月前

    我目前正在学习 C中的类型转换 ,我在隐式类型转换中遇到了常用算术转换的概念。

    隐式类型转换,其中C编译器在没有程序员参与的情况下自动转换类型,这是我所理解的:如果类型很窄,它会被提升为更宽的类型。

    关系运算符的首次实验

    #include <stdio.h>
    
    int main(void){
    
        unsigned long int a  = 8888;
        unsigned long int c = 0;
        signed int b = -1;
    
        if(a > b){
            c = 10;
        }else {
            c = 7;
        }
        
        printf("C value is %lu\n",c);
        
    }
    

    在提供的代码中,c的值最终为7,尽管从数学上讲,a大于b,我们预计c为10。 enter image description here

    为什么没有发生? 这取决于类型的推广。因为在这里,与更大(更宽)的无符号类型相比,有符号类型是一个更小的类型(窄),因此它被提升=>这会导致类型升级,因此有符号类型会隐式转换为无符号类型,这意味着现在b的值为2^32,约为40亿,因此if语句为false,else语句被执行。

    对于关系运算符来说,这是可以的。

    算术运算符的第二次实验

    #include <stdio.h>
    
    int main(void){
    
        unsigned long int a  = 8888;
        unsigned long int c;
        signed int b = -1;
    
        c = b + a;
        
        printf("C value is %lu\n",c);
        
    }
    

    enter image description here

    对我来说,这已经发生了

    1. 为什么关系运算符(a>b)的行为不符合我的预期?具体来说,为什么比较似乎将-1(带符号)视为一个大数字而不是一个小数字?

    对于这个关系运算符,隐式转换发生意味着为什么算术运算不发生?

    1. 在算术运算b+a中,为什么我得到的结果是8887,而不是像4294976184这样的更大数字?这是否涉及溢出,或者是如何提升签名值的另一个问题?
    3 回复  |  直到 8 月前
        1
  •  4
  •   ikegami Gilles Quénot    8 月前

    如您所料,以下操作数 < + 进行类型转换。具体来说,他们经历了 常用的算术转换 . 1.

    执行时 常用的算术转换 对于A unsigned long int ( a )以及a signed int ( b ),the 有符号整型 ( b )转换为a 无符号长整型 . [C23§6.3.1.8]

    转换 -1 到a 无符号长整型 ,我们反复加或减 ULONG_MAX+1 (使用算术规则,而不是C规则),直到我们得到一个范围内的结果。 [C23§6.3.1.3 2] 2. 这产生 ULONG_MAX .

    a > b

    这将比较转换后的值。 8888 ULONG_MAX 。显然是假的。

    a + b

    这将对转换后的值求和。更具体地说,由于这些是无符号整数,因此将转换后的值相加,取该类型的最大值加1的模。 [C23§6.2.5 11] 3. 所以这个产品 ( 8888 + ULONG_MAX ) % ( ULONG_MAX + 1 ) (使用算术规则,而不是C规则),或 8887 .


    C23§6.3.1.8[…]否则,如果具有无符号整数类型的操作数的秩大于或等于其他操作数的类型的秩,则具有有符号整数的操作数将转换为无符号整数的类型。[...]

    C23§6.3.1.3 2否则,如果新类型是无符号的,则通过重复增加或减少新类型中可以表示的最大值来转换值,直到该值在新类型的范围内。

    C23§6.2.5 11有符号整数类型的非负值范围是相应无符号整数类型中的一个子范围,每种类型中相同值的表示是相同的。无符号类型的可表示值范围为0到2 N 1(含)。涉及无符号操作数的计算永远不会产生溢出,因为无符号类型的算术运算是以模2执行的 N .


    1. 处理器通常无法对不同类型的操作数执行操作。
    2. 这相当于在二进制补码机上进行符号扩展操作。
    3. 这相当于简单地删除不合适的高意义位。
        2
  •  1
  •   Eric Postpischil    8 月前
    1. 具体来说,为什么比较似乎将-1(带符号)视为一个大数字而不是一个小数字?

    C中的算术在很大程度上遵循了处理器中可用的算术。处理器通常有处理相同格式数字的指令。例如,可能有一条指令用于将两个8位有符号整数相加,另一条指令是将两个16位有符号数字相加,还有一条指令可以将两个32位有符号的整数相加,以及类似的乘法、除法和比较数字的指令。(有时这些指令利用数学特性,使一条指令可以服务于多个目的。例如,通过添加两个32位数字,可以添加两个8位数字,但忽略前24位。但这是另一个话题。)

    处理器通常没有将32位无符号整数与8位有符号整数进行比较的指令。添加这样的指令需要向处理器添加更多的线路和开关,对于很少使用的操作来说,这是不值得的。如果没有具体的指令,C可以提供比较操作,但它必须使用多条指令。我们可以通过将八位整数与零进行比较来实现。如果它小于零,它也小于无符号整数。否则,我们可以将8位整数转换为32位无符号整数(只需将其放入更大的寄存器中,将高24位设置为零),并使用该指令比较两个32位无信号整数。

    但这比大多数比较都要费力。C在很大程度上是为了促进和鼓励高效的代码。C表达式中的算术转换规则是为典型处理器设计的。想要以不同方式计算表达式的程序员需要为此编写自己的代码。

    1. 在算术运算b+a中,为什么我得到的结果是8887,而不是像4294976184这样的更大数字?这是否涉及溢出,或者是如何提升签名值的另一个问题?

    为了说明,我假设你的C实现使用32位 unsigned long int 。当您添加 无符号长整型 值8888(22B8 16 十六进制)和 signed int 值1 有符号整型 已转换为 无符号长整型 ,产生4294967295(FFFFFFFF 16 ). 这是因为转换为32位无符号整数被定义为模2 32 = 4,294,967,296.

    添加22B8 16 以及FFFFFFF 16 数学上是1000022B7 16 ,但那是33位。无符号整数算术也被定义为换行,因此2 32 位被消除,结果为000022B7 16 = 8887.

        3
  •  1
  •   John Bollinger    8 月前

    隐式类型转换,c编译器在没有程序员参与的情况下自动转换类型,我所理解的是,如果类型很窄,它会升级为更宽的类型

    是的,这是对通常算术转换的合理(尽管粗略)总结。C不会对小于以下值的类型执行任何整数运算 int , * 并且它不直接计算具有混合算术操作数类型的表达式。为了实现这一点,操作数值会根据需要自动转换,使其具有相同的类型(对于二进制操作),至少与 int 如果它是整数类型。转换的结构是为了尽可能地保持价值,但这有两个不可调和的挑战:

    1. 如果一个原始类型是浮点类型,另一个是整数类型,那么FP类型的范围通常更宽,但整数类型的精度更高。在这些情况下,选择浮点类型是为了最好地保留每个操作数的大小,但这可能会失去精度。

    2. 如果操作数具有不同符号的(提升的)整数类型,且两者都不能表示另一个可以表示的所有值,则将有符号的操作数转换为无符号的操作。将超出范围的值转换为无符号类型在C中有很好的定义,它涉及将值除以无符号类型的最大值。这确实意味着小的负数会转化为大的正数。

    1. 为什么关系运算符(a>b)的行为不符合我的预期?具体来说,为什么比较似乎将-1(带符号)视为一个大数字而不是一个小数字?

    关系运算符的操作数需要进行通常的算术转换。您的特定操作数具有类型 unsigned long int signed int ,属于上述第(2)类。带符号操作数转换为 无符号长整型 ,在这种情况下产生类型的最大值 无符号长整型 。这比其他操作数更大( 888UL ).

    1. 在算术运算b+a中,为什么我得到的结果是8887,而不是像4294976184这样的更大数字?这是否涉及溢出,或者是如何提升签名值的另一个问题?

    是的,转换后的操作数的真实算术和大于类型可表示的最大值 无符号长整型 在这种情况下,.C指定了相同的模转换,它具有您观察到的结果。这种情况下的结果与8888+-1的数学结果相同并非偶然:从签名方式来看,它应该是这样的——>定义了无符号转换。


    * 从技术上讲, 整数转换等级低于 int .