2016-08-07 11 views
0

これは、浮動小数点の表現を印刷するための問題の解決策です(x =(-1)^ sign・(1.m 22 m 21 m 20。 m 0)・2 ^(e-bias))と私はそれにいくつかのことを理解していない: 1)組合の使用は、なぜですか? 2)MANTISSA_MASKとEXPONENET_MASK、それは何ですか? 3)ここで&の使用:浮動小数点表現(ビット演算子を使用)

#include <stdio.h> 
#include <stdlib.h> 
#include <stdint.h> 
#include <math.h> 

#define ABSOLUTE_WIDTH 31 
#define MANTISSA_WIDTH 23 
#define EXPONENT_WIDTH 8 
#define EXPONENT_MASK 0xffu 
#define MANTISSA_MASK 0x007fffffu 
#define EXPONENT_BIAS 127 

union float_bits { 
    float f; 
    uint32_t bits; 
}; 

void print_float(FILE *output, float f) { 
    union float_bits t; t.f = f; 

    uint32_t sign_bit = (t.bits >> ABSOLUTE_WIDTH); 
    uint32_t exponent = (t.bits >> MANTISSA_WIDTH) & EXPONENT_MASK; 
    uint32_t mantissa = (t.bits & MANTISSA_MASK); 

    if(sign_bit != 0) { 
    fprintf(output, "-"); 
    } 

    if(exponent > 2 * EXPONENT_BIAS) { 
    fprintf(output, "Inf\n"); /* Infinity */ 
    return; 
    } else if(exponent == 0) { 
    fprintf(output, "0."); /* Zero or Denormal */ 
    exponent = (mantissa != 0) ? exponent + 1 : exponent; 
    } else { 
    fprintf(output, "1."); /* Usual */ 
    } 

    for(int k = MANTISSA_WIDTH - 1; k >= 0; --k) { 
    fprintf(output, "%d", (mantissa >> k) & 1); 
    } 

    if(exponent != 0 || mantissa != 0) { 
    fprintf(output, " * 2^%d\n", (int) (exponent - EXPONENT_BIAS)); 
    } 
} 

int main() { 
    FILE *input = fopen("floating.in", "r"), 
     *output = fopen("floating.out", "w"); 

    size_t N; float f; 
    fscanf(input, "%zu", &N); 

    for(size_t i = 0; i < N; ++i) { 
    fscanf(input, "%f", &f); 
    print_float(output, f); 
    } 

    fclose(input); 
    fclose(output); 
    return 0; 
} 
+0

'frexp 'と呼ばれることができる関数がmath.hにあります。仮数部は[0.5,1 [ – rondino

+1

の間にあります。*型打ち*には' union'があります。 2つの異なるタイプ。そうでなければ、 'float'だけを持っていれば、その' float'にアクセスすると、(浮動小数点型の)*値*が得られます。決してビットパターンはありません。 uint32_tとしての2番目の*ビュー*は、値(unsinged number)がそれを表すビットパターンとの移植可能な関係を持っているという利点があります。 –

答えて

1

1)労働組合の使用、なぜ:ここ

uint32_t exponent = (t.bits >> MANTISSA_WIDTH) & EXPONENT_MASK; 
    uint32_t mantissa = (t.bits & MANTISSA_MASK); 

コードですか?

ビット演算子は整数型にのみ使用できます。明白な理由から、浮動小数点数を整数に変換することはできません。しかし、組合は、重複しているコンポーネントのメモリを探します。したがって、浮動小数点コンポーネントに書き込むことによって、整数コンポーネントを読み取ることによって、浮動小数点数の整数表現が返されます。明らかにするには:これはではなく、浮動小数点数の整数値です。これを計算の整数として使用すると、予期しない結果が得られます。しかし、浮動小数点数のビットのように、整数のビットにアクセスすることができます。

2)MANTISSA_MASKとEXPONENET_MASKについては何ですか?

浮動小数点数は、仮数(桁ストリング)を指定するビット数と、数字の「位置」を表す指数部によって表されます。浮動小数点数の整数型への「変換」後、この2つの部分は整数値で混合されます。 MANTISSA_MASKEXPONENT_MASK(あなたのQに誤字があります)は、その部分をマスクします。 MANTISSA_BITS指数部を正しい場所に移動します。

3)ここで&の使用:

それはビットのビットアウトマスク演算子です。完全仮想 - -

はのは、持ってみましょう例を:

あなたのコードからは、仮数部の23ビットと指数部の8ビットを持っています。 32ビットのうちの1ビットは符号のために予約されている。番号を持ってみましょう:

00000001000010011010011010101010 

は、1符号ビット、8指数ビットおよび23個の仮数ビットを有する、あなたが唯一の仮数部を持つマスクを使用し、仮数を取得するには、この

0 00100010 00010011010011010101010 
s exponent --------mantissa------- 

ようにそれを読むことができますビットがセットさ:

0 00000000 11111111111111111111111 

あなたはビットと、それは、両方のオペランドに1をされている唯一のビットが1あり、他のすべてのビットが0のとき:

0 00100010 00010011010011010101010 A 
0 00000000 11111111111111111111111 B 
- -------- ----------------------- 
0 00000000 00010011010011010101010 A&B 

仮数部は指数部から除外されます(仮数部を表す実数の整数値です)。指数は(右端の)ビット0から始まるように

が指数を取得するには、まず右の単語全体をシフト:

0 00100010 00010011010011010101010 
00000000000000000000000 0 00100010 >> 23 (mantissa bist) 

は、符号ビットから指数を単離するために、あなたはbit-しなければなりませんそれと再び:

00000000000000000000000 0 00100010 A 
00000000000000000000000 0 11111111 B 
------------------------------------ 
00000000000000000000000 0 00100010 A&B 

Etvoíla。

+0

あなたの説明をありがとう、私は知っている:D。 – ahmedgu

1

セクション3.4のIEEE Std 754-2008(C規格ではIEC 60559と同じものを使用しています)に記載されているように、コードで想定しているフォーマットは「バイナリ交換浮動小数点フォーマット」であり、 )。それはイェンスGustedtが彼のコメントで説明したように、使用する労働組合がintとして使用しているフロートできるようにコンパイラを説得

bits:  0  1-9   10-32 
     sign bit exponent  significant (or mantissa) 

である32ビット浮動小数点の場合

(同じサイズのを!)、およびその逆。一度整数があれば、ビットで噛み砕くことができます。

符号ビットは左端のビットです.2^31で割るか、31で右にシフトすると得られます。

指数は以下の8ビットにあります。このコードは、仮数部のサイズの23ビットを右にシフトし、指数部をマスクする(符号ビットを除く)ことでそれを取得します。

これらは、右端の23ビットをマスクするだけで有効です。

指数自体に偏りがあります。どうして?数字が0の場合0 < n < 1に負の指数があり、数字> = 1の指数が正の値になります。符号に余分なビットを追加する代わりに、指数は半分になります。一定の限度(偏り)以下のものは全て負とみなし、上のすべてを正として取る必要があります。指数部の符号を正しい値で得るには、バイアスを減算するだけです。

規格によって定義されたいくつかの特別な値がありますInfNaNNaNと静かNaNシグナリング)

  • NaN(IEEE STD 754-2008セクション6.2.1)としてエンコードされ

すべてのバイナリNaNビットストリングは、バイアスされた指数フィールドEのすべてのビットを1に設定します(3.4を参照)。静かなNaNビットストリングは、後続の仮数フィールドTの最初のビット(d1)を1にしてエンコードする必要があります。シグナリングNaNビットストリングは、後続の仮数フィールドの最初のビットを0にしてエンコードする必要があります。末尾の仮数フィールドが0である場合、後続の仮数フィールドの他のビットは、NaNを無限大と区別するために非ゼロでなければなりません。今説明した好ましい符号化では、シグナリングNaNは、d1を1に設定することによって静かにされ、Tの残りのビットは変更されない。

バイナリ形式の場合、ペイロードは末尾の有効なフィールドのp-2個の最小有効ビットでエンコードされます。Infため

  • Infエンコーディングは、セクション3.5.2でのみ進エンコーディングのために、(または私はそれを見つけていない)IEEE-754で、そのような明示的に説明されていないが、通常最大であります指数(すべてのビットが1に設定されている)、プラスおよびマイナスの無限大を区別するための変更されていない符号ビット、および有限数と区別するために0に設定された仮数のすべてのビット。簡単にテストされています。

コードにおけるビットジャグリングは、非常に複雑であり、特定のエンディアンを想定しfloatuint32_tが同じエンディアンを有しており、IEEE規格754-2008 /に記載されているように、そのfloatが単精度フォーマットで符号化されますIEC 60559(C標準マクロ__STDC_IEC_559__で確認する必要があります)、unionのトリックはコンパイラを使用して動作します。 frexp(3)のようなものが必要な場合は、実際にビルドインを使用する必要があります。

frexp()(それを書き換えることが怠惰にdoubleため、。機能のほんの一握りが必要とメモリが疎であったので、それが書かれた、libmathの私自身のバージョンからである)浮いのみという、あまり多くのことを前提としていますポイント数は、IEC 60559に準拠しています。私は、複雑なインラインコムを置き換え

int isinf(double x){ 
    // TODO: not every compiler might eat this check for Inf 
    // GCC-4.8.4 does 
    // TCC 0.9.25 does 
    // clang 3.4-1ubuntu3 (based on LLVM 3.4) does 
    return (x == 1.0/0.0 || x == -1.0/0.0); 
} 


int isnan(double x){ 
    return (x != x); 
} 

を:

double frexp(double x, int *eptr) 
{ 
    int sign, exponent; 
    int i; 

    /* 
    * The exponent of an IEEE-754 double (binary64) is an 11-bit large integer 
    */ 
    double ap_2[11] = { 
    2.0000000000000000000000000000000000000, 
    4.0000000000000000000000000000000000000, 
    16.000000000000000000000000000000000000, 
    256.00000000000000000000000000000000000, 
    65536.000000000000000000000000000000000, 
    4294967296.0000000000000000000000000000, 
    18446744073709551616.000000000000000000, 
    3.4028236692093846346337460743176821146e38, 
    1.1579208923731619542357098500868790785e77, 
    1.3407807929942597099574024998205846128e154, 
    1.7976931348623157e308 // DBL_MAX 
    }; 

    double ap_half[11] = { 
    0.50000000000000000000000000000000000000, 
    0.25000000000000000000000000000000000000, 
    0.062500000000000000000000000000000000000, 
    0.0039062500000000000000000000000000000000, 
    1.5258789062500000000000000000000000000e-5, 
    2.3283064365386962890625000000000000000e-10, 
    5.4210108624275221700372640043497085571e-20, 
    2.9387358770557187699218413430556141946e-39, 
    8.6361685550944446253863518628003995711e-78, 
    7.4583407312002067432909653154629338374e-155, 
    5.5626846462680034577255817933310101606e-309 // < DBL_MIN 
    }; 

    if (isinf(x)) { 
    *eptr = 0; 
    return x; 
    } 
    if (isnan(x)) { 
    *eptr = 0; 
    return x; 
    } 

    if (x == 0.0) { 
    *eptr = 0; 
    return x; 
    } 

    exponent = 0.0; 
    /* 
    * Easier to work with positive values 
    */ 
    if (x < 0) { 
    x = -x; 
    sign = 1; 
    } 

    else { 
    sign = 0; 
    } 

    if (x >= 1.0) { 
    /* 
    * Big steps 
    */ 
    for (i = 0; x >= ap_2[i]; i++) { 
     exponent += (1 << i); 
     x *= ap_half[i]; 
    } 
    /* 
    * Small steps 
    */ 
    if (x < 0.5) { 
     while (x < 0.5) { 
     x *= 2.0; 
     exponent--; 
     } 
    } else { 
     while (x > 1.0) { 
     x /= 2.0; 
     exponent++; 
     } 
    } 
    } else { 
    /* 
    * Same as above, but in the opposite direction 
    */ 
    for (i = 0; x < ap_half[i]; i++) { 
     exponent -= (1 << i); 
     x *= ap_2[i]; 
    } 
    if (x < 0.5) { 
     while (x < 0.5) { 
     x *= 2.0; 
     exponent--; 
     } 
    } else { 
     while (x > 1.0) { 
     x /= 2.0; 
     exponent++; 
     } 
    } 
    } 

    if (sign) { 
    x = -x; 
    } 
    *eptr = exponent; 
    return x; 
} 

機能isinf()は、すべてのコンパイラがそれをサポートする可能性があります、私はそれを置くものとどのように、ビットで大胆かつません2の倍数の2つの表の(先に述べたように:メモリは疎であった)putation。私はそれを行うことでコードの残りの部分を崩壊させないことを願っています。

Aaaaaand私はいつものように遅すぎました。この時間はAmin Negm-Awadが43分に迫った。

関連する問題