强制转换是解释值还是只解释位?

本文关键字:解释 转换 | 更新日期: 2023-09-27 18:14:22

所以我一直认为强制转换意味着取某物的二进制,并将其解释为强制转换的实体(必要时截断)。然而,我注意到在c#中,你可以采用双精度,这是一个64位的表示,包括指数和值。您可以将其强制转换为long,这是一个64位的整数表示形式,不带指数。因此,在我看来,如果您将一个随机的十进制值(例如10.43245)存储在双精度类型中,并将其转换为long类型,则您将得到一些随机数,该随机数恰好是该二进制在long类型中的结果。然而,实际情况并非如此,您只是得到截断的小数和值10。

我的定义是错误的吗?或者c#在这里稍微改变了一下规则,给程序员更多他们可能期望从这样的强制转换中得到的东西(我也假设java可能也这样做,c++呢?)

强制转换是解释值还是只解释位?

它没有什么神奇的,但它必须做的不仅仅是直接转换位。例如,看看如何在汇编中将浮点数转换为整数:

(参考:http://www.codeproject.com/Articles/10679/Convert-Float-to-Int-using-Assembly-Language-Progr)

int ftoi(float flt)
{
    int i;
    _asm
    {
        mov  eax,flt; //loaded mem to acc
        rcl  eax,1;   //left shift acc to remove the sign
        mov  ebx,eax; //save the acc
        mov  edx,4278190080; //clear reg edx;
        and  eax,edx; //and acc to retrieve the exponent
        shr  eax,24;
        sub  eax,7fh; //subtract 7fh(127) to get the actual power 
        mov  edx,eax; //save acc val power
        mov  eax,ebx; //retrieve from ebx
        rcl  eax,8;     //trim the left 8 bits that contain the power
        mov  ebx,eax; //store
        mov  ecx, 1fh; //subtract 17 h
        sub  ecx,edx; 
        mov  edx,00000000h;
        cmp  ecx,0;
        je   loop2;
        shr  eax,1;
        or   eax,80000000h;        
loop1:    
        shr  eax,1; //shift (total bits - power bits);
        sub  ecx,1;
        add  edx,1;
        cmp  ecx,0;
        ja   loop1;
loop2:  
        mov  i, eax;        
//check sign +/-        
sign:
        mov  eax,flt;
        and  eax,80000000h;
        cmp  eax,80000000h;
        je     putsign;
    }
    return i;
putsign:
    return -i;
}

它移动,然后修剪,然后减去,然后再次移动。

在c#中,cast是显式转换。c#规范5.0的第6节讨论了转换。特别是第6.2.1节介绍了如何在语言中定义数字显式转换。作为一项规则,它们会尽可能地保留被转换的数值。

From MSDN:

当从双精度或浮点型值转换为整型值时,该值将被截断。如果产生的整数值在目标值的范围之外,则结果取决于溢出检查上下文。在已检查的上下文中,将抛出OverflowException,而在未检查的上下文中,结果是目标类型的未指定值。

因此,正如您所观察到的,当您将double转换为整型long时,您将获得截断的值。

强制类型转换的思想是通过转换将数据转换为另一种类型,几乎没有数据丢失。为了更专业,我们将引用强制类型转换和类型转换页面:

隐式转换:不需要特殊的语法,因为转换是类型安全的,不会丢失数据。示例包括从小整型到大整型的转换,以及从派生类到基类的转换。

我们在这里将重点放在"没有数据会丢失"。

我认为你在这里面临的问题是转换发生的地方,更重要的是,我们希望避免数据丢失的地方。

按照您的示例,将double 10.43245转换为long,我们面临的情况是,无论如何,都存在类型隐式数据丢失:10.43245是而不是 10,也不是您的随机数字字符串。double天生会存储更多小数点后的数字(特别是因为long存储的是实零),所以转换这个数字无论如何都会导致数据丢失。

说句题外话,这有点像把口语转换成摩尔斯电码。我们这样做是因为它很有用。但在这个过程中,我们仍然失去了一些有价值的语言品质。这是一种权衡,我们选择支持数据传输的重要性,而不是以自然和情感的方式传达语音的重要性。

因此,在我们接受了数据丢失的事实之后,我们必须选择在哪里丢失数据最有意义。我们可以通过保留位序之类的方式,把它以人类可读的形式丢掉,这就是你得到随机数字串的地方,或者我们可以把它以原始的二进制形式丢掉,尽可能多地保留"的数字",在这种情况下,就是地板:10。

从本质上讲,这只是第一批语言建立时所做的另一种简单的权衡,没有人想改变一件好事。它的方式,方式,方式 (long)10.43245解析为10更有用。

同样重要的是要记住,正如其他几个人指出的那样,这只是默认的行为。因为存在这种强制转换,所以没有理由认为不能将直接二进制值从一种类型转换为另一种类型。许多语言中都有一些方法,比如。net的BitConverter.DoubleToInt64Bits,可以完成这种一对一的位转换。它作为一个典型的用例是没有意义的,语言设计,特别是对于我们主要处理的高级语言,是一个使典型用例尽可能高效和有效的问题。

强制转换将截断双精度类型。您要查找的是c#中的BitConverter.DoubleToInt64Bits或Java中的Double.doubleToLongBits

long x = (long) doubleVal;

是显式转换的一个示例,它将尝试保留数值。

如果希望操作双精度型的位,可以使用不安全的代码进行转换:

long x;
unsafe
{
    x = *((long*) &val);
}
或者使用库函数BitConverter.DoubleToInt64Bits