4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

[Rust] IEEE754浮動小数点数の実装 (1) 比較演算子

Last updated at Posted at 2020-09-10

IEEE754-2019 に従って倍精度浮動小数点数を実装するノートです. 初回は比較演算子についてですが, Equal ができればあとはだいたい同じなので Equal についてのみ述べます.

以下で使う F64 の定義はこちら.

# [derive(Debug, Clone, Copy)]
pub struct F64 {
    v: u64,
}

impl F64 {
    pub fn to_bits(self) -> u64 { self.v }
    pub fn from_bits(v: u64) -> F64 { F64 { v } }

    pub fn is_nan(self) -> bool {
        let a = self.to_bits();
        // exponent がすべて 1 && significand が 0 ではない
        (!a & 0x7ff0000000000000 == 0) && (a & 0x000fffffffffffff != 0)
    }
}

仕様

IEEE754 §5.6.1 に掲げられているサポートしなければならない比較演算子のうち等号 (Equal) に関するものは以下の4つです (pp. 43-44 の table 5.1, 5.2 もご覧ください).

返り値 関数名 true になる条件 備考
boolean compareQuietEqual equal = に相当
boolean compareQuietNotEqual equal 以外 != に相当
boolean compareSignalingEqual equal
boolean compareSignalingNotEqual equal 以外

より詳細な仕様は §5.11 に記述されています. これを読むと次のことがわかります.

  • すべての浮動小数点数の二つ組は, equal, less than, greater than, unordered のいずれか.
    • 一方が NaN ならば unorderd (たとえふたつの入力が同一の bit 列であったとしても).
  • ゼロの比較は符号を無視して行う.
    • つまり -0+0equal.
  • 正負の無限大は符号も含めて一致するとき equal.
  • signaling NaN が入力された場合は不正 (invalid) な演算となる.
    • Quiet とついている関数は, signaling NaN が渡された場合のみ invalid シグナルを出す.
    • Signaling とついている関数は, quiet NaN が渡された場合にも invalid シグナルを出す.
  • Not とついている関数の返り値は, ついていない関数の返り値の論理的否定に等しい.
    • 例えば unorderdNotEqual を true にする.
      • この仕様が Rust で f64PartialEq であるが Eq ではない理由です. Eq は同値類なので a == a が常に成り立つことが要求されますが, NaN != NAN が成立しています.
    • invalid 例外については上に述べた通り.

これだけわかれば素直に実装できます. なお, 今は倍精度のみ実装しているので関係ないですが, 精度の異なるデータ (例えば単精度と倍精度) についても (共通の基数を持つならば) 比較できるようにしておく必要があります.

実装

コード中の is_signaling, raise_flags, StatusFlag については別記事にする予定なのでとりあえず詳細な説明は省略しますが, 要は signaling NaN かどうかを判定して invalid シグナルを発する処理です.

pub fn compare_quiet_equal(a: F64, b: F64) -> bool {
    if a.is_nan() || b.is_nan() {
        if self.is_signaling() || other.is_signaling() {
            raise_flags(StatusFlag::Invalid);
        }
        false
    } else {
        let ua = a.to_bits();
        let ub = b.to_bits();

        // ビット列として一致 || 入力がどちらも `\pm 0` (符号は無視する)
        (ua == ub) || ((ua | ub) & 0x7fffffffffffffff == 0)
    }
}

pub fn compare_quiet_not_equal(a: F64, b: F64) -> bool {
    !compare_quiet_equal(a, b)
}

pub fn compare_signaling_equal(a: F64, b: F64) -> bool {
    if a.is_nan() || b.is_nan() {
        raise_flags(StatusFlag::Invalid);
        false
    } else {
        let ua = a.to_bits();
        let ub = b.to_bits();

        // ビット列として一致 || 入力がどちらも `\pm 0` (符号は無視する)
        (ua == ub) || ((ua | ub) & 0x7fffffffffffffff) == 0)
    }
}

pub fn compare_signaling_not_equal(a: F64, b: F64) -> bool {
    !compare_signaling_equal(a, b)
}

これを使えば PartialEq を impl できます.

impl PartialEq for F64 {
    fn eq(&self, other: &F64) -> bool {
        compare_quiet_equal(*self, *other)
    }
}

参考文献

4
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?