第一 数字
计算机正如其名,从诞生之日,就是为了进行快速地数值计算,主要用于炮弹的弹道计算。
为了搞清楚计算机的工作原理,我们首先要了解数字(尤其是整数)在计算机中是怎么表示的。
编码方式
我们知道计算机中数据都是以0、1组成的二进制进行编码、计算、存储、传输的。二进制编码又分为:原码、补码、反码几种编码方式。
所谓原码就是二进制定点表示法,即最高位为符号位,“0”表示正,“1”表示负,其余位表示数值的大小。
反码表示法规定:
正数的反码与其原码相同;负数的反码是对正数逐位取反,符号位保持为1.
补码表示法规定:正数的补码与其原码相同;负数的补码是在其反码的末位加1。
在计算机系统中,数值一律用**补码**来表示和存储。原因在于,使用补码,可以将符号位和数值域统一处理;同时,加法和减法也可以统一处理。
// 二进制 10001
println!("0b{:08b}",17i8);
// 八进制 21
println!("0o{:08o}",17i8);
// 十六进制 11
println!("0x{:08x}",17i8);
println!("0b{:08b}",0i8);
println!("0b{:08b}",1i8);
println!("0b{:08b}",-1i8);
println!("0b{:08b}",i8::MAX);
println!("0b{:08b}",-2i8);
println!("0b{:08b}",17u8);
println!("0o{:08o}",17u8);
println!("0x{:08x}",17u8);
### Excel表格列名(26进制,golang版本)
// ColumnNameToNumber provides a function to convert Excel sheet column name
// (case-insensitive) to int. The function returns an error if column name
// incorrect.
//
// Example:
//
// excelize.ColumnNameToNumber("AK") // returns 37, nil
func ColumnNameToNumber(name string) (int, error) {
if len(name) == 0 {
return -1, newInvalidColumnNameError(name)
}
col := 0
multi := 1
for i := len(name) - 1; i >= 0; i-- {
r := name[i]
if r >= 'A' && r <= 'Z' {
col += int(r-'A'+1) * multi
} else if r >= 'a' && r <= 'z' {
col += int(r-'a'+1) * multi
} else {
return -1, newInvalidColumnNameError(name)
}
multi *= 26
}
if col > MaxColumns {
return -1, ErrColumnNumber
}
return col, nil
}
// ColumnNumberToName provides a function to convert the integer to Excel
// sheet column title.
//
// Example:
//
// excelize.ColumnNumberToName(37) // returns "AK", nil
func ColumnNumberToName(num int) (string, error) {
if num < MinColumns || num > MaxColumns {
return "", ErrColumnNumber
}
var col string
for num > 0 {
col = string(rune((num-1)%26+65)) + col
num = (num - 1) / 26
}
return col, nil
}
### Excel表格列名(26进制,rust版本)
const MIN_COLUMNS: usize = 1;
const MAX_COLUMNS: usize = 16384;
/// The column number convert to column name
fn column_number_to_name(num: usize) -> std::string::String {
if num < MIN_COLUMNS || num > MAX_COLUMNS {
return "".to_string();
}
let mut ret = num;
let mut col = std::string::String::new();
while ret > 0 {
let ch = ((ret - 1) % 26 + 65) as u8;
ret = (ret - 1) / 26;
col.insert(0, ch as char);
}
col
}
///
/// column name convert to column number
fn column_name_to_number(name: std::string::String) -> usize {
let len = name.len();
if len == 0 {
return 0;
}
let mut col = 0;
let bytes = name.as_bytes();
let mut i = len - 1;
let mut multi = 1;
loop {
let ch = bytes[i];
if ch >= b'A' && ch <= b'Z' {
col += multi * (ch - b'A' + 1) as usize;
} else if ch >= b'a' && ch <= b'z' {
col += multi * (ch - b'a' + 1) as usize;
} else {
return 0;
}
if i < 1 {
break;
}
i -= 1;
multi *= 26;
}
if col > MAX_COLUMNS {
return 0;
}
col
}
编码转换
原码 => 反码 =>补码
原码=>补码
(1)正整数的补码是其二进制表示,与原码相同。
(2)求负整数的补码,将其原码除符号位外的所有位取反(0变1,1变0,符号位为1不变)后加1。
补码=>原码
(1)如果补码的符号位为“0”,表示是一个正数,其原码就是补码。
(2)如果补码的符号位为“1”,表示是一个负数,那么求给定的这个补码的补码就是要求的原码。
简而言之:正整数的原码、反码、补码相同;负整数的补码就是在原码的基础上符号位不变,其他位取反(得到反码),然后加1。
以-1为例(假设类型为i8),-1的原码为10000001,反码为11111110,补码为11111111。由此可见反码作为原码和补码相互转换的中间码。
### 力扣实战:1009. 十进制整数的反码
原码通过与掩码进行异或(XOR)运算得到“反码”,注意:这里的“反码”和真正的反码定义是不一样的。
pub fn bitwise_complement(n: i32) -> i32 {
if n == 0 {
return 1;
}
let mut num = n;
let mut mark = 1;
let mut high_bit = 0;
while num > 0 {
num >>= 1;
high_bit += 1;
}
//dbg!(high_bit);
let mark = match (high_bit == 31) {
true => i32::MAX - 1,
false => (1 << high_bit) - 1,
};
n ^ mark
}
fn main() {
let n = 911;
let complement = bitwise_complement(n);
println!("{:032b}",n);
println!("{:032b}",complement);
}
大数
### 大整型
数值多大才能称得上天文数字呢?天文数字又有何用呢?
100的阶乘(100!)有多少个零呢?
use num::bigint::{BigInt, ToBigInt};
/// 计算x的阶乘,即x!
fn factorial(x: i32) -> BigInt {
if let Some(mut facatorial) = 1.to_bigint() {
for i in 1..(x + 1) {
facatorial *= i;
}
facatorial
} else {
panic!("Failed to calculate factorial!");
}
}
use num::BigUint;
let x = BigUint::parse_bytes(b"786fe10f87d8ddfeeeea9a4a49e63388e3b2a9e1b0a794907908f6123dbf6c6a", 16).unwrap();
### 质数