ほのぼのしたエンジニアのブログ

触った技術についてまとめていくブログです。

Rust:Floatについてもうちょっと

どうもShZgZです。

 

寒いです。みなさん体調にはくれぐれもお気をつけて。

 

PartialEqについて調べていた時に

・i32:Eq

・f32:PartialEq

となっていて、なんぞ?と思ったらサンプルのところで

NaNがあるからって書いてある。

 

・・・ほう。わからん。

 

NaNって恥ずかしながらあんまり意識してこなかったため

Nilとかnullみたいなもんやろと思っていました。

大学のときに先生が説明してくれていたらごめんなさい。

 

で、とりあえずNaNについてWikiさん曰く

NaN - Wikipedia

NaNNot a Number、非数、ナン)は、コンピュータにおいて、主に浮動小数点演算の結果として、不正なオペランドを与えられたために生じた結果を表す値またはシンボルである。NaNの体系的仕様は、無限大の表現などと共に1985年の IEEE 754 浮動小数点規格で標準が与えられている。 

 なんかエラーがあったときの値ってことみたいな感じ。

浮動小数点数は値を丸めちゃうから0で割るような状況が起き得るからかな?

なんとなくわかったんだけど、ここでさらに不穏な"無限大の表現などと共に"って言葉。

 

・・・無限大ってあるの?

 

もしかしたらとても基本的なことなのかもしれないけどとりあえずこれまで気にした事なかった。証明支援系で使う時に実装してたのは知ってたけど、一般的に実装されてるものだと思ってなかった。

 

で、とりあえずRustでどう実装されてるか見てみた。

/// Not a Number (NaN).
#[stable(feature = "rust1", since = "1.0.0")]
pub const NAN: f32 = 0.0_f32 / 0.0_f32;
/// Infinity (∞).
#[stable(feature = "rust1", since = "1.0.0")]
pub const INFINITY: f32 = 1.0_f32 / 0.0_f32;
/// Negative infinity (-∞).
#[stable(feature = "rust1", since = "1.0.0")]
pub const NEG_INFINITY: f32 = -1.0_f32 / 0.0_f32;

要するに、

0/0・・・NaN

1/0・・・無限大

-1/0・・・マイナス無限大

ってことらしい。

 

とりあえずいくつか試してみる。

use std::f32::{NAN, INFINITY, NEG_INFINITY};
fn main() {
println!("INFINITY:{}", INFINITY);
println!("INFINITY + 1000:{}", INFINITY + 1000_f32);
println!("INFINITY - 1000:{}", INFINITY - 1000_f32);
println!("INFINITY * 1000:{}", INFINITY * 1000_f32);
println!("INFINITY / 1000:{}", INFINITY / 1000_f32);
println!("INFINITY % 1000:{}", INFINITY % 1000_f32);
println!("NEG_INFINITY:{}", NEG_INFINITY);
println!("NEG_INFINITY + 1000:{}", NEG_INFINITY + 1000_f32);
println!("NEG_INFINITY - 1000:{}", NEG_INFINITY - 1000_f32);
println!("NEG_INFINITY * 1000:{}", NEG_INFINITY * 1000_f32);
println!("NEG_INFINITY / 1000:{}", NEG_INFINITY / 1000_f32);
println!("NEG_INFINITY % 1000:{}", NEG_INFINITY % 1000_f32);
println!("NAN:{}", NAN);
println!("NAN + 1000:{}", NAN + 1000_f32);
println!("NAN - 1000:{}", NAN - 1000_f32);
println!("NAN * 1000:{}", NAN * 1000_f32);
println!("NAN / 1000:{}", NAN / 1000_f32);
println!("NAN % 1000:{}", NAN % 1000_f32);
println!("INFINITY - INFINITY:{}", INFINITY - INFINITY);
}

INFINITY:inf

INFINITY + 1000:inf

INFINITY - 1000:inf

INFINITY * 1000:inf

INFINITY / 1000:inf

INFINITY % 1000:NaN

NEG_INFINITY:-inf

NEG_INFINITY + 1000:-inf

NEG_INFINITY - 1000:-inf

NEG_INFINITY * 1000:-inf

NEG_INFINITY / 1000:-inf

NEG_INFINITY % 1000:NaN

NAN:NaN

NAN + 1000:NaN

NAN - 1000:NaN

NAN * 1000:NaN

NAN / 1000:NaN

NAN % 1000:NaN

INFINITY - INFINITY:NaN

 大体直感通りな感じ。

 

因みに、traitのEq, PartialEqは実装する関数が全く一緒です。というか、EqはPartialEqを実装してればOKってやつです。

ただ、ここには意味上の違いがあり数学的には半同値関係と同値関係を表すものとなっています。(Eq・・・同値, PartialEq・・・半同値)

↑の意味は、興味あったら調べてみて下さい。

 

簡単にいうと、それぞれの要素aに関して

 a == a

が成り立つならEq, そうじゃない要素があるならPartialEqですよってことです。

 

具体的にいきます。

u32で考えてみると、u32の要素(1, 2, 3, 4)はそれぞれ

1 == 1 // true
2 == 2 // true
3 == 3 // true
4 == 4 // true

となり、

まぁそれ以外でも成り立ちますから、u32はEqを実装していると言えます。ただ、ここでも実際に実装する関数はPartialEqと同じですから、自分が新しく構造体を作りPartialEqを実装した場合に、PartialEqの対象となる要素a全てに対して

 a == a

としておくと 、後で自分や他人が見た時に意味が判り易くて助かるよってことです。

 

f32はどうかというと、f32の要素(1.1_f32, 2.52_f32, INFINITY, NaN)などの数値)はそれぞれ

1.1_f32 == 1.1_f32 // true
2.52_f32 == 2.52_f32 // true
INFINITY == INFINITY // true
NAN == NAN // false

となり、NANに関して等号が成り立ちません。

そのため、f32, f64は要素a全てに対して

 a == a

が成り立たないため、PartialEqとなっています。要するに、PartialEq実装したけど、同じ要素を比較した時に等号が成り立たない奴いるよってことを表してくれているわけです。

 

・まとめ

floatには小数要素の他に、数じゃない要素と無限大を表す要素あるよ。

(しかもIEEE 754で定まってるから結構色んな言語であるのかも。知ってたら教えて下さい。)

EqとPartialEqの違いは実装上は無い。ただ、意味として全要素について同じ要素 同士の等号が成り立つ(数学だと反射律って言います。)かどうかっていう違いがあるよ。