C:SHA-1 的输出作为小端到浮动

C: Output of SHA-1 as little endian to float

根据 this documentation,我的任务是根据字母数字标识符计算颜色值。这个想法是 运行 通过 SHA-1 的标识符字符串,并使用部分结果来计算颜色值。

我在执行以下步骤时遇到问题:

Treat the output as little endian and extract the last-significant 16 bits. (These are the first two bytes of the output, with the second byte being the most significant one.)

这是我目前的情况:

double get_CbCr_angle(const char* identifier) {

    unsigned char temp[SHA_DIGEST_LENGTH];
    char buf[SHA_DIGEST_LENGTH*2];

    // initalise with 0s
    memset(buf, 0x0, SHA_DIGEST_LENGTH*2);
    memset(temp, 0x0, SHA_DIGEST_LENGTH);

    // compute hash into temp
    SHA1((unsigned char *)identifier, strlen(identifier), temp);

    // print from temp into buf and
    // interpret as (signed) chars
    int i = 0;
    for (i=0; i < SHA_DIGEST_LENGTH; i++) {
        sprintf( (char*) &(buf[i*2]), "%02x", temp[i]);
    }

    printf("SHA1 is %s\n", buf);

    // make a union type of char and float so we can read a char array
    // as float.
    union charFloat {
        float f;
        char s[sizeof(float)];
    };

    union charFloat foo;

    // put first two chars (bytes) into union.
    // bracket notation should be fine since both foo and buf are
    // char arrays.
    foo.s[0] = buf[1];
    foo.s[1] = buf[0];

    printf("interpreted as chars: %s\n", foo.s);
    printf("interpreted as float: %f\n", foo.f);  // 0.000000 - why?

}

但我无法通过这种方法获得任何合理的输出。使用更多的字节只会给我 float 的无意义值,以及将其解释为 int.

显然,我不希望有人为我解决这个问题。我非常感谢您提供一些有关查看方向的提示。

非常感谢。

我会通过使用显式移位和 OR 将两个 char 组装成一个 uint16_t 来做到这一点:

uint16_t x = (((uint16_t)(uint8_t)temp[1]) << 8) |
             (((uint18_t)(uint8_t)temp[0]) << 0);

两次强制转换是必要的,以确保每个 char 值在移位之前都是零而不是符号扩展到 uint16_t 的宽度。另请注意,我正在读取 temp,而不是 buf - raw SHA 哈希,而不是十六进制编码的哈希。使用十六进制编码的哈希值会限制 x 可以采用的可能值。

然后,将 x 除以 65536 将其转换为 [0, 1) 范围内的浮点数:

double f = x / 65536.0;

65536 是 216uint16_t 的取值范围是 0 到 65535(含)。除数上的 .0 后缀是使 C 执行浮点除法而不是整数除法所必需的。为了效率,这个除法可以结合下一步乘以2π:

double f = x * (2 * M_PI / 65536);

M_PI(在math.h中声明)已经是一个浮点数,所以我们不再需要在65536上加上.0后缀,但是你需要在括号里加上括号整个子表达式 2 * M_PI / 65536 以强制编译器在编译时计算该部分并仅发出一个运行时乘法。

让我们仔细看看该算法:

  1. Run the input through SHA-1 (RFC 3174 [4]).
  2. Treat the output as little endian and extract the last-significant 16 bits. (These are the first two bytes of the output, with the second byte being the most significant one.)
  3. Divide the value by 65536 (use float division) and multiply it by 2π (two Pi).

该算法并不是说要将前两个字节视为浮点值。上面写着把它们当成一个16位的整数,然后把这个值转成浮点型进行下一步。

此外,在转换值时,您应该使用 temp 而不是 buf,因为 buf 包含表示散列输出的可打印字符,而 temp 包含实际输出.

所以你会像这样转换值:

uint16_t value;
value = temp[0];
value |= temp[1] << 8;

然后将其转换为 double 以进行下一步:

double result = value * 2 * M_PI / 65536;