double, long double で切り下げた場合の結果が異なる

#include <stdio.h>

void show(long double x) {
  char result[10];
  __asm__("fldl %1\n\t"
          "fstpt %0\n\t"
          : "=m"(result)
          : "m"(x));
  int bits = 0;
  for (int i = 9; i >= 0; i--) {
    for (int j = 7; j >= 0; j--) {
      printf("%d", (result[i] >> j & 1) != 0);
      bits++;
      if (bits == 1 || bits == 16) {
        printf(" ");
      }
    }
  }
  printf("\n");
}

int main() {
  long double a = 0.53;
  long double b = 0.53L;
  show(a);
  show(b);

  long double c = a * 100.0;
  long double d = b * 100.0;
  show(c);
  show(d);

  // 52 53
  printf("%d %d\n", (int)(c), (int)(d));
}

上記のコードの出力は次のようになる。(x86_64 + gcc 5.4.0)

1 011110001111010 1111000010100011110101110000101000111101100000000000000000000000  # 真の値より大きい
1 011110001111010 1111000010100011110101110000101000111101011100001010000000000000  # 真の値より小さい
1 100000101000000 1000000000000000000000000000000000000000000110000000000000000000
1 100000100111111 1111111111111111111111111111111111111111111111111111100000000000
53 52

long double は、指数部 15 bit, 仮数部 63 bit の extended precision によって計算される。
double の仮数部は 52 bit なので、桁落ちする位置が異なり、偶数丸めによって、切り上げられたり切り下げられたりする。

参考: