LoginSignup
2
1

More than 3 years have passed since last update.

x86-64 で double の値を uint64_t に変換するのは効率悪いですよという話

Last updated at Posted at 2019-05-08

はじめに

フツーはやらないと思いますが、

uint64_t double2uint64_t(double x)
{
    return (uint64_t)x;
}

こういうやつですね、gcc が uint32_t や int64_t への変換に比べて残念なコード吐くのに気付いたのでメモ。

#include <stdint.h>

uint32_t double2uint32_t(double x)
{
    return (uint32_t)x;
}

int64_t double2int64_t(double x)
{
    return (int64_t)x;
}

uint64_t double2uint64_t(double x)
{
    return (uint64_t)x;
}
double2uint32_t:
        cvttsd2siq      %xmm0, %rax
        ret
double2int64_t:
        cvttsd2siq      %xmm0, %rax
        ret
double2uint64_t:
        movsd   .LC0(%rip), %xmm1
        comisd  %xmm1, %xmm0
        jnb     .L5
        cvttsd2siq      %xmm0, %rax
        ret
.L5:
        subsd   %xmm1, %xmm0
        cvttsd2siq      %xmm0, %rax
        btcq    $63, %rax
        ret
.LC0:
        .long   0
        .long   1138753536

どうしてこうなった

x86-64 では double から uint32_t や int64_t へは 1命令で変換できる命令があるが、uint64_t へはそういう命令がない、ということですね。
あ、ひとつウソ書きました。x86-64 でも AVX-512 に対応しているプロセッサであれば double を uint64_t に 1命令で変換することができるようです。

double2uint64_t:
        vcvttsd2usi     %xmm0, %rax
        ret

で、この煩いコードは何やってんの?

gcc で下記のソースをコンパイルすると凡そ同等のコードを吐かせることができます。

#include <stdint.h>

uint64_t double2uint64_t(double x)
{
    if (x <= INT64_MAX) {
        // int64_t の最大値以下であれば int64_t へ変換
        return (int64_t)x;
    } else {
        // int64_t の最大値より大きければ int64_t の最大値+1 の値を引き int64_t へ変換
        int64_t y = x - (INT64_MAX + 1UL);
        // MSB を反転することでさっき引いた int64_t の最大値+1 分を足したのと同じにする
        return y ^ (1UL << 63);
    }
}
double2uint64_t:
        vmovsd  .LC0(%rip), %xmm1
        vcomisd %xmm0, %xmm1
        jb      .L6
        vcvttsd2siq     %xmm0, %rax
        ret
.L6:
        vsubsd  %xmm1, %xmm0, %xmm0
        vcvttsd2siq     %xmm0, %rax
        btcq    $63, %rax
        ret
.LC0:
        .long   0
        .long   1138753536

要は、元の値が 0~INT64_MAX の範囲であれば double → int64_t の命令を使用して変換、それより大きい場合は INT64_MAX+1 分ゲタ履いてることにして double → int64_t 変換するということです。

なぜ気付いた

こちらの記事で x86-64 用に不思議なコードを吐かせているのを見掛けたことがきっかけでした。株式会社フィックスターズ様と記事を書かれた方には感謝いたします。

おわりに

おわりです。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1