
コンピュータサイエンスにおいて、サブノーマル数とは、浮動小数点演算におけるゼロ周辺のアンダーフローギャップを埋める非正規化数(デノーマル数とも呼ばれる)のサブセットのことです。最小の正の正規数よりも小さい絶対値を持つ非ゼロ数はすべてサブノーマル数であり、デノーマル数はその範囲外の数を指す場合もあります。[ a ]
古い文書(特にIEEE 754の初版(1985年)やC言語の最初の標準版(1989年)などの標準文書)では、「denormal」は非正規数のみを指すために使用されています。この用法は、特に他の非正規化数を表現できないハードウェアについて議論する場合など、さまざまな標準文書で引き続き使用されていますが、ここではIEEE 754の2008年改訂版に沿って「subnormal」という用語を使用します。非正規化IEEEバイナリ数には非正規化範囲外の数がないため、日常会話ではsubnormalとdenormalという用語はしばしば同義語として使用されます。
「数値」という用語は、数学的な抽象概念ではなく、特定の数字列を表すためにやや広義に用いられています。実数と浮動小数点表現の関係については、「浮動小数点演算」を参照してください。明確さが求められる場合は、「数値」ではなく「表現」という用語を用いることができます。
数学的な実数は、複数の浮動小数点表現によって近似することができます。1つの表現は「正規表現」と定義され、他の表現は「正規表現」との関係によって「劣正規表現」 、「非正規表現」、 「異常正規表現」と定義されます。
通常の浮動小数点値では、仮数部(仮数とも呼ばれる)に先頭のゼロはありません。代わりに、指数を調整することで先頭のゼロが削除されます(たとえば、数値 0.0123 は次のように表記されます)。1.23 × 10 −2 )。逆に、非正規化浮動小数点値は、先頭桁がゼロの仮数部を持ちます。これらのうち、サブノーマル数は、正規化した場合に表現可能な最小指数(指数には制限された範囲がある)よりも小さい指数を持つ値を表します。
IEEE 浮動小数点数の仮数部 (またはマンティッサ) は、浮動小数点数の有効桁を表す部分です。正の正規化された数の場合、m 0 . m 1 m 2 m 3 ... m p −2 m p −1 ( mは有効桁を表し、pは精度) のように表すことができ、 m 0はゼロ以外です。バイナリ基数の場合、先頭のバイナリ桁は常に 1 であることに注意してください。非正規数では、指数が最小値であるため、先頭の有効桁はゼロ (0. m 1 m 2 m 3 ... m p −2 m p −1 ) となり、最小の正規数よりもゼロに近い数を表すことができます。浮動小数点数は、指数が最小値である場合に非正規数として認識されます。
このようにアンダーフローのギャップを埋めることで、有効数字は失われますが、アンダーフロー時にすべての有効数字を破棄する「アンダーフロー時にゼロにフラッシュする」方式ほど急激には失われません。そのため、サブノーマル数の生成は、計算結果が小さい場合に精度が徐々に低下することを可能にするため、「段階的アンダーフロー」と呼ばれることがあります。
IEEE 754-2008では、非正規数はバイナリ形式と10進形式の両方でサポートされています。バイナリ交換形式では、非正規数はバイアス指数0でエンコードされますが、解釈される値は許容される最小指数(1より大きい値、つまり1としてエンコードされたかのように)となります。10進交換形式では、非正規数を直接サポートするため、特別なエンコードは必要ありません。
数学的に言えば、特定の符号を持つ正規化された浮動小数点数はほぼ対数的に間隔が取られており、そのため有限サイズの正規浮動小数点数にはゼロは含まれません。非正規浮動小数点数は、負の正規浮動小数点数と正の正規浮動小数点数の間のギャップを埋める、線形間隔で並んだ値の集合です。
サブノーマル数を使用すると、浮動小数点数の加算と減算でアンダーフローが発生しないことが保証されます。つまり、近い浮動小数点数の差は常に表現可能な非ゼロになります。段階的アンダーフローがない場合、a − bの減算では、値が等しくなくてもアンダーフローが発生してゼロになる可能性があります。これは、段階的アンダーフローが使用されている場合には発生しないゼロ除算エラーにつながる可能性があります。 [ 1 ]
IEEE 754 規格が作成されている間に、 Intel 8087にサブノーマル数が実装されました。これらは、最終的に採用されたKCS フォーマット提案の中で最も議論を呼んだ機能でしたが、 [ 2 ]この実装により、サブノーマル数が実用的な実装でサポートできることが実証されました。浮動小数点ユニットの実装の中には、サブノーマル数をハードウェアで直接サポートせず、何らかのソフトウェアサポートに頼っているものがあります。これはユーザーには透過的かもしれませんが、サブノーマル数を生成または消費する計算は、通常の数に対する同様の計算よりもはるかに遅くなる可能性があります。
IEEEバイナリ浮動小数点フォーマットでは、サブノーマルは、ゼロの指数部とゼロ以外の仮数部を持つことで表現されます。[ 3 ]この規格では、サブノーマル数の存在により、絶対値がゼロ以外の値を表現できます。にbinary32の場合、そしてにbinary64用。
IEEEバイナリ浮動小数点形式には、他の非正規化数値は存在しませんが、IEEE十進浮動小数点形式など、他の形式には存在します。
一部のシステムは、正常値と同様に、ハードウェアで異常値を処理します。他のシステムは、正常値とゼロのみをハードウェアで処理し、異常値の処理をシステムソフトウェア(「アシスト」)に任せます。ソフトウェアで異常値を処理すると、常にパフォーマンスが大幅に低下します。異常値を完全にハードウェアで計算する場合、正常値と同等の速度で処理できるようにする実装技術が存在します。[ 4 ]しかし、多くの最新のx86プロセッサでは、計算速度が大幅に低下したままです。極端な場合、異常値オペランドを含む命令は最大100クロックサイクル余分にかかり、最速の命令でも最大6倍遅くなることがあります。[ 5 ] [ 6 ]
この速度差はセキュリティリスクとなる可能性がある。研究者らは、この速度差がタイミングサイドチャネルを提供し、悪意のあるWebサイトがWebブラウザ内で別のサイトからページコンテンツを抽出できることを示した。[ 7 ]
アプリケーションによっては、精度を維持するため、または一部のプロセッサでのパフォーマンス低下を回避するために、正規化値以下の数値を回避するコードを含める必要がある場合があります。たとえば、オーディオ処理アプリケーションでは、正規化値以下の値は通常、人間の聴覚範囲外の非常に小さな信号を表します。このため、パフォーマンス低下が発生するプロセッサで正規化値以下の数値を回避する一般的な対策は、信号が正規化値以下のレベルに達したらゼロにカットするか、非常に小さなノイズ信号をミックスすることです。[ 8 ]正規化値以下の数値を防止するその他の方法としては、 DCオフセットの追加、数値の量子化、ナイキスト信号の追加などがあります。 [ 9 ] SSE2プロセッサ拡張機能以降、IntelはCPUハードウェアでこのような機能を提供し、正規化値以下の数値をゼロに丸めています。[ 10 ]
Intel の C および Fortran コンパイラは、最適化レベルが よりも高い場合、デフォルトでSSEのDAZ(denormals-are-zero) および(flush-to-zero) フラグを有効にします。[ 11 ]の効果は、浮動小数点演算への非正規入力引数をゼロとして扱うことであり、 の効果は、入力引数自体が非正規でない場合でも、非正規浮動小数点数になる演算に対して、非正規浮動小数点数の代わりにゼロを返すことです。ClangおよびGCC は、プラットフォームと最適化レベルに応じて、デフォルトの状態が異なります。FTZ-O0DAZFTZ
SSEをサポートするターゲットでフラグとフラグを有効にするC99非準拠の方法を以下に示しますが、広くサポートされていません。少なくとも2006年以降、 Mac OS Xで動作することが知られています。 [ 12 ]DAZFTZ
#include <fenv.h> #pragma STDC FENV_ACCESS ON // DAZ と FTZ を設定し、他の CSR 設定を上書きします。fesetenv ( FE_DFL_DISABLE_SSE_DENORMS_ENV ); // fesetenv(FE_DFL_ENV) // 両方を無効にし、他の CSR 設定を上書きします。Cライブラリがまだこのフラグを実装していない他のx86-SSEプラットフォームでは、次の方法が機能する可能性があります。[ 13 ]
#include <xmmintrin.h> _mm_setcsr ( _mm_getcsr () | 0x0040 ); // DAZ _mm_setcsr ( _mm_getcsr () | 0x8000 ); // FTZ _mm_setcsr ( _mm_getcsr () | 0x8040 ); // 両方_mm_setcsr ( _mm_getcsr () & ~ 0x8040 ); // 両方を無効化とマクロは_MM_SET_DENORMALS_ZERO_MODE、_MM_SET_FLUSH_ZERO_MODE上記のコードをより読みやすくするインターフェースをラップします。[ 14 ]
// DAZを有効にするには#include <pmmintrin.h> _MM_SET_DENORMALS_ZERO_MODE ( _MM_DENORMALS_ZERO_ON ); // FTZを有効にするには#include <xmmintrin.h> _MM_SET_FLUSH_ZERO_MODE ( _MM_FLUSH_ZERO_ON );ほとんどのコンパイラは既に上記のマクロをデフォルトで提供していますが、そうでない場合は、次のコードスニペットを使用できます(FTZ の定義も同様です)。
#define _MM_DENORMALS_ZERO_MASK 0x0040 #define _MM_DENORMALS_ZERO_ON 0x0040 #define _MM_DENORMALS_ZERO_OFF 0x0000#define _MM_SET_DENORMALS_ZERO_MODE(mode) _mm_setcsr((_mm_getcsr() & ~_MM_DENORMALS_ZERO_MASK) | (mode)) #define _MM_GET_DENORMALS_ZERO_MODE() (_mm_getcsr() & _MM_DENORMALS_ZERO_MASK)デフォルトの非正規化動作はABIによって規定されているため、適切に動作するソフトウェアは、呼び出し元または他のライブラリの呼び出しコードに戻る前に、非正規化モードを保存および復元する必要があります。
ARMアーキテクチャでは、非正規浮動小数点数の処理は浮動小数点ユニットの構成に依存し、一部の実装ではハードウェアでの段階的なアンダーフローをサポートしている一方、パフォーマンス上の理由からデフォルトでゼロへのフラッシュ動作になっているものもある。[ 15 ]
AArch32 NEON (SIMD) FPU は常にゼロフラッシュモードを使用します。[ 16 ]これは と同じですFTZ + DAZ。スカラー FPU および AArch64 SIMD の場合、ゼロフラッシュ動作はオプションであり、FZ制御レジスタのビット( Arm32 では FPSCR、AArch64 では FPCR) によって制御されます。[ 17 ]
これを実現する方法の一つとして、以下のことが挙げられます。
#if defined(__arm64__) || defined(__aarch64__) uint64_t fpcr ; asm ( "mrs %0, fpcr" : "=r" ( fpcr )); // FPCR レジスタをロードasm ( "msr fpcr, %0" :: "r" ( fpcr | ( 1 << 24 ) )); // 24 番目のビット (FTZ) を 1 に設定#endif一部のARMプロセッサは、サブノーマルをハードウェアで処理する機能を備えている。