C++
 Computer >> コンピューター >  >> プログラミング >> C++

C++で乗算時の整数オーバーフローを検出する方法

2つの数値 A と B を掛け合わせた結果を求めたい場合、その積が64ビット整数(long long)の表現範囲を超えてしまうかどうかを事前に確認する必要があります。例えば、100 × 200 のような小さな数値同士の乗算では範囲を超えませんが、10000000000 × -10000000000 のように大きな数値同士を掛け合わせると、結果が64ビット整数の上限・下限を超えてオーバーフローが発生します。

このようなオーバーフローを検出するには、以下の手順に従います。

オーバーフロー判定の手順

  • どちらか一方の数値が 0 である場合、積は必ず0になるため、オーバーフローは発生しません。
  • それ以外の場合、計算した積(A × B)を片方の数値で割り戻したとき、もう片方の元の数値と一致すれば、オーバーフローは発生していません。
  • 上記以外の場合(割り戻した結果が元の数値と一致しない場合)は、オーバーフローが発生しています。

この方法が機能する理由は、オーバーフローが起きると値がラップアラウンド(桁あふれによる反転)するため、逆算しても元の数値に戻らなくなるからです。

サンプルコード

#include <iostream>
#include <cmath>
using namespace std;

bool isMulOverflow(long long A, long long B) {
    if (A == 0 || B == 0)
        return false;   // どちらかが0ならオーバーフローしない

    long long result = A * B;

    if (A == result / B)
        return false;   // 割り戻して元に戻れば安全
    else
        return true;    // 一致しなければオーバーフロー
}

int main() {
    long long a = 10000000000, b = -10000000000;

    if (isMulOverflow(a, b)) {
        cout << "It will overflow";
    } else {
        cout << "It will not overflow";
    }
}

実行結果

It will overflow

補足:より安全な代替手段

なお、C++の仕様では符号付き整数のオーバーフローは未定義動作(UB)と定められているため、上記のように一度 A * B を計算してから判定する方法は、厳密には環境依存のリスクを伴います。GCC や Clang を使用している場合は、オーバーフロー時に自動的に飽和処理を行う組み込み関数 __builtin_mul_overflow() を使うことで、未定義動作を回避しながら安全に判定できます。

long long result;
if (__builtin_mul_overflow(a, b, &result)) {
    // オーバーフローが発生した場合の処理
}

C++23以降では標準ライブラリの std::mul_sat(飽和演算)なども利用可能になり、ポータブルかつ安全な乗算処理が容易になっています。

  1. C++で二分木の子ノード合計プロパティを検証する方法

    二分木が与えられたとき、次のプロパティ(性質)を満たしていれば、その二分木は有効とみなされます。各ノードのデータ値は、左の子ノードと右の子ノードの値の合計と一致していなければなりません。どちらかの側に子ノードが存在しない場合は、その値は0として扱われます。例えば、以下のような木が与えられた場合、このプロパティを満たしていることになります。この性質を確認するための特別なトリックは存在せず、木を再帰的に走査する必要があります。ノードとその両方の子がプロパティを満たしていればtrueを返し、そうでなければfalseを返します。アルゴリズムの流れ検証は以下の手順で行われます。ノードがNULL、または葉

  2. 配列の全要素を乗算するC++プログラムの解説

    整数型の要素を持つ配列が与えられたとき、配列内のすべての要素を掛け合わせ、その積を表示することを考えます。本記事では、この問題をC++(C言語スタイルのコード)で解く方法を、アプローチ、アルゴリズム、サンプルコード、実行結果まで順を追って解説します。 例 入力: arr[]={1,2,3,4,5,6,7} 出力: 1 x 2 x 3 x 4 x 5 x 6 x 7 = 5040 入力: arr[]={3, 4, 6, 2, 7, 8, 4} 出力: 3 x 4 x 6 x 2 x 7 x 8 x 4 = 32256 解き方のアプローチ この問題は、累積用の一時変数を用意し、配列の要素を先頭