1.JSbinarysystem//Binary(二进制)//以0b或0B开头varx=0b10000000000000000000000000000000000;//2147483648vary=0B00000000011111111111111111111118转换:7为正数/8/6//0数的原码//负数:负号+正数的原码//补码源码表示isnotavalue(10).toString(2)//1010(-10).toString(2)//-1010//Octal(八进制数字系统)//以0开头,ECMAScript6支持0ovarn=0755;//493vare=0o755;//493ECMAScript6specification//Decimal(十进制)//以0开头,但后面的8会被当作八进制varl=0888;//888十进制varo=0777;//511八进制//十六进制(Hexadecimal)//以0x或0X开头0x123456789ABCDEF//819855292164869000XA//10二、原码、反码、补码我们先来看看原码、反码、补码1和-1。然后我们用这两个数分别解释原码、反码、补码。原码:一个数的二进制表示有符号数:最高位作为符号位,0表示+,1表示-无符号数:即无符号位的反码:正数和+0,反码为原码本身的负数和-0,符号位根据原码保持不变,其余数位逐位取反,1换0,0替换为1的补码:正数和+0是原代码本身的补码,负数和-0先计算,然后补码加1得到补码关键点:JavaScript负数显示是减号+原码(理论上方便查看),如parseInt(-10).toString(2)二进制显示输出为-1010数据以补码形式存入内存(方便转换),原码和补码代码在运行过程中被转换。二进制元素实际上是一个补码操作。补码是通过补码计算得到的,然后转化为反码,再转化为原码(这里不是减1,也不是加1)。补码的符号位是一个实数值,只是因为补码的最高位与原码的符号位完全相同,所以可以认为是一个符号位。S2]补码+[S1]补码[S2-S1]补码=[S2]补码+[-S1]补码示例计算-2原码:10000010-2反码:11111101-2补码:11111110计算-2+(-2),用补码计算,舍去最高位的进位。11111110+11111110=11111100//补码-1=11111011//补码10000100=-4//原码溢出varuint8=newUint8Array(1);uint8[0]=256;console.log(uint8[0])//0Uint8Array是一个无符号的8位视图,范围是0~255,最大11111111256是100000000,所以只有最后8位,所以是0uint8[0]=-1;console.log(uint8[0])//255-1在计算机中存储的是补码,补码为11111111,根据无符号位,即255正溢出而上面的栗子是负溢出,第一个是正溢出,??第二个是负溢出正溢出:最小值+余数-1负溢出:最大值-余数+1varint8=newInt8Array(1);//-128~127int8[0]=128;console.log(int8[0])//-128=-128+128%127-1int8[0]=-129;console.log(int8[0])//127=127-(-129%-128)+1解决溢出错误int8c=newUint8ClampedArray(1)//通过边界值处理溢出int8c[0]=256console.log(int8c[0])//255int8c[0]=-1console.log(int8c[0])//0注意:为什么是-128~127三、关于文本字符编码1、ASCII码1960年代,美国为英文字符制定了一套字符编码二进制数字之间的关系已经统一规定。这称为ASCII,至今仍在使用。ASCII码一共规定了128种字符编码。例如空格SPACE为32(二进制00100000),大写字母A为65(二进制01000001)。这128个符号(包括32个不能打印出来的控制符号)只占一个字节的后7位,首位统一规定为02。非ASCII编码的英文用128个符号编码。但是要表示其他语言,128个符号是不够的。所以不同的通行证根据自己的语言扩展了编码。然而,这里又出现了新的问题。不同的国家有不同的字母,同一个数字所代表的字符也可能不同。例如,130在法语编码中代表é,但在希伯来语编码中代表字母Gimel(?)。编码方式有多种,同一个二进制数可以解释为不同的符号。因此,如果要打开一个文本文件,就必须知道它的编码方式,否则如果用错误的编码方式进行解释,就会出现乱码。如果有编码,请在其中包含所有符号。那么每个符号都有唯一的编码,那么乱码问题就没有了。这是UnicodeUnicode是一个可以容纳超过100万个符号的大型集合。每个符号的编码都不同。例如U+0639代表阿拉伯字母Ain,U+0041代表英文大写字母A,U+4E25代表汉字Yan。具体的符号对应表可以查询unicode.org,或者特殊汉字对应表。4、Unicode的问题Unicode只规定了符号的二进制编码,并没有规定二进制编码应该如何存储。如何区分Unicode和ASCII?计算机怎么知道三个字节代表一个符号,而不是分别代表三个符号呢?英文字母只用一个字节表示。如果Unicode统一规定每个符号用三四个字节表示,那么每个英文字母前都要有两到三个字节的0,这对存储是一种极大的浪费。5、UTF-8在互联网上的普及,强烈要求统一的编码方式。UTF-8是Internet上使用最广泛的Unicode实现。其他实现包括UTF-16(字符用两个字节或四个字节表示)和UTF-32(字符用四个字节表示),但这些大多不在Internet上使用。重复一下,这里的关系是UTF-8是Unicode的一种实现。UTF-8最大的特点之一就是它是一种变长编码方式。它可以用1~4个字节来表示一个符号,字节长度可以根据不同的符号而改变。具体规则如下:对于单字节符号,字节的第一位设置为0,后7位为符号Unicode编码。所以对于英文字母,UTF-8编码和ASCII编码是一样的。对于一个n字节的符号(n>1),第一个字节的前n位全部设为1,第n+1位设为0,后面字节的前两位全部设为10其余未提及的二进制位均为该符号的Unicode编码。下表总结了编码规则,字母x表示可用的编码位。根据上表,解读UTF-8编码就很简单了。如果一个字节的第一位是0,那么这个字节就是一个单独的字符;如果第一位为1,则一行中有多少个1表示当前字符占多少字节。例子:还是以汉字颜为例,演示如何实现UTF-8编码。严格的Unicode是4E25(100111000100101)。根据上表可以发现4E25在第三行(00000800-0000FFFF)范围内,所以严格的UTF-8编码需要三个字节,即格式为1110xxxx10xxxxxx10xxxxxx。然后从最后一个二进制位开始,按照格式从后往前填入x,多余的位补0。这样严格的UTF-8编码就是111001001011100010100101,转成16进制是E4B8A5。6.字节顺序小端和大端以汉字颜为例,Unicode编码为4E25,需要用两个字节存储,一个字节为4E,另一个字节为25。存储时,4E在前25在最后,这是Bigendian方法;25在前,4E在后,即Littleendian。第一个字节在前是“大端”(Bigendian),第二个字节在前是“小端”(Littleendian)。那么自然而然,计算机怎么知道某个文件使用的是哪种编码方式呢?Unicode规范定义了在每个文件的前面加上一个代表编码顺序的字符。这个字符的名字叫做“zerowidthno-breakspace”(零宽度不间断空格),用FEFF表示。那正好是两个字节,FF比FE大1。如果一个文本文件的前两个字节是FEFF,说明该文件采用大头方式;如果前两个字节是FFFE,说明文件使用小头方式。例如,打开“记事本”程序notepad.exe,新建一个文本文件,内容为严格单词,依次以ANSI、Unicode、Unicodebigendian和UTF-8编码方式保存。然后,利用文本编辑软件UltraEdit中的“十六进制函数”,观察文件内部的编码方式。ANSI:文件的编码是两个字节的D1CF,正好是严格的GB2312编码,也暗示GB2312是以bulk格式存储的。Unicode:编码为四个字节FFFE254E,其中FFFE表示存放在小头中,真正的编码是4E25。Unicodebigendian:编码为四个字节FEFF4E25,其中FEFF表示以大端方式存储。UTF-8:编码为六个字节EFBBBFE4B8A5,前三个字节EFBBBF表示这是UTF-8编码,后三个E4B8A5是严格的特定编码,其存储顺序和编码顺序是一致的。注:UTF-8编码不存在字节序问题(因为字节序只影响同时处理两个以上字节的编码方式,如UTF-16/UTF-32,而UTF-8是基于单word部分),所以UTF-8BOM只是用来标记文件的编码方式,可以加也可以不加。7、关于URL转码网页的URL只能包含合法字符。合法字符分为两类。URL元字符:分号(;)、逗号(,)、斜杠(/)、问号(?)、冒号(:)、at(@)、&、等号(=)、加号(+)、美元符号($)、井号(#)语义字符:a-z、A-Z、0-9、连字符(-)、下划线(_)、点(.)、感叹号(!)、波浪号(~)、星号(*)、单引号(')、圆括号(())除上述字符外,其他字符出现在URL中时必须进行转义。规则是根据操作系统默认的编码,将每个字节转换成百位。分号(%)后跟两个大写十六进制字母。例如,在UTF-8操作系统上,URLhttps://www.baidu.com/s?ie=UTF-8&wd=中国,汉字“中国”在URL中不是合法字符,所以它由浏览器自动转换。进入https://www.baidu.com/s?ie=UTF-8&wd=%E4%B8%AD%E5%9B%BD。其中,“中”转换为%E4%B8%AD,“国”转换为%E5%9B%BD。这是因为“中国”和“国家”的UTF-8编码分别是E4B8AD和E59BBD。在每个字节前加一个百分号就构成了URL编码。8.encodeURI和encodeURIComponentencodeURI()方法用于对整个URL进行转码。它的参数是一个代表整个URL的字符串。它转义元字符和语义字符以外的字符。encodeURIComponent()方法用于对URL的组成部分进行转码,除语义字符外的所有字符都会被转码,即元字符也会被转码。因此,它不能用于转码整个URL。它接受一个参数,即URL的一个片段。9.js转换console.log('0'.charCodeAt())//"48"十进制console.log('0'.charCodeAt().toString(16))//"30"十六进制console.log(0x0030.toString(10))//"48"十进制console.log(String.fromCharCode(48))//"0"console.log('万'.charCodeAt().toString(16))//"4e07"十六进制console.log(String.fromCharCode(0x4e07))//"10,000"console.log('10,000'.charCodeAt().toString(2))//"100111000000111"二进制控制台。log(String.fromCharCode(0b100111000000111))//"10000"4、位运算需要注意:负数以补码的形式参与按位与运算。1、与运算符(&)按位与运算符(&)对运算涉及的两个数据进行与运算,即当两个位同时为1时,结果为1,否则结果为0.运算规则如下:0&0=00&1=01&0=01&1=1比如3&5的运算结果如下:0000001100000101=00000001因此,3&5的值是1,比如3&-53的二进制是0000,00115的二进制是0000,0101-5的二进制需要用5的补码表示,即11111011和运算0000001111111011=00000011=3.根据最后一位是0还是1来判断,如果是0就是偶数,如果是1就是奇数。因此可以用if((i&1)===0)代替if(i%2===0)判断a是否为偶数。(2)清零如果要清零一个单元,即使它的二进制位全部为0,只要与一个全为0的值进行与运算,结果也为0。(3)是否是2的n次方//(x&x-1)===0console.log((2&2-1)===0)//true(4)average防止溢出//平均值,防溢出函数avg(x,y){return(x&y)+((x^y)>>1);}2.bitwiseor||的区别operatorand&是如果对应的任意一个操作数为1,则结果为1//1的二进制表示为:00000000000000000000000000000001//3的二进制表示为:00000000000000000000000000000011//----------------------------//1的二进制表示|3is:00000000000000000000000000000011console.log(1|3)//3四舍五入为整数1.3|0//1-1.9|0//-13。按位异或^^如果两个运算位对应的只有一个1,则结果为1,其他为0//1的二进制表示为:00000000000000000000000000000001//3的二进制表示为:00000000000000000000000000000011//-----------------------------//2的二进制表示为:00000000000000000000000000000010console.log(1^3)//2的异或运算具有以下性质:交换律:(a^b)^c==a^(b^c)结合律:(a+b)^c==a^b+b^c对于任意数x,存在x^x=0,x^0=x自反性:a^b^b=a^0=a;//判断赋值if(x===a){x=b}else{x=a}//等价于下面的x=a^b^x4。BitwiseNOT~~operator就是把位取反,1变0,0变1,也就是求二进制的补码。//1的二进制表示是:00000000000000000000000000000001//3的二进制表示是:00000000000000000000000000000011//-------------------------------//1的补码二进制表示:11111111111111111111111111111110//由于第一位(符号位)为1,所以这个数为负数。JavaScript内部使用补码表示负数,即需要将这个数减1,再取倒数,再加一个负号,得到负数对应的十进制值。//----------------------------//1减1的反码:11111111111111111111111111111101//反码取反:00000000000000000000000000000010//表示为十进制加减号:-2console.log(~1)//-2简单记忆:一个数与其取反的值相加等于-1。5、左移<<<<左移指定次数,其移动规则:舍去高位,低位加0//1的二进制表示为:00000000000000000000000000000001//----------------------------//2的二进制表示为:00000000000000000000000000000010console.log(1<<1)//26.Signedrightshift>>>>向右移动指定位数。向右移出的位被丢弃,最左边的位被复制以填充左侧。由于新的最左边的位总是和以前一样,符号位没有改变。这就是为什么它被称为“符号通信”。//1的二进制表示是:00000000000000000000000000000001//------------------------------//的二进制表示0IS:0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000lyleg(1>>1)//0//0//1>>>1//-1表示为:11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111往。>1)//-17。无符号右移>>>>>>右移指定位数。右移的位被丢弃,左边补0。因为符号位变为0,所以结果总是非负的。(译注:即使右移0位,结果也不是负数。)对于非负数,有符号和无符号右移总是返回相同的结果。例如,9>>>2得到2和9>>2是一样的。TODO阮一峰Base64Note浮点数的存储和计算0.1+0.2不等于0.3?为什么JavaScript会有这种“骚”操作?参考链接阮一峰的字符编码笔记:ASCII、Unicode和UTF-8URL编解码使用详解为什么补码的符号位可以参与原码运算、反码运算、补码运算和overflow为什么是8-bitsignedtypevalue范围是-128~127个字符,web开发中的编码和二进制(一)JavaScript中的二进制
