2017-03-09 15 views
0

私は実際にSHA-256ハッシュ関数を書こうとしています。 wikipediaでは、最初のハッシュ値は、最初の8つの素数の平方根の小数部によって与えられることを意味しています。今、私はそれらを計算しようとしています。私はこれまでに行った:SHA-256の二重小数部の16進表現

#include <vector> 
#include <cstdint> 
#include <cmath> 
#include <cstdio> 

// fill primes with all prime values between min and max value 
int getPrimes(uint32_t min, uint32_t max, std::vector<uint32_t>* primes) 
{ 
    if (min < 1) min = 1;  // primes can only be >= 1 
    if (min > max) return 0; // max has to be larger than min 


    for (uint32_t value = min; value <= max; value++) 
    { 

    uint32_t tmp; 
    for (tmp = 2; tmp <= sqrt(value); tmp++) // start to check with 2, because 1 is always going to work 
    { 
     if (value % tmp == 0) 
     { 
     break; 
     } 
    } 
    if (tmp > sqrt(value)) primes->push_back(value); // if no other integer divisor is found, add number to vector 
    } 

    return 0; 
} 

int main() 
{ 
    std::vector<uint32_t> primes; 

    getPrimes(2, 20, &primes); // fills vector with all prime values between 2 and 20 

    double tmp = sqrt(primes[0]);      // get square root, returns double 
    printf("value %f\n", tmp);       // debug 
    printf("size of double %i\n", sizeof(double));  // get representation byte size 
    double * tmpOffset = &tmp;       // get value offset 
    unsigned char * tmpChar = (unsigned char*)tmpOffset;    // convert to char pointer 

    printf("address of variable %i\n", &tmp);   // debug 
    printf("raw values\n1:%X\n2:%X\n3:%X\n4:%X\n5:%X\n6:%X\n7:%X\n8:%X\n", 
     (uint8_t)tmpChar[0], (uint8_t)tmpChar[1], (uint8_t)tmpChar[2], (uint8_t)tmpChar[3], 
     (uint8_t)tmpChar[4], (uint8_t)tmpChar[5], (uint8_t)tmpChar[6], (uint8_t)tmpChar[7]); 


    return 0; 
} 

これは、最初の8つの素数を返す2の平方根を計算し、それが実際のバイト値が格納されるメモリ位置から直接フェッチ:

value 1.414214 
size of double 8 
address of variable 6881016 
raw values 
1:CD 
2:3B 
3:7F 
4:66 
5:9E 
6:A0 
7:F6 
8:3F 

ウィキペディアの記事0x6a09e667で与えられた値と比べると、私がここでやっていることはひどく間違っています。再マッピングが起こっているか、二重表現のバイナリ表現ですか?誰かが正しい方向にポイントして、16進数の小数部分を正しく計算する方法はありますか?

: ありがとうございます!それはかなりではありませんが、今のところうまくいきます。 EDIT2 よりよい実装を少し

printf("raw fractional part:\n0x%02X %02X %02X %02X %02X %02X %02X\n", 
     (uint8_t)(0xf & tmpChar[6]), (uint8_t)tmpChar[5], (uint8_t)tmpChar[4], (uint8_t)tmpChar[3], 
     (uint8_t)tmpChar[2], (uint8_t)tmpChar[1], (uint8_t)tmpChar[0]); 


    uint32_t fracPart = (0xf & tmpChar[6]); 
    fracPart <<= 8; 
    fracPart |= tmpChar[5]; 
    fracPart <<= 8; 
    fracPart |= tmpChar[4] ; 
    fracPart <<= 8; 
    fracPart |= tmpChar[3]; 
    fracPart <<= 4; 
    fracPart |= (0xf0 & tmpChar[2]) >> 4; 
    printf("fractional part: %X\n", fracPart); 

uint32_t fracPart2 = *(uint32_t*)((char*)&tmp + 3);  // point to fractional part - 4 bit 
    fracPart2 <<= 4;          // shift to correct value 
    fracPart2 |= (0xf0 & *((char*)&tmp + 2)) >> 4;   // append last 4 bit 
    printf("beautiful fractional part: %X\n", fracPart2); 

このソリューションは非常にプラットフォームに依存し、第二のアプローチでは、私はコメント2のリンクのように何かのためつもりです。

EDIT3

これは私の最終的な解法で、doubleの内部表現に依存せず、単に数式を使って小数を計算します。

uint32_t getFractionalPart(double value) 
{ 
    uint32_t retValue = 0; 

    for (uint8_t i = 0; i < 8; i++) 
    { 
    value = value - floor(value); 
    retValue <<= 4; 
    value *= 16; 
    retValue += floor(value); 
    } 

    return retValue; 
} 
+1

あなたが疑われるとして、ダブル、フロートのバイナリ表現は、簡単ではありません。私は数学的に、バイトと疑わしいポインタのキャストを囲むのではなく、16進数の文字列を作成することをお勧めします。 (繰り返して16を掛けて整数部分を得る) – qxz

+0

0x6a09e667の√2は16進数の1.6a09e667 ... 16です。http://stackoverflow.com/questions/20650954/how-to-convert-decimal-fractions-to- 16進分数。 'double'はこのようには表されません。 https://en.wikipedia.org/wiki/Double-precision_floating-point_formatを読んでください。 – kennytm

答えて

0

ここで2重は64ビットであることに留意してください。 以下のリンクでdouble型のIEEE表現を見ると、1つの符号ビットと11の指数ビットがあり、残りは精度ビットです。

あなたが得た出力を見ると、引用符で囲んだニブルを見てみましょう。よく見かけますか? 番号が後方にある理由は、エンディアンのためです。 12番目のビットは、小数部分がこの場合に開始しているところであり、次にそれは後方に移動する。

1:CD 
2:3B 
3:'7'F 
4:'66' 
5:'9E' 
6:'A0' 
7:F'6' 
8:3F 

または

8:3F 
7:F'6' 
6:'A0' 
5:'9E' 
4:'66' 
3:'7'F 
2:3B 
1:CD 

https://en.wikipedia.org/wiki/Double-precision_floating-point_format