|
|
|
|
<!DOCTYPE HTML>
|
|
|
|
|
<html lang="zh-CN" class="light" dir="ltr">
|
|
|
|
|
<head>
|
|
|
|
|
<!-- Book generated using mdBook -->
|
|
|
|
|
<meta charset="UTF-8">
|
|
|
|
|
<title>数值类型 - Rust语言圣经(Rust Course)</title>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Custom HTML head -->
|
|
|
|
|
|
|
|
|
|
<meta name="description" content="">
|
|
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
|
|
|
<meta name="theme-color" content="#ffffff">
|
|
|
|
|
|
|
|
|
|
<link rel="icon" href="../../favicon.svg">
|
|
|
|
|
<link rel="shortcut icon" href="../../favicon.png">
|
|
|
|
|
<link rel="stylesheet" href="../../css/variables.css">
|
|
|
|
|
<link rel="stylesheet" href="../../css/general.css">
|
|
|
|
|
<link rel="stylesheet" href="../../css/chrome.css">
|
|
|
|
|
<link rel="stylesheet" href="../../css/print.css" media="print">
|
|
|
|
|
|
|
|
|
|
<!-- Fonts -->
|
|
|
|
|
<link rel="stylesheet" href="../../FontAwesome/css/font-awesome.css">
|
|
|
|
|
<link rel="stylesheet" href="../../fonts/fonts.css">
|
|
|
|
|
|
|
|
|
|
<!-- Highlight.js Stylesheets -->
|
|
|
|
|
<link rel="stylesheet" href="../../highlight.css">
|
|
|
|
|
<link rel="stylesheet" href="../../tomorrow-night.css">
|
|
|
|
|
<link rel="stylesheet" href="../../ayu-highlight.css">
|
|
|
|
|
|
|
|
|
|
<!-- Custom theme stylesheets -->
|
|
|
|
|
<link rel="stylesheet" href="../../theme/style.css">
|
|
|
|
|
|
|
|
|
|
</head>
|
|
|
|
|
<body class="sidebar-visible no-js">
|
|
|
|
|
<div id="body-container">
|
|
|
|
|
<!-- Provide site root to javascript -->
|
|
|
|
|
<script>
|
|
|
|
|
var path_to_root = "../../";
|
|
|
|
|
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
|
|
|
|
<script>
|
|
|
|
|
try {
|
|
|
|
|
var theme = localStorage.getItem('mdbook-theme');
|
|
|
|
|
var sidebar = localStorage.getItem('mdbook-sidebar');
|
|
|
|
|
|
|
|
|
|
if (theme.startsWith('"') && theme.endsWith('"')) {
|
|
|
|
|
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
|
|
|
|
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
|
|
|
|
}
|
|
|
|
|
} catch (e) { }
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<!-- Set the theme before any content is loaded, prevents flash -->
|
|
|
|
|
<script>
|
|
|
|
|
var theme;
|
|
|
|
|
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
|
|
|
|
if (theme === null || theme === undefined) { theme = default_theme; }
|
|
|
|
|
var html = document.querySelector('html');
|
|
|
|
|
html.classList.remove('light')
|
|
|
|
|
html.classList.add(theme);
|
|
|
|
|
var body = document.querySelector('body');
|
|
|
|
|
body.classList.remove('no-js')
|
|
|
|
|
body.classList.add('js');
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
|
|
|
|
|
|
|
|
|
|
<!-- Hide / unhide sidebar before it is displayed -->
|
|
|
|
|
<script>
|
|
|
|
|
var body = document.querySelector('body');
|
|
|
|
|
var sidebar = null;
|
|
|
|
|
var sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
|
|
|
|
|
if (document.body.clientWidth >= 1080) {
|
|
|
|
|
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
|
|
|
|
sidebar = sidebar || 'visible';
|
|
|
|
|
} else {
|
|
|
|
|
sidebar = 'hidden';
|
|
|
|
|
}
|
|
|
|
|
sidebar_toggle.checked = sidebar === 'visible';
|
|
|
|
|
body.classList.remove('sidebar-visible');
|
|
|
|
|
body.classList.add("sidebar-" + sidebar);
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
|
|
|
|
<div class="sidebar-scrollbox">
|
|
|
|
|
<ol class="chapter"><li class="chapter-item affix "><a href="../../about-book.html">关于本书</a></li><li class="chapter-item affix "><a href="../../into-rust.html">进入 Rust 编程世界</a></li><li class="chapter-item affix "><a href="../../first-try/sth-you-should-not-do.html">避免从入门到放弃</a></li><li class="chapter-item affix "><a href="../../community.html">社区和锈书</a></li><li class="spacer"></li><li class="chapter-item affix "><a href="../../some-thoughts.html">Xobserve: 一切皆可观测</a></li><li class="chapter-item affix "><a href="../../beat-ai.html">BeatAI: 工程师 AI 入门圣经</a></li><li class="chapter-item affix "><li class="part-title">Rust 语言基础学习</li><li class="spacer"></li><li class="chapter-item "><a href="../../first-try/intro.html"><strong aria-hidden="true">1.</strong> 寻找牛刀,以便小试</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../first-try/installation.html"><strong aria-hidden="true">1.1.</strong> 安装 Rust 环境</a></li><li class="chapter-item "><a href="../../first-try/editor.html"><strong aria-hidden="true">1.2.</strong> 墙推 VSCode!</a></li><li class="chapter-item "><a href="../../first-try/cargo.html"><strong aria-hidden="true">1.3.</strong> 认识 Cargo</a></li><li class="chapter-item "><a href="../../first-try/hello-world.html"><strong aria-hidden="true">1.4.</strong> 不仅仅是 Hello world</a></li><li class="chapter-item "><a href="../../first-try/slowly-downloading.html"><strong aria-hidden="true">1.5.</strong> 下载依赖太慢了?</a></li></ol></li><li class="chapter-item expanded "><a href="../../basic/intro.html"><strong aria-hidden="true">2.</strong> Rust 基础入门</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../basic/variable.html"><strong aria-hidden="true">2.1.</strong> 变量绑定与解构</a></li><li class="chapter-item expanded "><a href="../../basic/base-type/index.html"><strong aria-hidden="true">2.2.</strong> 基本类型</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../basic/base-type/numbers.html" class="active"><strong aria-hidden="true">2.2.1.</strong> 数值类型</a></li><li class="chapter-item "><a href="../../basic/base-type/char-bool.html"><strong aria-hidden="true">2.2.2.</strong> 字符、布尔、单元类型</a></li><li class="chapter-item "><a href="../../basic/base-type/statement-expression.html"><strong aria-hidden="true">2.2.3.</strong> 语句与表达式</a></li><li class="chapter-item "><a href="../../basic/base-type/function.html"><strong aria-hidden="true">2.2.4.</strong> 函数</a></li></ol></li><li class="chapter-item "><a href="../../basic/ownership/index.html"><strong aria-hidden="true">2.3.</strong> 所有权和借用</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../basic/ownership/ownership.html"><strong aria-hidden="true">2.3.1.</strong> 所有权</a></li><li class="chapter-item "><a href="../../basic/ownership/borrowing.html"><strong aria-hidden="true">2.3.2.</strong> 引用与借用</a></li></ol></li><li class="chapter-item "><a href="../../basic/compound-type/intro.html"><strong aria-hidden="true">2.4.</strong> 复合类型</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../basic/compound-type/string-slice.html"><strong aria-hidden="true">2.4.1.</strong> 字符串与切片</a></li><li class="chapter-item "><a href="../../basic/compound-type/tuple.html"><strong aria-hidden="true">2.4.2.</strong> 元组</a></li><li class="chapter-item "><a href="../../basic/compound-type/struct.html"><strong aria-hidden="true">2.4.3.</strong> 结构体</a></li><li class="chapter-item "><a href="../../basic/compound-type/enum.html"><strong aria-hidden="true">2.4.4.</strong> 枚举</a></li><li class="chapter-item "><a href="../../basic/compound-type/array.html"><strong aria-h
|
|
|
|
|
</div>
|
|
|
|
|
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
|
|
|
|
|
<div class="sidebar-resize-indicator"></div>
|
|
|
|
|
</div>
|
|
|
|
|
</nav>
|
|
|
|
|
|
|
|
|
|
<!-- Track and set sidebar scroll position -->
|
|
|
|
|
<script>
|
|
|
|
|
var sidebarScrollbox = document.querySelector('#sidebar .sidebar-scrollbox');
|
|
|
|
|
sidebarScrollbox.addEventListener('click', function(e) {
|
|
|
|
|
if (e.target.tagName === 'A') {
|
|
|
|
|
sessionStorage.setItem('sidebar-scroll', sidebarScrollbox.scrollTop);
|
|
|
|
|
}
|
|
|
|
|
}, { passive: true });
|
|
|
|
|
var sidebarScrollTop = sessionStorage.getItem('sidebar-scroll');
|
|
|
|
|
sessionStorage.removeItem('sidebar-scroll');
|
|
|
|
|
if (sidebarScrollTop) {
|
|
|
|
|
// preserve sidebar scroll position when navigating via links within sidebar
|
|
|
|
|
sidebarScrollbox.scrollTop = sidebarScrollTop;
|
|
|
|
|
} else {
|
|
|
|
|
// scroll sidebar to current active section when navigating via "next/previous chapter" buttons
|
|
|
|
|
var activeSection = document.querySelector('#sidebar .active');
|
|
|
|
|
if (activeSection) {
|
|
|
|
|
activeSection.scrollIntoView({ block: 'center' });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<div id="page-wrapper" class="page-wrapper">
|
|
|
|
|
|
|
|
|
|
<div class="page">
|
|
|
|
|
<div id="menu-bar-hover-placeholder"></div>
|
|
|
|
|
<div id="menu-bar" class="menu-bar sticky">
|
|
|
|
|
<div class="left-buttons">
|
|
|
|
|
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
|
|
|
|
<i class="fa fa-bars"></i>
|
|
|
|
|
</label>
|
|
|
|
|
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
|
|
|
|
<i class="fa fa-paint-brush"></i>
|
|
|
|
|
</button>
|
|
|
|
|
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
|
|
|
|
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
|
|
|
|
|
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
|
|
|
|
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
|
|
|
|
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
|
|
|
|
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
|
|
|
|
</ul>
|
|
|
|
|
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
|
|
|
|
<i class="fa fa-search"></i>
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<h1 class="menu-title">Rust语言圣经(Rust Course)</h1>
|
|
|
|
|
|
|
|
|
|
<div class="right-buttons">
|
|
|
|
|
<a href="../../print.html" title="Print this book" aria-label="Print this book">
|
|
|
|
|
<i id="print-button" class="fa fa-print"></i>
|
|
|
|
|
</a>
|
|
|
|
|
<a href="https://github.com/sunface/rust-course" title="Git repository" aria-label="Git repository">
|
|
|
|
|
<i id="git-repository-button" class="fa fa-github"></i>
|
|
|
|
|
</a>
|
|
|
|
|
<a href="https://github.com/sunface/rust-course/edit/main/src/basic/base-type/numbers.md" title="Suggest an edit" aria-label="Suggest an edit">
|
|
|
|
|
<i id="git-edit-button" class="fa fa-edit"></i>
|
|
|
|
|
</a>
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div id="search-wrapper" class="hidden">
|
|
|
|
|
<form id="searchbar-outer" class="searchbar-outer">
|
|
|
|
|
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
|
|
|
|
</form>
|
|
|
|
|
<div id="searchresults-outer" class="searchresults-outer hidden">
|
|
|
|
|
<div id="searchresults-header" class="searchresults-header"></div>
|
|
|
|
|
<ul id="searchresults">
|
|
|
|
|
</ul>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
|
|
|
|
<script>
|
|
|
|
|
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
|
|
|
|
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
|
|
|
|
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
|
|
|
|
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
|
|
|
|
});
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<div id="content" class="content">
|
|
|
|
|
<!-- Page table of contents -->
|
|
|
|
|
<div class="sidetoc"><nav class="pagetoc"></nav></div>
|
|
|
|
|
<main>
|
|
|
|
|
<h1 id="数值类型"><a class="header" href="#数值类型">数值类型</a></h1>
|
|
|
|
|
<p>我朋友有一个领导(读者:你朋友?黑人问号)说过一句话:所有代码就是 0 和 1 ,简单的很。咱不评价这句话的正确性,但是计算机底层由 01 组成倒是真的。</p>
|
|
|
|
|
<p>计算机和数值关联在一起的时间,远比我们想象的要长,因此数值类型可以说是有计算机以来就有的类型,下面内容将深入讨论 Rust 的数值类型以及相关的运算符。</p>
|
|
|
|
|
<p>Rust 使用一个相对传统的语法来创建整数(<code>1</code>,<code>2</code>,...)和浮点数(<code>1.0</code>,<code>1.1</code>,...)。整数、浮点数的运算和你在其它语言上见过的一致,都是通过常见的运算符来完成。</p>
|
|
|
|
|
<blockquote>
|
|
|
|
|
<p>不仅仅是数值类型,Rust 也允许在复杂类型上定义运算符,例如在自定义类型上定义 <code>+</code> 运算符,这种行为被称为运算符重载,Rust 具体支持的可重载运算符见<a href="https://course.rs/appendix/operators.html#%E8%BF%90%E7%AE%97%E7%AC%A6">附录 B</a>。</p>
|
|
|
|
|
</blockquote>
|
|
|
|
|
<h2 id="整数类型"><a class="header" href="#整数类型">整数类型</a></h2>
|
|
|
|
|
<p><strong>整数</strong>是没有小数部分的数字。之前使用过的 <code>i32</code> 类型,表示有符号的 32 位整数( <code>i</code> 是英文单词 <em>integer</em> 的首字母,与之相反的是 <code>u</code>,代表无符号 <code>unsigned</code> 类型)。下表显示了 Rust 中的内置的整数类型:</p>
|
|
|
|
|
<div class="table-wrapper"><table><thead><tr><th>长度</th><th>有符号类型</th><th>无符号类型</th></tr></thead><tbody>
|
|
|
|
|
<tr><td>8 位</td><td><code>i8</code></td><td><code>u8</code></td></tr>
|
|
|
|
|
<tr><td>16 位</td><td><code>i16</code></td><td><code>u16</code></td></tr>
|
|
|
|
|
<tr><td>32 位</td><td><code>i32</code></td><td><code>u32</code></td></tr>
|
|
|
|
|
<tr><td>64 位</td><td><code>i64</code></td><td><code>u64</code></td></tr>
|
|
|
|
|
<tr><td>128 位</td><td><code>i128</code></td><td><code>u128</code></td></tr>
|
|
|
|
|
<tr><td>视架构而定</td><td><code>isize</code></td><td><code>usize</code></td></tr>
|
|
|
|
|
</tbody></table>
|
|
|
|
|
</div>
|
|
|
|
|
<p>类型定义的形式统一为:<code>有无符号 + 类型大小(位数)</code>。<strong>无符号数</strong>表示数字只能取正数和 0,而<strong>有符号</strong>则表示数字可以取正数、负数还有 0。就像在纸上写数字一样:当要强调符号时,数字前面可以带上正号或负号;然而,当很明显确定数字为正数时,就不需要加上正号了。有符号数字以<a href="https://en.wikipedia.org/wiki/Two%27s_complement">补码</a>形式存储。</p>
|
|
|
|
|
<p>每个有符号类型规定的数字范围是 -(2<sup>n - 1</sup>) ~ 2<sup>n -
|
|
|
|
|
1</sup> - 1,其中 <code>n</code> 是该定义形式的位长度。因此 <code>i8</code> 可存储数字范围是 -(2<sup>7</sup>) ~ 2<sup>7</sup> - 1,即 -128 ~ 127。无符号类型可以存储的数字范围是 0 ~ 2<sup>n</sup> - 1,所以 <code>u8</code> 能够存储的数字为 0 ~ 2<sup>8</sup> - 1,即 0 ~ 255。</p>
|
|
|
|
|
<p>此外,<code>isize</code> 和 <code>usize</code> 类型取决于程序运行的计算机 CPU 类型: 若 CPU 是 32 位的,则这两个类型是 32 位的,同理,若 CPU 是 64 位,那么它们则是 64 位。</p>
|
|
|
|
|
<p>整形字面量可以用下表的形式书写:</p>
|
|
|
|
|
<div class="table-wrapper"><table><thead><tr><th>数字字面量</th><th>示例</th></tr></thead><tbody>
|
|
|
|
|
<tr><td>十进制</td><td><code>98_222</code></td></tr>
|
|
|
|
|
<tr><td>十六进制</td><td><code>0xff</code></td></tr>
|
|
|
|
|
<tr><td>八进制</td><td><code>0o77</code></td></tr>
|
|
|
|
|
<tr><td>二进制</td><td><code>0b1111_0000</code></td></tr>
|
|
|
|
|
<tr><td>字节 (仅限于 <code>u8</code>)</td><td><code>b'A'</code></td></tr>
|
|
|
|
|
</tbody></table>
|
|
|
|
|
</div>
|
|
|
|
|
<p>这么多类型,有没有一个简单的使用准则?答案是肯定的, Rust 整型默认使用 <code>i32</code>,例如 <code>let i = 1</code>,那 <code>i</code> 就是 <code>i32</code> 类型,因此你可以首选它,同时该类型也往往是性能最好的。<code>isize</code> 和 <code>usize</code> 的主要应用场景是用作集合的索引。</p>
|
|
|
|
|
<h4 id="整型溢出"><a class="header" href="#整型溢出">整型溢出</a></h4>
|
|
|
|
|
<p>假设有一个 <code>u8</code> ,它可以存放从 0 到 255 的值。那么当你将其修改为范围之外的值,比如 256,则会发生<strong>整型溢出</strong>。关于这一行为 Rust 有一些有趣的规则:当在 debug 模式编译时,Rust 会检查整型溢出,若存在这些问题,则使程序在编译时 <em>panic</em>(崩溃,Rust 使用这个术语来表明程序因错误而退出)。</p>
|
|
|
|
|
<p>在当使用 <code>--release</code> 参数进行 release 模式构建时,Rust <strong>不</strong>检测溢出。相反,当检测到整型溢出时,Rust 会按照补码循环溢出(<em>two’s complement wrapping</em>)的规则处理。简而言之,大于该类型最大值的数值会被补码转换成该类型能够支持的对应数字的最小值。比如在 <code>u8</code> 的情况下,256 变成 0,257 变成 1,依此类推。程序不会 <em>panic</em>,但是该变量的值可能不是你期望的值。依赖这种默认行为的代码都应该被认为是错误的代码。</p>
|
|
|
|
|
<p>要显式处理可能的溢出,可以使用标准库针对原始数字类型提供的这些方法:</p>
|
|
|
|
|
<ul>
|
|
|
|
|
<li>使用 <code>wrapping_*</code> 方法在所有模式下都按照补码循环溢出规则处理,例如 <code>wrapping_add</code></li>
|
|
|
|
|
<li>如果使用 <code>checked_*</code> 方法时发生溢出,则返回 <code>None</code> 值</li>
|
|
|
|
|
<li>使用 <code>overflowing_*</code> 方法返回该值和一个指示是否存在溢出的布尔值</li>
|
|
|
|
|
<li>使用 <code>saturating_*</code> 方法,可以限定计算后的结果不超过目标类型的最大值或低于最小值,例如:</li>
|
|
|
|
|
</ul>
|
|
|
|
|
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
|
|
|
|
|
</span><span class="boring">fn main() {
|
|
|
|
|
</span>assert_eq!(100u8.saturating_add(1), 101);
|
|
|
|
|
assert_eq!(u8::MAX.saturating_add(127), u8::MAX);
|
|
|
|
|
<span class="boring">}</span></code></pre></pre>
|
|
|
|
|
<p>下面是一个演示<code>wrapping_*</code>方法的示例:</p>
|
|
|
|
|
<pre><pre class="playground"><code class="language-rust edition2021">fn main() {
|
|
|
|
|
let a : u8 = 255;
|
|
|
|
|
let b = a.wrapping_add(20);
|
|
|
|
|
println!("{}", b); // 19
|
|
|
|
|
}</code></pre></pre>
|
|
|
|
|
<h2 id="浮点类型"><a class="header" href="#浮点类型">浮点类型</a></h2>
|
|
|
|
|
<p><strong>浮点类型数字</strong> 是带有小数点的数字,在 Rust 中浮点类型数字也有两种基本类型: <code>f32</code> 和 <code>f64</code>,分别为 32 位和 64 位大小。默认浮点类型是 <code>f64</code>,在现代的 CPU 中它的速度与 <code>f32</code> 几乎相同,但精度更高。</p>
|
|
|
|
|
<p>下面是一个演示浮点数的示例:</p>
|
|
|
|
|
<pre><pre class="playground"><code class="language-rust edition2021">fn main() {
|
|
|
|
|
let x = 2.0; // f64
|
|
|
|
|
|
|
|
|
|
let y: f32 = 3.0; // f32
|
|
|
|
|
}</code></pre></pre>
|
|
|
|
|
<p>浮点数根据 <code>IEEE-754</code> 标准实现。<code>f32</code> 类型是单精度浮点型,<code>f64</code> 为双精度。</p>
|
|
|
|
|
<h4 id="浮点数陷阱"><a class="header" href="#浮点数陷阱">浮点数陷阱</a></h4>
|
|
|
|
|
<p>浮点数由于底层格式的特殊性,导致了如果在使用浮点数时不够谨慎,就可能造成危险,有两个原因:</p>
|
|
|
|
|
<ol>
|
|
|
|
|
<li>
|
|
|
|
|
<p><strong>浮点数往往是你想要数字的近似表达</strong>
|
|
|
|
|
浮点数类型是基于二进制实现的,但是我们想要计算的数字往往是基于十进制,例如 <code>0.1</code> 在二进制上并不存在精确的表达形式,但是在十进制上就存在。这种不匹配性导致一定的歧义性,更多的,虽然浮点数能代表真实的数值,但是由于底层格式问题,它往往受限于定长的浮点数精度,如果你想要表达完全精准的真实数字,只有使用无限精度的浮点数才行</p>
|
|
|
|
|
</li>
|
|
|
|
|
<li>
|
|
|
|
|
<p><strong>浮点数在某些特性上是反直觉的</strong>
|
|
|
|
|
例如大家都会觉得浮点数可以进行比较,对吧?是的,它们确实可以使用 <code>></code>,<code>>=</code> 等进行比较,但是在某些场景下,这种直觉上的比较特性反而会害了你。因为 <code>f32</code> , <code>f64</code> 上的比较运算实现的是 <code>std::cmp::PartialEq</code> 特征(类似其他语言的接口),但是并没有实现 <code>std::cmp::Eq</code> 特征,但是后者在其它数值类型上都有定义,说了这么多,可能大家还是云里雾里,用一个例子来举例:</p>
|
|
|
|
|
</li>
|
|
|
|
|
</ol>
|
|
|
|
|
<p>Rust 的 <code>HashMap</code> 数据结构,是一个 KV 类型的 Hash Map 实现,它对于 <code>K</code> 没有特定类型的限制,但是要求能用作 <code>K</code> 的类型必须实现了 <code>std::cmp::Eq</code> 特征,因此这意味着你无法使用浮点数作为 <code>HashMap</code> 的 <code>Key</code>,来存储键值对,但是作为对比,Rust 的整数类型、字符串类型、布尔类型都实现了该特征,因此可以作为 <code>HashMap</code> 的 <code>Key</code>。</p>
|
|
|
|
|
<p>为了避免上面说的两个陷阱,你需要遵守以下准则:</p>
|
|
|
|
|
<ul>
|
|
|
|
|
<li>避免在浮点数上测试相等性</li>
|
|
|
|
|
<li>当结果在数学上可能存在未定义时,需要格外的小心</li>
|
|
|
|
|
</ul>
|
|
|
|
|
<p>来看个小例子:</p>
|
|
|
|
|
<pre><pre class="playground"><code class="language-rust edition2021">fn main() {
|
|
|
|
|
// 断言0.1 + 0.2与0.3相等
|
|
|
|
|
assert!(0.1 + 0.2 == 0.3);
|
|
|
|
|
}</code></pre></pre>
|
|
|
|
|
<p>你可能以为,这段代码没啥问题吧,实际上它会 <em>panic</em>(程序崩溃,抛出异常),因为二进制精度问题,导致了 0.1 + 0.2 并不严格等于 0.3,它们可能在小数点 N 位后存在误差。</p>
|
|
|
|
|
<p>那如果非要进行比较呢?可以考虑用这种方式 <code>(0.1_f64 + 0.2 - 0.3).abs() < 0.00001</code> ,具体小于多少,取决于你对精度的需求。</p>
|
|
|
|
|
<p>讲到这里,相信大家基本已经明白了,为什么操作浮点数时要格外的小心,但是还不够,下面再来一段代码,直接震撼你的灵魂:</p>
|
|
|
|
|
<pre><pre class="playground"><code class="language-rust edition2021">fn main() {
|
|
|
|
|
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);
|
|
|
|
|
}</code></pre></pre>
|
|
|
|
|
<p>运行该程序,输出如下:</p>
|
|
|
|
|
<pre><code class="language-console">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
|
|
|
|
|
</code></pre>
|
|
|
|
|
<p>仔细看,对 <code>f32</code> 类型做加法时,<code>0.1 + 0.2</code> 的结果是 <code>3e99999a</code>,<code>0.3</code> 也是 <code>3e99999a</code>,因此 <code>f32</code> 下的 <code>0.1 + 0.2 == 0.3</code> 通过测试,但是到了 <code>f64</code> 类型时,结果就不一样了,因为 <code>f64</code> 精度高很多,因此在小数点非常后面发生了一点微小的变化,<code>0.1 + 0.2</code> 以 <code>4</code> 结尾,但是 <code>0.3</code> 以<code>3</code>结尾,这个细微区别导致 <code>f64</code> 下的测试失败了,并且抛出了异常。</p>
|
|
|
|
|
<p>是不是<strong>blow your mind away</strong>? 没关系,在本书的后续章节中类似的直击灵魂的地方还很多,这就是敢号称 <code>Rust语言圣经(Rust Course)</code> 的底气!</p>
|
|
|
|
|
<h4 id="nan"><a class="header" href="#nan">NaN</a></h4>
|
|
|
|
|
<p>对于数学上未定义的结果,例如对负数取平方根 <code>-42.1.sqrt()</code> ,会产生一个特殊的结果:Rust 的浮点数类型使用 <code>NaN</code> (not a number) 来处理这些情况。</p>
|
|
|
|
|
<p><strong>所有跟 <code>NaN</code> 交互的操作,都会返回一个 <code>NaN</code></strong>,而且 <code>NaN</code> 不能用来比较,下面的代码会崩溃:</p>
|
|
|
|
|
<pre><pre class="playground"><code class="language-rust edition2021">fn main() {
|
|
|
|
|
let x = (-42.0_f32).sqrt();
|
|
|
|
|
assert_eq!(x, x);
|
|
|
|
|
}</code></pre></pre>
|
|
|
|
|
<p>出于防御性编程的考虑,可以使用 <code>is_nan()</code> 等方法,可以用来判断一个数值是否是 <code>NaN</code> :</p>
|
|
|
|
|
<pre><pre class="playground"><code class="language-rust edition2021">fn main() {
|
|
|
|
|
let x = (-42.0_f32).sqrt();
|
|
|
|
|
if x.is_nan() {
|
|
|
|
|
println!("未定义的数学行为")
|
|
|
|
|
}
|
|
|
|
|
}</code></pre></pre>
|
|
|
|
|
<h2 id="数字运算"><a class="header" href="#数字运算">数字运算</a></h2>
|
|
|
|
|
<p>Rust 支持所有数字类型的基本数学运算:加法、减法、乘法、除法和取模运算。下面代码各使用一条 <code>let</code> 语句来说明相应运算的用法:</p>
|
|
|
|
|
<pre><pre class="playground"><code class="language-rust edition2021">fn main() {
|
|
|
|
|
// 加法
|
|
|
|
|
let sum = 5 + 10;
|
|
|
|
|
|
|
|
|
|
// 减法
|
|
|
|
|
let difference = 95.5 - 4.3;
|
|
|
|
|
|
|
|
|
|
// 乘法
|
|
|
|
|
let product = 4 * 30;
|
|
|
|
|
|
|
|
|
|
// 除法
|
|
|
|
|
let quotient = 56.7 / 32.2;
|
|
|
|
|
|
|
|
|
|
// 求余
|
|
|
|
|
let remainder = 43 % 5;
|
|
|
|
|
}</code></pre></pre>
|
|
|
|
|
<p>这些语句中的每个表达式都使用了数学运算符,并且计算结果为一个值,然后绑定到一个变量上。<a href="https://course.rs/appendix/operators.html#%E8%BF%90%E7%AE%97%E7%AC%A6">附录 B</a> 中给出了 Rust 提供的所有运算符的列表。</p>
|
|
|
|
|
<p>再来看一个综合性的示例:</p>
|
|
|
|
|
<pre><pre class="playground"><code class="language-rust edition2021">fn main() {
|
|
|
|
|
// 编译器会进行自动推导,给予twenty i32的类型
|
|
|
|
|
let twenty = 20;
|
|
|
|
|
// 类型标注
|
|
|
|
|
let twenty_one: i32 = 21;
|
|
|
|
|
// 通过类型后缀的方式进行类型标注:22是i32类型
|
|
|
|
|
let twenty_two = 22i32;
|
|
|
|
|
|
|
|
|
|
// 只有同样类型,才能运算
|
|
|
|
|
let addition = twenty + twenty_one + twenty_two;
|
|
|
|
|
println!("{} + {} + {} = {}", twenty, twenty_one, twenty_two, addition);
|
|
|
|
|
|
|
|
|
|
// 对于较长的数字,可以用_进行分割,提升可读性
|
|
|
|
|
let one_million: i64 = 1_000_000;
|
|
|
|
|
println!("{}", one_million.pow(2));
|
|
|
|
|
|
|
|
|
|
// 定义一个f32数组,其中42.0会自动被推导为f32类型
|
|
|
|
|
let forty_twos = [
|
|
|
|
|
42.0,
|
|
|
|
|
42f32,
|
|
|
|
|
42.0_f32,
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
// 打印数组中第一个值,并控制小数位为2位
|
|
|
|
|
println!("{:.2}", forty_twos[0]);
|
|
|
|
|
}</code></pre></pre>
|
|
|
|
|
<h2 id="位运算"><a class="header" href="#位运算">位运算</a></h2>
|
|
|
|
|
<p>Rust 的位运算基本上和其他语言一样</p>
|
|
|
|
|
<div class="table-wrapper"><table><thead><tr><th>运算符</th><th>说明</th></tr></thead><tbody>
|
|
|
|
|
<tr><td>& 位与</td><td>相同位置均为1时则为1,否则为0</td></tr>
|
|
|
|
|
<tr><td>| 位或</td><td>相同位置只要有1时则为1,否则为0</td></tr>
|
|
|
|
|
<tr><td>^ 异或</td><td>相同位置不相同则为1,相同则为0</td></tr>
|
|
|
|
|
<tr><td>! 位非</td><td>把位中的0和1相互取反,即0置为1,1置为0</td></tr>
|
|
|
|
|
<tr><td><< 左移</td><td>所有位向左移动指定位数,右位补0</td></tr>
|
|
|
|
|
<tr><td>>> 右移</td><td>所有位向右移动指定位数,带符号移动(正数补0,负数补1)</td></tr>
|
|
|
|
|
</tbody></table>
|
|
|
|
|
</div>
|
|
|
|
|
<pre><pre class="playground"><code class="language-rust edition2021">fn main() {
|
|
|
|
|
// 无符号8位整数,二进制为00000010
|
|
|
|
|
let a: u8 = 2; // 也可以写 let a: u8 = 0b_0000_0010;
|
|
|
|
|
|
|
|
|
|
// 二进制为00000011
|
|
|
|
|
let b: u8 = 3;
|
|
|
|
|
|
|
|
|
|
// {:08b}:左高右低输出二进制01,不足8位则高位补0
|
|
|
|
|
println!("a value is {:08b}", a);
|
|
|
|
|
|
|
|
|
|
println!("b value is {:08b}", b);
|
|
|
|
|
|
|
|
|
|
println!("(a & b) value is {:08b}", a & b);
|
|
|
|
|
|
|
|
|
|
println!("(a | b) value is {:08b}", a | b);
|
|
|
|
|
|
|
|
|
|
println!("(a ^ b) value is {:08b}", a ^ b);
|
|
|
|
|
|
|
|
|
|
println!("(!b) value is {:08b}", !b);
|
|
|
|
|
|
|
|
|
|
println!("(a << b) value is {:08b}", a << b);
|
|
|
|
|
|
|
|
|
|
println!("(a >> b) value is {:08b}", a >> b);
|
|
|
|
|
|
|
|
|
|
let mut a = a;
|
|
|
|
|
// 注意这些计算符除了!之外都可以加上=进行赋值 (因为!=要用来判断不等于)
|
|
|
|
|
a <<= b;
|
|
|
|
|
println!("(a << b) value is {:08b}", a);
|
|
|
|
|
}</code></pre></pre>
|
|
|
|
|
<h2 id="序列range"><a class="header" href="#序列range">序列(Range)</a></h2>
|
|
|
|
|
<p>Rust 提供了一个非常简洁的方式,用来生成连续的数值,例如 <code>1..5</code>,生成从 1 到 4 的连续数字,不包含 5 ;<code>1..=5</code>,生成从 1 到 5 的连续数字,包含 5,它的用途很简单,常常用于循环中:</p>
|
|
|
|
|
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
|
|
|
|
|
</span><span class="boring">fn main() {
|
|
|
|
|
</span>for i in 1..=5 {
|
|
|
|
|
println!("{}",i);
|
|
|
|
|
}
|
|
|
|
|
<span class="boring">}</span></code></pre></pre>
|
|
|
|
|
<p>最终程序输出:</p>
|
|
|
|
|
<pre><code class="language-console">1
|
|
|
|
|
2
|
|
|
|
|
3
|
|
|
|
|
4
|
|
|
|
|
5
|
|
|
|
|
</code></pre>
|
|
|
|
|
<p>序列只允许用于数字或字符类型,原因是:它们可以连续,同时编译器在编译期可以检查该序列是否为空,字符和数字值是 Rust 中仅有的可以用于判断是否为空的类型。如下是一个使用字符类型序列的例子:</p>
|
|
|
|
|
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
|
|
|
|
|
</span><span class="boring">fn main() {
|
|
|
|
|
</span>for i in 'a'..='z' {
|
|
|
|
|
println!("{}",i);
|
|
|
|
|
}
|
|
|
|
|
<span class="boring">}</span></code></pre></pre>
|
|
|
|
|
<h2 id="使用-as-完成类型转换"><a class="header" href="#使用-as-完成类型转换">使用 As 完成类型转换</a></h2>
|
|
|
|
|
<p>Rust 中可以使用 As 来完成一个类型到另一个类型的转换,其最常用于将原始类型转换为其他原始类型,但是它也可以完成诸如将指针转换为地址、地址转换为指针以及将指针转换为其他指针等功能。你可以在<a href="../../advance/into-types/converse.html">这里</a>了解更多相关的知识。</p>
|
|
|
|
|
<h2 id="有理数和复数"><a class="header" href="#有理数和复数">有理数和复数</a></h2>
|
|
|
|
|
<p>Rust 的标准库相比其它语言,准入门槛较高,因此有理数和复数并未包含在标准库中:</p>
|
|
|
|
|
<ul>
|
|
|
|
|
<li>有理数和复数</li>
|
|
|
|
|
<li>任意大小的整数和任意精度的浮点数</li>
|
|
|
|
|
<li>固定精度的十进制小数,常用于货币相关的场景</li>
|
|
|
|
|
</ul>
|
|
|
|
|
<p>好在社区已经开发出高质量的 Rust 数值库:<a href="https://crates.io/crates/num">num</a>。</p>
|
|
|
|
|
<p>按照以下步骤来引入 <code>num</code> 库:</p>
|
|
|
|
|
<ol>
|
|
|
|
|
<li>创建新工程 <code>cargo new complex-num && cd complex-num</code></li>
|
|
|
|
|
<li>在 <code>Cargo.toml</code> 中的 <code>[dependencies]</code> 下添加一行 <code>num = "0.4.0"</code></li>
|
|
|
|
|
<li>将 <code>src/main.rs</code> 文件中的 <code>main</code> 函数替换为下面的代码</li>
|
|
|
|
|
<li>运行 <code>cargo run</code></li>
|
|
|
|
|
</ol>
|
|
|
|
|
<pre><pre class="playground"><code class="language-rust edition2021">use num::complex::Complex;
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
let a = Complex { re: 2.1, im: -1.2 };
|
|
|
|
|
let b = Complex::new(11.1, 22.2);
|
|
|
|
|
let result = a + b;
|
|
|
|
|
|
|
|
|
|
println!("{} + {}i", result.re, result.im)
|
|
|
|
|
}</code></pre></pre>
|
|
|
|
|
<h2 id="总结"><a class="header" href="#总结">总结</a></h2>
|
|
|
|
|
<p>之前提到了过 Rust 的数值类型和运算跟其他语言较为相似,但是实际上,除了语法上的不同之外,还是存在一些差异点:</p>
|
|
|
|
|
<ul>
|
|
|
|
|
<li><strong>Rust 拥有相当多的数值类型</strong>. 因此你需要熟悉这些类型所占用的字节数,这样就知道该类型允许的大小范围以及你选择的类型是否能表达负数</li>
|
|
|
|
|
<li><strong>类型转换必须是显式的</strong>. Rust 永远也不会偷偷把你的 16bit 整数转换成 32bit 整数</li>
|
|
|
|
|
<li><strong>Rust 的数值上可以使用方法</strong>. 例如你可以用以下方法来将 <code>13.14</code> 取整:<code>13.14_f32.round()</code>,在这里我们使用了类型后缀,因为编译器需要知道 <code>13.14</code> 的具体类型</li>
|
|
|
|
|
</ul>
|
|
|
|
|
<p>数值类型的讲解已经基本结束,接下来,来看看字符和布尔类型。</p>
|
|
|
|
|
<h2 id="课后练习"><a class="header" href="#课后练习">课后练习</a></h2>
|
|
|
|
|
<blockquote>
|
|
|
|
|
<p><a href="https://practice-zh.course.rs/basic-types/numbers.html">Rust By Practice</a>,支持代码在线编辑和运行,并提供详细的<a href="https://github.com/sunface/rust-by-practice/blob/master/solutions/basic-types/numbers.md">习题解答</a>。</p>
|
|
|
|
|
</blockquote>
|
|
|
|
|
|
|
|
|
|
<div id="giscus-container"></div>
|
|
|
|
|
</main>
|
|
|
|
|
|
|
|
|
|
<nav class="nav-wrapper" aria-label="Page navigation">
|
|
|
|
|
<!-- Mobile navigation buttons -->
|
|
|
|
|
<a rel="prev" href="../../basic/base-type/index.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
|
|
|
|
<i class="fa fa-angle-left"></i>
|
|
|
|
|
</a>
|
|
|
|
|
|
|
|
|
|
<a rel="next prefetch" href="../../basic/base-type/char-bool.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
|
|
|
|
<i class="fa fa-angle-right"></i>
|
|
|
|
|
</a>
|
|
|
|
|
|
|
|
|
|
<div style="clear: both"></div>
|
|
|
|
|
</nav>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
|
|
|
|
<a rel="prev" href="../../basic/base-type/index.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
|
|
|
|
<i class="fa fa-angle-left"></i>
|
|
|
|
|
</a>
|
|
|
|
|
|
|
|
|
|
<a rel="next prefetch" href="../../basic/base-type/char-bool.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
|
|
|
|
<i class="fa fa-angle-right"></i>
|
|
|
|
|
</a>
|
|
|
|
|
</nav>
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
window.playground_copyable = true;
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<script src="../../ace.js"></script>
|
|
|
|
|
<script src="../../editor.js"></script>
|
|
|
|
|
<script src="../../mode-rust.js"></script>
|
|
|
|
|
<script src="../../theme-dawn.js"></script>
|
|
|
|
|
<script src="../../theme-tomorrow_night.js"></script>
|
|
|
|
|
|
|
|
|
|
<script src="../../elasticlunr.min.js"></script>
|
|
|
|
|
<script src="../../mark.min.js"></script>
|
|
|
|
|
<script src="../../searcher.js"></script>
|
|
|
|
|
|
|
|
|
|
<script src="../../clipboard.min.js"></script>
|
|
|
|
|
<script src="../../highlight.js"></script>
|
|
|
|
|
<script src="../../book.js"></script>
|
|
|
|
|
|
|
|
|
|
<script type="text/javascript" charset="utf-8">
|
|
|
|
|
var pagePath = "basic/base-type/numbers.md"
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Custom JS scripts -->
|
|
|
|
|
<script src="../../assets/custom.js"></script>
|
|
|
|
|
<script src="../../assets/bigPicture.js"></script>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
</body>
|
|
|
|
|
</html>
|