Rust 基础入门 ——数值类型

news2024/11/18 0:41:34

数值类型

概述

数值类型 这里重点在于一些特殊的书写方式的格式,和几种特殊类型。除此以外,还包括一些常见的类型处理方式(这之中包括了一些问题处理和Rust 特有内容)。

细分之下为:

  1. 整数类型
    1. 重点问题:溢出
  2. 浮点数类型
    1. 重点类型:浮点数陷阱
  3. 运算
    1. 数字运算
    2. 位运算
  4. 序列
  5. 有理数和复数(我的图形计算和数据计算可算省事了>_<)

整形

首先的是 一些特殊的整形表示方式
在Rust中,我们是可以在数字中通过 下划线,来优化阅读体验的。毕竟一千万或者一亿还要自己数零也太让人眼花了吧。
具体实例如下:

let zry:i64 =  1000_0000_0000; // 这里的区分完全按照个人喜好,我喜欢四个一组正好按习惯区分,三位的还要转换一下不方便。

然后是取决于计算机处理器类型的特殊类型
在rust 中我们除去可以手动标定选择哪一种类型长度,还可以将这个过程交给处理器(CPU)。 这就是 size类型。 而在rust 中将类型简化,你可以认为 有几种类型,每种可以涉及正负号的类型都有有符号和无符号两种。
因此,我们可以推理出, 视架构而定的类型有 isize(有符号类型)usize(无符号类型)。

应用场景isizeusize 的主要应用场景是用作集合的索引。

最后说一下类型定义的统一形式有无符号 + 类型大小(位数)
无符号数表示数字只能取正数,而有符号则表示数字既可以取正数又可以取负数。 有符号数字以 补码 形式存储。

长度有符号类型无符号类型
8 位i8u8
16 位i16u16
32 位i32u32
64 位i64u64
128 位i128u128
视架构而定isizeusize

类型的存储范围计算规则
每个有符号类型规定的数字范围是 -(2n - 1) ~ 2n -
1
- 1,其中 n 是该定义形式的位长度。因此 i8 可存储数字范围是 -(27) ~ 27 - 1,即 -128 ~ 127。无符号类型可以存储的数字范围是 0 ~ 2n - 1,所以 u8 能够存储的数字为 0 ~ 28 - 1,即 0 ~ 255。

默认的整型
Rust 整型默认使用 i32,例如 let i = 1,那 i 就是 i32 类型,因此你可以首选它,同时该类型也往往是性能最好的。

多进制的表述方式
当然我们在C++ 中最喜欢的 0x00 清理内存时使用值。就有 十六进制表示法。
因此在Rust 中同样也有,针对不同的类型,有类似 C++ 的表示方式。具体展示如下:

数字字面量示例
十进制98_222
十六进制0xff
八进制0o77
二进制0b1111_0000
字节 (仅限于 u8)b'A'

整型溢出

在编程语言中,计算时常常会遇到一个问题,在长期运行后会遇到原有的空间不能存放运行结果的问题。即整型溢出。

在rust中,我们同样会遇到这个问题。 不过作为自称严格的开发语言,他会在调试模式下直接告诉你说:不行,这个东西你不能用。 (但要是你用 --release 那就没这优待了,都要投产了还给你做测试。开发人员总要有点自我要求啊)。

总结一下:

  • 在 debug 模式编译时,Rust 会检查整型溢出,若存在这些问题,则使程序在编译时 panic(崩溃,Rust 使用这个术语来表明程序因错误而退出)。
  • 使用 --release 参数进行 release 模式构建时,Rust 检测溢出。相反,当检测到整型溢出时,Rust 会按照补码循环溢出(two’s complement wrapping)的规则处理。
    • 简而言之,大于该类型最大值的数值会被补码转换成该类型能够支持的对应数字的最小值。比如在 u8 的情况下,256 变成 0,257 变成 1,
      • 依此类推。程序不会 panic,但是该变量的值可能不是你期望的值。
      • 依赖这种默认行为的代码都应该被认为是错误的代码

我们总不能提出问题不给解决方案的小喷子。所以解决这样的问题怎么做呢:
显示处理可能的溢出
使用标准库针对原始数字类型提供的这些方法:

  • 使用 wrapping_* 方法在所有模式下都按照补码循环溢出规则处理,例如 wrapping_add
fn main() {
    let a : u8 = 255;
    let b = a.wrapping_add(20);
    println!("{}", b);  // 19
}
  • 如果使用 checked_* 方法时发生溢出,则返回 None
fn main() {
    let a: u8 = 200;
    let b: u8 = 100;
    
    let result = a.checked_add(b);
    
    match result {
        Some(value) => println!("The sum is: {}", value),
        None => println!("Overflow occurred"),
    }
}
  • 使用 overflowing_* 方法返回该值和一个指示是否存在溢出的布尔值
fn main() {
    let a: u8 = 200;
    let b: u8 = 100;
    
    let (result, overflow) = a.overflowing_add(b);
    
    if overflow {
        println!("Overflow occurred");
    } else {
        println!("The sum is: {}", result);
    }
}
  • 使用 saturating_* 方法使值达到最小值或最大值
fn main() {
    let a: u8 = 250;
    let b: u8 = 10;
    
    let result = a.saturating_add(b);
    
    println!("The sum is: {}", result);
}

浮点数

在浮点数这一节,我们对类型本身的书写没有过多的解释,f32f64、是他可用的两种类型,没有无符号类型 在这里插入图片描述

接下来重点在于浮点陷阱:
所谓的浮点陷阱是指,在rust 中的浮点并非真正意义的完全精确,只是我们通过一系列的手段给了它足够的精度。也就是说超过了它的精度保证区,就会出现异常。

举例

fn main(){
    let zry = 2.0; //默认的是使用 f64格式
    let z: f32 = 3.0; // f32

    let abc: (f32, f32, f32) = (0.1, 0.2, 0.3);
    let xyz: (f64, f64, f64) = (0.1, 0.2, 0.3);

    println!("abc (f32)");
    println!("   0.1 + 0.2: {:x}", (abc.0 + abc.1).to_bits());
    println!("         0.3: {:x}", (abc.2).to_bits());
    println!();

    println!("xyz (f64)");
    println!("   0.1 + 0.2: {:x}", (xyz.0 + xyz.1).to_bits());
    println!("         0.3: {:x}", (xyz.2).to_bits());
    println!();

    assert!(abc.0 + abc.1 == abc.2);
    assert!(xyz.0 + xyz.1 == xyz.2);
}
abc (f32)
   0.1 + 0.2: 3e99999a
         0.3: 3e99999a

xyz (f64)
   0.1 + 0.2: 3fd3333333333334
         0.3: 3fd3333333333333

thread 'main' panicked at 'assertion failed: xyz.0 + xyz.1 == xyz.2',
➥ch2-add-floats.rs.rs:14:5
note: run with `RUST_BACKTRACE=1` environment variable to display
➥a backtrace

f32 类型做加法时,0.1 + 0.2 的结果是 3e99999a0.3 也是 3e99999a,因此 f32 下的 0.1 + 0.2 == 0.3 通过测试,但是到了 f64 类型时,结果就不一样了,因为 f64 精度高很多,因此在小数点非常后面发生了一点微小的变化,0.1 + 0.24 结尾,但是 0.33结尾,这个细微区别导致 f64 下的测试失败了,并且抛出了异常。

如果非要进行比较呢?可以考虑用这种方式 (0.1_f64 + 0.2 - 0.3).abs() < 0.00001 ,具体小于多少,取决于你对精度的需求。

为了避免上面说的两个陷阱,你需要遵守以下准则:

  • 避免在浮点数上测试相等性
  • 当结果在数学上可能存在未定义时,需要格外的小心

总结一下

浮点数由于底层格式的特殊性,导致了如果在使用浮点数时不够谨慎,就可能造成危险,有两个原因:

  1. 浮点数往往是你想要数字的近似表达 浮点数类型是基于二进制实现的,但是我们想要计算的数字往往是基于十进制,例如 0.1 在二进制上并不存在精确的表达形式,但是在十进制上就存在。这种不匹配性导致一定的歧义性,更多的,虽然浮点数能代表真实的数值,但是由于底层格式问题,它往往受限于定长的浮点数精度,如果你想要表达完全精准的真实数字,只有使用无限精度的浮点数才行

  2. 浮点数在某些特性上是反直觉的 例如大家都会觉得浮点数可以进行比较,对吧?是的,它们确实可以使用 >>= 等进行比较,但是在某些场景下,这种直觉上的比较特性反而会害了你。因为 f32f64 上的比较运算实现的是 std::cmp::PartialEq 特征(类似其他语言的接口),但是并没有实现 std::cmp::Eq 特征,但是后者在其它数值类型上都有定义,说了这么多,可能大家还是云里雾里,用一个例子来举例:

Rust 的 HashMap 数据结构,是一个 KV 类型的 Hash Map 实现,它对于 K 没有特定类型的限制,但是要求能用作 K 的类型必须实现了 std::cmp::Eq 特征,因此这意味着你无法使用浮点数作为 HashMapKey,来存储键值对,但是作为对比,Rust 的整数类型、字符串类型、布尔类型都实现了该特征,因此可以作为 HashMapKey

NaN 不是NULL 的新一种未定义代称

首先我们再次复习一下,rust 的类型是被包装之后的。所以其原生的带着一些可供操作的API接口。
因此我们可以有这样的写法:

fn main() {
  let x = (-42.0_f32).sqrt();
  assert_eq!(x, x);
}

执行结果:

Compiling playground v0.0.1 (/playground)  
Finished dev [unoptimized + debuginfo] target(s) in 0.55s  
Running `target/debug/playground`  
thread 'main' panicked at 'assertion failed: `(left == right)`  
left: `NaN`,  
right: `NaN`', src/main.rs:3:3  
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

对于数学上未定义的结果,例如对负数取平方根 -42.1.sqrt() ,会产生一个特殊的结果:Rust 的浮点数类型使用 NaN (not a number)来处理这些情况。

出于防御性编程的考虑,可以使用 is_nan() 等方法,可以用来判断一个数值是否是 NaN

fn main() {
    let x = (-42.0_f32).sqrt();
    if x.is_nan() {
        println!("未定义的数学行为")
    }
}

运算

数字运算

在rust 中的数字运算,依旧是传统意义上的加减乘除四则运算,然后是取模求余运算,
注意,不同类型是不可以混合计算的,否则会出现这样的结果报错![[Pasted image 20230629153008.png]]

位运算

Rust的运算基本上和其他语言一样

运算符说明
& 位与相同位置均为1时则为1,否则为0
| 位或相同位置只要有1时则为1,否则为0
^ 异或相同位置不相同则为1,相同则为0
! 位非把位中的0和1相互取反,即0置为1,1置为0
<< 左移所有位向左移动指定位数,右位补0
>> 右移所有位向右移动指定位数,带符号移动(正数补0,负数补1)
fn main() {
    // 二进制为00000010
    let a:i32 = 2;
    // 二进制为00000011
    let b:i32 = 3;

    println!("(a & b) value is {}", a & b);

    println!("(a | b) value is {}", a | b);

    println!("(a ^ b) value is {}", a ^ b);

    println!("(!b) value is {} ", !b);

    println!("(a << b) value is {}", a << b);

    println!("(a >> b) value is {}", a >> b);

    let mut a = a;
    // 注意这些计算符除了!之外都可以加上=进行赋值 (因为!=要用来判断不等于)
    a <<= b;
    println!("(a << b) value is {}", a);
}

序列

Rust 提供了一个非常简洁的方式,用来生成连续的数值,例如 1..5,生成从 1 到 4 的连续数字,不包含 5 ;1..=5,生成从 1 到 5 的连续数字,包含 5,它的用途很简单,常常用于循环中:

for i in 1..=5 {
    println!("{}",i);
}

最终程序输出:

1
2
3
4
5

序列只允许用于数字或字符类型,原因是:它们可以连续,同时编译器在编译期可以检查该序列是否为空,字符和数字值是 Rust 中仅有的可以用于判断是否为空的类型。如下是一个使用字符类型序列的例子:

for i in 'a'..='z' {
    println!("{}",i);
}

有理数和复数

rust 对有理数和复数进行了支持,但是目前不在标准库中,需要我们显式说明加载对应工具库。

按照以下步骤来引入 num 库:

  1. 创建新工程 cargo new complex-num && cd complex-num
  2. Cargo.toml 中的 [dependencies] 下添加一行 num = "0.4.0"
  3. src/main.rs 文件中的 main 函数替换为下面的代码
  4. 运行 cargo run
use num::complex::Complex;

 fn main() {
   let z = Complex { re: 2.1, im: -1.2 };
   let r = Complex::new(11.1, 22.2);
   let y = z + r;

   println!("{} + {}i", y.re, y.im)
 }

运行结果:![[Pasted image 20230629154223.png]]

downloaded部分是只在初次运行时使用的。后面就没有了。

总结

  1. 整数类型:

    • 有符号类型:i8i16i32i64i128isize,表示带有正负号的整数。
    • 无符号类型:u8u16u32u64u128usize,表示非负整数。
    • 整数类型的范围取决于位数,例如i8可以表示范围为-128到127的整数。
  2. 浮点数类型:

    • f32f64,分别表示32位和64位的浮点数。
    • 浮点数在表示小数时具有一定的精度限制,可能存在舍入误差。
  3. NaN类型

  4. 运算:

    1. 数值运算
    2. 位运算
      • 位与(&):相同位置均为1时结果为1,否则为0。
      • 位或(|):相同位置只要有1时结果为1,否则为0。
      • 异或(^):相同位置不相同结果为1,相同结果为0。
      • 位非(!):将位中的0和1相互取反。
      • 左移(<<):将所有位向左移动指定位数,右侧补0。
      • 右移(>>):将所有位向右移动指定位数,带符号移动(正数补0,负数补1)。
  5. 序列(Range):

    • 使用..表示左闭右开的序列,例如1..5表示从1到4的连续数字。
    • 使用..=表示左闭右闭的序列,例如1..=5表示从1到5的连续数字。
  6. 有理数和复数:

    • Rust的标准库不直接提供有理数和复数类型,但可以使用第三方库(num)来实现。(use num::complex::Complex;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/705551.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于PyQt5的图形化界面开发——天气应用

目录 0. 前言1. 注册心知天气2. 代码实现3. 其他PyQt5文章 0. 前言 本节使用PyQt5开发天气应用程序实现以下功能&#xff1a; 通过调用天气API获取实时天气数据&#xff0c;并在应用程序中显示当前城市的温度、天气状况、风速等信息。 操作系统&#xff1a;Windows10 专业版…

关于 3.0 和 2.0 的数据文件差异以及性能优化思路

如果需要对数据库性能优化&#xff0c;了解数据文件的存储方式和工作原理是必要的。 对于时序数据库&#xff08;Time Series Database&#xff09; TDengine 来说&#xff0c;在 2.x 版本中时序数据的保留策略是由keep和days这两个参数把控的。&#xff08;详情可见&#xff…

云原生——Kubenetes基础

❄️作者介绍&#xff1a;奇妙的大歪❄️ &#x1f380;个人名言&#xff1a;但行前路&#xff0c;不负韶华&#xff01;&#x1f380; &#x1f43d;个人简介&#xff1a;云计算网络运维专业人员&#x1f43d; 目录 一.什么是Kubernetes&#xff1f; 二.为什么你需要 Kubern…

markdown数学公式总结

行内与独行 行内公式&#xff1a;将公式插入到本行内&#xff0c;符号&#xff1a; 公式内容 公式内容 公式内容&#xff0c;如&#xff1a; x y z xyz xyz 独行公式&#xff1a;将公式插入到新的一行内&#xff0c;并且居中&#xff0c;符号&#xff1a; 公式内容 公式内容 公…

RISC-V IDE MRS使用笔记(十):嵌入式编程开发技巧汇总

RISC-V IDE MRS使用笔记(十)&#xff1a;嵌入式编程开发技巧汇总 MRS常见嵌入式开发技巧: Q1&#xff1a;如何修改程序编译生成库&#xff1f; A1&#xff1a;在工具栏中点击活动工程的编译配置按钮&#xff0c;在Build Artifact的Tab页面指定目标类型&#xff0c;选中为Stat…

uniapp仿浙北惠生活微信小程序

最近给公司写了一个内部微信小程序&#xff0c;功能比较简单&#xff0c;之前是用微信小程序原声写的&#xff0c;一遍看文档一边写&#xff0c;js&#xff0c;wxml&#xff0c;wxcc&#xff0c;json分在不同文件的写法很不习惯&#xff0c;于是花了两天用uniapp重写了一遍&…

思维导图对我生活以及工作的帮助(用户投稿)

作为一名白领&#xff0c;我每天都面临着各种各样的工作和生活压力。 为了更好地应对这些挑战&#xff0c;我开始尝试使用思维导图来帮助自己更好地组织和管理各种信息和任务。其中一款非常优秀的软件就是ProcessOn思维导图&#xff0c;它为我的工作和生活带来了很多便利和帮助…

Spring Boot|启动图案修改ASCII字符画

效果图 实现 在项目目录的resources文件夹中新建一个banner.txt&#xff0c;将内容放入即可&#xff1a; // _ooOoo_ // // o8888888o // // …

华为ENSP配置无线AC-网关模式

1、配置交换机以及AC的接口为trunk 交换机 vlan b 10 20 # interface GigabitEthernet0/0/1port link-type trunkport trunk allow-pass vlan 10 20 # # interface GigabitEthernet0/0/2port link-type trunkport trunk pvid vlan 10port trunk allow-pass vlan 10 20 # …

Linux系统安装QQ最新版 2023-06-30

腾讯在2023-05-30更新了linux版的qq&#xff0c;这次界面终于不再复古&#xff0c;好看多了。 安装步骤&#xff1a; 1.进入官网&#xff0c;寻找合适的安装包下载 https://im.qq.com/linuxqq/index.shtml 选择跟自己计算机匹配的版本&#xff0c;一般都是X86&#xff0c;如…

JAVA-编程基础-05-方法的定义、调用、重载

Lison <dreamlison163.com>, v1.0.0, 2023.03.22 JAVA-编程基础-05-方法的定义、调用、重载 文章目录 JAVA-编程基础-05-方法的定义、调用、重载方法的概念什么是方法 方法的定义和调用无参数方法定义和调用方法调用过程图解 带参数方法的定义和调用带返回值方法的定义和…

Ureport 多数据集合关联显示

ureport 使用较少&#xff0c;目前单位这边有项目正好使用到了&#xff0c;也遇到不少坑&#xff0c;和其他报表稍微有些不同&#xff0c;在这里记录一下&#xff0c;为大家排一下坑 目前需求是要求数据以这样显示 1、创建数据源 1.1 B2、C1、D2 、C4、C2 各使用了不同的数据…

chatGPT流式回复是怎么实现的

chatGPT流式回复是怎么实现的 先说结论&#xff1a; chatGPT的流式回复用的就是HTTP请求方案中的server-send-event流式接口&#xff0c;也就是服务端向客户端推流数据。 那eventStream流式接口怎么实现呢&#xff0c;下面就进入正题&#xff01; 文章目录 chatGPT流式回复…

UE5ControlRig脚部IK匹配地面(FootIK)的简易实现

UE的Foot IK可以有多套方案&#xff0c;老的有动画蓝图中用TwoBoneIK&#xff0c;新的做法有ControlRig的BasicIK、FullBodyIK。博主针对FootIK做一个简单的验证&#xff0c;因此使用ControlRigTwoBoneIK的做法&#xff0c;并做一下记录&#xff0c;之前做过UE FootIK的大佬可以…

小白到运维工程师自学之路 第四十四集 (mariadb高可用集群故障转移)

一、概述 故障转移是指在集群中某个节点发生故障时&#xff0c;自动将服务转移到其他正常节点上的 过程。在MariaDB高可用集群中&#xff0c;通常使用主从复制的方式来实现故障转移。其中一个 节点被指定为主节点&#xff0c;负责处理所有的写操作和部分读操作&#xff0c;其…

通过无模型多代理强化学习掌握Stratego游戏

Stratego是一款流行的双人不完美信息棋盘游戏。由于其复杂性源于其巨大的游戏树、在不完善的信息下进行决策以及一开始的分段部署阶段&#xff0c;Stratego对人工智能&#xff08;AI&#xff09;构成了挑战。以前的计算机程序充其量只在业余水平上运行。 Perolat等人引入了一种…

Crontab的sendmail邮件发送引发磁盘问题

一.背景 大清早的收到一台服务器的内存占用高达98%的钉钉告警。 回想了一下&#xff0c;这台服务器没跑什么业务啊&#xff0c;咋从凌晨1点就开始告警呢? 赶紧登录服务器&#xff0c;top 按照内存倒序一下&#xff0c;没发现占用特别大的进程。内存监控图如下: 但是发现了很…

Windows环境本地部署Oracle11g r2实操手册

前言&#xff1a;一直在做其他测试&#xff0c;貌似都忘了Windows环境oracle安装&#xff0c;这是一个很早很早的安装记录了&#xff0c;放上来做个备录给到大家参考。 环境&#xff1a; &#xff08;都是常规系统及工具&#xff0c;官网度娘搜索即可下载测试学习&#xff09;…

通过无代码CDC工具实现Oracle实时同步到Mysql

Oracle数据实时同步需求 现代企业对于数据实时同步的需求日益迫切&#xff0c;特别是在涉及Oracle数据库的情况下。实时同步数据对企业来说至关重要&#xff0c;因为它可以确保不同系统之间的数据始终保持一致性和最新性&#xff0c;提供准确的决策支持和及时的业务反应。 针…

Leetcode-每日一题【725.分隔链表】

题目 给你一个头结点为 head 的单链表和一个整数 k &#xff0c;请你设计一个算法将链表分隔为 k 个连续的部分。每部分的长度应该尽可能的相等&#xff1a;任意两部分的长度差距不能超过 1 。这可能会导致有些部分为 null 。这 k 个部分应该按照在链表中出现的顺序排列&#…