今日の内容
- u128の限界
- 累乗を求める、パワー!!
- 限界を超える?!「uint」
はじめに
前回は「クレート」について学び、randクレートを使って、迷路と数当てゲームを作成しました。今回は、とあるクレートを使ってu128を超えたいと思います。
u128の限界
Rustでは標準で以下の符号なし整数型があります。
- u8
- u16
- u32
- u64
- u128
この中で一番大きい整数を扱えるのは、u128になります。ここで、u128が表すことのできる最大の値を確認してみましょう。DAY6で学習しましたが、型の範囲は「i8::MIN」や「i8::MAX」で取得することができました。
fn main(){
println!("u128: {} ~ {}", u128::MIN, u128::MAX);
}
/******** 実行結果 ********
u128: 0 ~ 340282366920938463463374607431768211455
*************************/
とても大きい整数ですが、これは「2¹²⁸-1」に等しいです。
Rustでは、pow関数を使用して累乗を計算することができます。
以下は、2の0乗から10乗までを求めるプログラムです。
fn main(){
let a:u128 = 2;
let mut x:u32 = 0;
for i in 0..11 {
println!("{}^{:<2} = {}", a, x, a.pow(x));
x += 1;
}
}
/******** 実行結果 ********
2^0 = 1
2^1 = 2
2^2 = 4
2^3 = 8
2^4 = 16
2^5 = 32
2^6 = 64
2^7 = 128
2^8 = 256
2^9 = 512
2^10 = 1024
*************************/
aのx乗を求めるときの、pow関数を使った書き方は、以下のようになります。
a.pow(x)
ここで、u8, u16, u32, u64, u128について、それぞれの最小と最大を実際に計算して表示するプログラムを作ってみましょう。
fn main() {
let a:u128 = 2;
let mut x:u32 = 8;
let arr = ["u8", "u16", "u32", "u64", "u128"];
for i in arr {
println!("{}: 0 ~ {}", i, a.pow(x) - 1);
x = x * 2;
}
}
/******** 実行結果 ********
u8: 0 ~ 255
u16: 0 ~ 65535
u32: 0 ~ 4294967295
u64: 0 ~ 18446744073709551615
thread 'main' panicked at C:\Users\username\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib/rustlib/src/rust\library\core\src\num\mod.rs:1196:5:
attempt to multiply with overflow
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
*************************/
実行すると、u128を表示する前にパニックしてしまいました。「attempt to multiply with overflow」、つまり、「乗算をやろうとしたらオバーフローおこしちゃった。」ってとこです。オーバーフローとは、許容範囲を超えた状態のことを指します。u128の最大(2¹²⁸-1)を計算する際に、u128でも扱えない整数(2¹²⁸)を出してしまったために、引き算する前にオーバーフローを起こしてしまったのです。
これはどうしたらよいでしょうか?
まあ、普通に考えて「u128::MAX」を表示すればよい話なのですが、それでは決め打ちになってしまいます。もしかしたら、たまたま表示したかった値が「u128::MAX」に等しかっただけかもしれません。とにかく、計算で出せるようにしたいです。
そこで、今回は巨大な整数を使えるようにする 「uint」 クレートを使います。
本当は、「bigint」を使用したかったのですが、今では更新されておらず、非推奨で、「uint」の使用を促されたので、「uint」を使いたいと思います。
巨大な整数 uint
uintを導入するプロジェクトを新規作成します。「try_uint」というプロジェクトファイルを作成しました。今回はここで試します。
Cargo.toml
新規作成したプロジェクトファイル内のCargo.tomlに、「uint」クレートを使うことを記します。
[dependencies]に、「uint = "0.8"」を追加します。
[package]
name = "try_uint"
version = "0.1.0"
edition = "2021"
[dependencies]
uint = "0.8"
インクルード
useは以下のように宣言します。
use uint::construct_uint;
uintでは「construct_uint!」マクロを利用します。これを使用して、「任意のビット幅を指定して、そのビット幅をサポートする型を生成する」ことができます。
use uint::construct_uint;
construct_uint! {
pub struct U1024(16);
}
これは、U1024という名前の、64ビット × 16 = 1024ビットの符号なし整数型を作成します。
この、引数(16)は、64ビットのブロック数を指します。
つまり、u128は、以下と等しいです。
construct_uint! {
pub struct U128(2);
}
あとは、これを利用して先ほどと同じようなプログラムを作成します。
use uint::construct_uint;
construct_uint! {
pub struct U1024(16);
}
fn main() {
let a: U1024 = U1024::from(2);
let mut x: U1024 = U1024::from(8);
let arr = ["u8", "u16", "u32", "u64", "u128"];
for i in arr {
println!("{:>4}: 0 ~ {}", i, a.pow(x) - 1);
x = x * 2;
}
}
/******** 実行結果 ********
u8: 0 ~ 255
u16: 0 ~ 65535
u32: 0 ~ 4294967295
u64: 0 ~ 18446744073709551615
u128: 0 ~ 340282366920938463463374607431768211455
*************************/
変数a、および変数xがU1024型の変数です。U1024型の「2」なら、U1024::from(2)となります。
もし、自分でBIG_INTと名前を付けたなら、BIG_INT型になります。
1024ビットで計算していますが、192ビットでも計算できます。
uintでは、理論上、無制限な大きさの型を作れますが、コンパイラが使用できるメモリサイズには環境別の制限があります。
おわりに
今回は「uint」クレートを利用してu128よりも大きな型を作りました。
次回は、「HashMap」について学びます。以前、似たようなもので「HashSet」を紹介しましたが、まだ「HashMap」を紹介していませんでした。
ご精読ありがとうございました。