SHA-256初始哈希值计算疑问:为何部分FIPS 180-4给定值与自行计算值存在偏移?
Let's break down why your calculated initial hash values (H2-H7) don't match the FIPS 180-4 specification, and fix the issue in your Java code.
The Root Cause
Your approach to extracting the fractional bits is incorrect because you're not accounting for how double-precision floating-point numbers represent values. For numbers greater than or equal to 1 (like the square roots of primes we're using here), double-precision format stores the value as:
- 1 sign bit
- 11 exponent bits
- 52 mantissa bits (which represent the fractional part of
1.mantissa, not just the raw fractional part of the number)
The key mistake: you're trying to extract bits directly from the raw long representation without including the implicit leading 1 in the mantissa, and your bit-shifting logic is misaligned with how the fractional part maps to the mantissa.
Correct Approach to Extract the First 32 Fractional Bits
The initial SHA-256 hash values are derived from the first 32 bits of the fractional part of the square root of the first 8 primes. To get this correctly:
- Take the square root of the prime.
- Subtract the integer part to isolate the pure fractional value (e.g.,
sqrt(5) = 2.23606...→ fractional part is0.23606...). - Multiply this fractional value by
2^32and take the integer result (this gives you the first 32 bits of the fractional part as a 32-bit integer).
Alternatively, using bit manipulation (more efficient):
- Convert the double value to its raw 64-bit long representation.
- Extract the 52-bit mantissa.
- Add the implicit leading
1to form the full 53-bit significand (1.mantissa). - Shift right to keep only the top 32 bits of the fractional part (since the fractional part is
0.mantissa, the first 32 bits correspond to the top 32 bits of the mantissa).
Fixed Java Code
Here's how to modify your firstFractionalParts method to get the correct values:
private static long firstFractionalParts(double d, int k) { // Option 1: Mathematical approach (simpler to understand) double fractional = d - Math.floor(d); return (long) (fractional * (1L << k)); // Option 2: Bit manipulation approach (more efficient) /* long rawBits = Double.doubleToRawLongBits(d); long mantissa = rawBits & 0x000FFFFFFFFFFFFFL; // Extract 52-bit mantissa long fullSignificand = (1L << 52) | mantissa; // Add implicit leading 1 return fullSignificand >>> (53 - k); // Keep top 32 bits of the fractional part */ }
Why This Works
Let's verify with H2 (prime = 5):
sqrt(5) ≈ 2.2360679775- Fractional part:
0.2360679775 - Multiply by
2^32(4294967296):0.2360679775 * 4294967296 ≈ 1013904242 - Convert to hex:
0x3c6ef372→ which matches the FIPS 180-4 H2 value exactly.
Your original code's bit-shifting logic was rotating and cutting bits incorrectly, leading to offset values. The fixed methods correctly isolate and extract the fractional bits as required by the SHA-256 specification.
Bonus: Verify Your Prime List
Make sure your PRIMES array starts with the first 8 primes: [2, 3, 5, 7, 11, 13, 17, 19]. Your initPrimes function should generate this correctly, but double-checking avoids another potential source of mismatch.
内容的提问来源于stack exchange,提问作者Rail Yulgutlin

