オーバーフローの取扱について。

全体設定

基本的にはオーバーフローすると開発ビルドではパニック、リリースビルドでは無視される。つまり、前者はエラー検出、後者は実行速度を優先している。この設定は Cargo.toml から以下のように変更できる (詳細は後述の仕様を参照)。


[profile.dev]
overflow-checks = false

API

checked_xxx

例: i32::checked_add

オーバーフローすると None が戻され、それ以外では Some で計算結果が戻される。


fn main() {
    assert_eq!(i32::checked_add(2, 3), Some(5));
    assert_eq!(i32::checked_add(i32::MAX, 1), None);
}

overflowing_xxx

例: i32::overflowing_add

オーバーフローした上位ビットは無視され、共にオーバーフローの有無が戻される。


fn main() {
    assert_eq!(i32::overflowing_add(2, 3), (5, false));
    assert_eq!(i32::overflowing_add(i32::MAX, 3), (i32::MIN + 2, true));
}

saturating_xxx

例: i32::saturating_add

計算結果が最小値と最大値に抑制される。


fn main() {
    assert_eq!(i32::saturating_add(2, 3), 5);
    assert_eq!(i32::saturating_add(i32::MAX, 3), i32::MAX);
}

strict_xxx

例: i32::strict_add

オーバーフローするとパニックする。


fn main() {
    assert_eq!(i32::strict_add(2, 3), 5);
    let _val = i32::strict_add(i32::MAX, 1); // ⚠️ Panic!
    unreachable!();
}

unbounded_xxx

例: i32::unbounded_shl

ビットシフト用。第二引数に整数型のビット数以上の値を指定可能にする。


fn main() {
    assert_eq!(i32::unbounded_shl(1, 3), 8);
    assert_eq!(i32::unbounded_shl(1, 32), 0);
}

unchecked_xxx

例: i32::unchecked_add

unsafe 関数。オーバーフローは UB (Undefineded Behavior) として処理する。


fn main() {
    unsafe {
        assert_eq!(i32::unchecked_add(2, 3), 5);
        let _val = i32::unchecked_add(i32::MAX, 1); // ⚠️ UB!
    }
}

wrapping_xxx

例: i32::wrapping_add

オーバーフローした上位ビットは無視される。


fn main() {
    assert_eq!(i32::wrapping_add(2, 3), 5);
    assert_eq!(i32::wrapping_add(i32::MAX, 3), i32::MIN + 2);
}

Saturating

saturating_xxx のラッパー版。


use std::num::Saturating;

fn main() {
    assert_eq!(Saturating(2) + Saturating(3), Saturating(5));
    assert_eq!(Saturating(i32::MAX) + Saturating(3), Saturating(i32::MAX));
}

Wrapping

wrapping_xxx のラッパー版。


use std::num::Wrapping;

fn main() {
    assert_eq!(Wrapping(2) + Wrapping(3), Wrapping(5));
    assert_eq!(Wrapping(i32::MAX) + Wrapping(3), Wrapping(i32::MIN + 2));
}

API (ナイトリー)

、以下の API はまだナイトリーだが、参考としてメモしておく。

borrowing_xxx

例: i32::borrowing_sub

多倍長整数の演算に利用できる。前の減算のオーバーフローを今回の減算に加味しつつ、減算結果とオーバーフローの発生有無を取得する。


#![feature(signed_bigint_helpers)]

fn main() {
    assert_eq!(i32::borrowing_sub(7, 3, false), (4, false));
    assert_eq!(i32::borrowing_sub(7, 3, true), (3, false));
    assert_eq!(i32::borrowing_sub(i32::MIN, 3, false), (i32::MAX - 2, true));
    assert_eq!(i32::borrowing_sub(i32::MIN, 3, true), (i32::MAX - 3, true));
}

carrying_xxx

例: i32::carrying_add

多倍長整数の演算に利用できる。前の計算のオーバーフローを今回の計算に加味しつつ、計算結果とオーバーフローの発生有無 (乗算の場合は上位ビット群) を取得する。


#![feature(signed_bigint_helpers)]

fn main() {
    assert_eq!(i32::carrying_add(2, 3, false), (5, false));
    assert_eq!(i32::carrying_add(2, 3, true), (6, false));
    assert_eq!(i32::carrying_add(i32::MAX, 3, false), (i32::MIN + 2, true));
    assert_eq!(i32::carrying_add(i32::MAX, 3, true), (i32::MIN + 3, true));
}

widening_mul

乗算結果とオーバーフローによる上位ビット群を取得する。


#![feature(widening_mul)]

fn main() {
    assert_eq!(u8::widening_mul(2, 3), (6, 0));
    assert_eq!(u8::widening_mul(0b1000_0000, 3), (0b1000_0000, 1));
}

仕様