0

I am going over the description of Sha-512. It is mentioned that the initial hash value consists of the sequence of 64-bit words that are obtained by taking the fractional part of the first eight primes. I am trying to replicate these values in Python, but I am not getting the same results. To include more digits, I am using the mpmath library.

from mpmath import *

mp.dps = 50

sqrt(2)
# mpf('1.4142135623730950488016887242096980785696718753769468')

mpf(0.4142135623730950488016887242096980785696718753769468 * 2 ** 64)
# mpf('7640891576956012544.0')

hex(7640891576956012544)
# '0x6a09e667f3bcc800'

However, the description indicates this value must be 6a09e667f3bcc908. As it can be seen, the result I get differs in the last three digits from what I should be getting according to the description. I was wondering why that is the case, and what is the correct approach.

I have come across a similar question, but adjusting it for 64-bit word would yield:

import math
hex(int(math.modf(math.sqrt(2))[0]*(1<<64)))
# '0x6a09e667f3bcd000'

which actually differs in the last four digits.

Josh
  • 131
  • 12
  • 2
    Your expression `0.4142135623730950488016887242096980785696718753769468 * 2 ** 64` was performed entirely with Python float objects, with their limited precision. Applying `mpf()` to the result will not magically restore the lost significant digits. – jasonharper Mar 08 '22 at 19:28
  • That is a good point. I just tried `fmul(0.4142135623730950488016887242096980785696718753769468, power(2, 64))`, and still get the same result. – Josh Mar 08 '22 at 19:33
  • 1
    No, `0.4142135623730950488016887242096980785696718753769468` all on its own is a Python float literal, and rounded back to 53 significant bits before it's used. – Tim Peters Mar 08 '22 at 19:34
  • I just passed it in `mpf`. But I see your point, I guess the issue is `hex` does not take a `mpf` number, as I am getting the following error: `TypeError: 'mpf' object cannot be interpreted as an integer`. Do you happen to know the fix for it? So I can use `mpmath` instead of the `decimal` package. – Josh Mar 08 '22 at 19:41
  • Don't copy/paste pieces of `mpath`'s output. Use `mpmath` to _do_ as much of the calculation as possible, akin to how I used `decimal` in my answer. After all the "hard parts" are done, then convert to int, and then apply `hex()`. – Tim Peters Mar 08 '22 at 19:45
  • Got it! I found the following code does what I was looking for: `hex(int(fmul(frac(sqrt(2)), pow(2, 64))))` – Josh Mar 08 '22 at 20:02

1 Answers1

2

As a comment already explained, you're actually only using 53 bits in your calculations (native CPython float precision).

Here's an easy way to reproduce the result you're apparently after:

>>> import decimal
>>> x = decimal.getcontext().sqrt(2) - 1
>>> x
Decimal('0.414213562373095048801688724')
>>> hex(int(x * 2**64))
'0x6a09e667f3bcc908'

Nothing really magical about decimal. It just happens to use enough precision by default. You could certainly do the same with mpmath.

For example,

>>> import mpmath
>>> mpmath.mp.prec = 80
>>> hex(int(mpmath.frac( mpmath.sqrt(2) ) * 2**64))
'0x6a09e667f3bcc908'
Tim Peters
  • 67,464
  • 13
  • 126
  • 132