メインコンテンツ

浮動小数点オペランドの統合

他方のオペランドに吸収される加算または減算オペランド

説明

この欠陥は、加算または減算演算のオペランドが他のオペランドより "常に" 無視できるほど小さい場合に発生します。その結果、演算の結果は大きなオペランドの値と必ず等しくなり、この演算が冗長になります。

リスク

冗長な演算はプロセッサの実行サイクルを浪費します。

浮動小数点オペランドの統合は、コード内のどこかに設計上の問題があることを示している場合があります。オペランドの 1 つの範囲が開発者の想定では異なっており、その演算が冗長になるとは開発者は想定していなかった可能性があります。そのオペランドの範囲が開発者の想定範囲と異なっている理由は、コード内のどこかに問題があるためです。

修正方法

オペランドの範囲が想定した範囲かどうかを確認します。範囲を確認するには、その演算にカーソルを置きます。

  • 範囲が想定どおりであれば、冗長な演算になる理由を正当化します。たとえば、コードの一部しか作成しておらず、まだ作成していないコードで一方または両方のオペランドが他の値になると予想される場合です。

    冗長な演算を正当化できない場合は、それを削除します。

  • 範囲が想定どおりでない場合、その範囲がコード内のどこで設定されるかをトレースバックします。トレースバックを開始するには、コード内でそのオペランドのインスタンスを検索します。そのオペランドの前のインスタンスを参照し、予期しない範囲を設定している箇所を判断します。

一方のオペランドが他方のオペランドと比べて非常に小さな値になるタイミングを判断するため、欠陥では IEEE® 754 規格に基づくルールを使用します。欠陥を修正するには、実際のルールを使用する代わりに、小さい値のオペランドと大きい値のオペランドの比率が少なくとも一部の値で 2p-1 よりも小さくならなければならないというヒューリスティックを使用します。ここで、p は、32 ビット精度では 24、64 ビット精度では 53 に相当します。この精度を決めるため、欠陥ではターゲット プロセッサ タイプ (-target)の指定を使用します。

この欠陥は、一方のオペランドが "常に" 他方オペランドより無視できるほど小さい場合に現れます。非正規オペランドのインスタンスまたは結果を確認するには、Polyspace® Code Prover™[非正規浮動小数点] チェックを使用します。

すべて展開する

#include <stdlib.h>

float get_signal(void);
void do_operation(float);

float input_signal1(void) {
    float temp = get_signal();
    if(temp > 0. && temp < 1e-30)
        return temp;
    else {
       /* Reject value */    
       exit(EXIT_FAILURE);
    }
}

float input_signal2(void) {
    float temp = get_signal();
    if(temp > 1.)
        return temp;
    else {
       /* Reject value */    
       exit(EXIT_FAILURE);
    }
}

void main() {
    float signal1 = input_signal1();
    float signal2 = input_signal2();
    float super_signal = signal1 + signal2;
    do_operation(super_signal);
}

この例では、オペランド signal1(0,1e-30) の範囲内に収まっていても、signal21 より大きいため、加算で欠陥が現れます。

修正 - 冗長な演算を削除

考えられる 1 つの修正方法として、冗長な加算演算を削除します。修正した次のコードでは、オペランド signal2 およびそれに関連するコードも検討対象から削除されています。

#include <stdlib.h>

float get_signal(void);
void do_operation(float);

float input_signal1(void) {
    float temp = get_signal();
    if(temp > 0. && temp < 1e-30)
        return temp;
    else {
       /* Reject value */    
       exit(EXIT_FAILURE);
    }
}

void main() {
    float signal1 = input_signal1();
    do_operation(signal1);
}
修正 — オペランドの範囲を検証

別の修正方法として、オペランドの範囲が想定どおりかどうかを確認します。たとえば、オペランドの範囲のいずれかが無視できないほど小さくなるとは想定されない場合は、範囲を小さくしている問題を修正します。修正した次のコードでは、signal2 の範囲を (0,1e-2) に限定して指定しているため、signal1 と比べて "常に" 無視できるほど小さくなりません。

#include <stdlib.h>

float get_signal(void);
void do_operation(float);

float input_signal1(void) {
    float temp = get_signal();
    if(temp > 0. && temp < 1e-2)
        return temp;
    else {
       /* Reject value */    
       exit(EXIT_FAILURE);
    }
}

float input_signal2(void) {
    float temp = get_signal();
    if(temp > 1.)
        return temp;
    else {
       /* Reject value */    
       exit(EXIT_FAILURE);
    }
}

void main() {
    float signal1 = input_signal1();
    float signal2 = input_signal2();
    float super_signal = signal1 + signal2;
    do_operation(super_signal);
}

結果情報

グループ: 数値
言語: C | C++
既定値: オン
コマンド ライン構文: FLOAT_ABSORPTION
影響度: High

バージョン履歴

R2016b で導入

すべて展開する