我该如何逐位打印 C 中的 double 类型以查看其底层表示?

4

我想学习计算机如何用二进制表示双精度类型,但是位运算符&|不能用于double。而memcpy(&d, &src, 8)也似乎不起作用。有什么建议吗?


1
你能展示一下你的作业哪部分已经写好了,哪部分没有达到你的预期吗? - Jonathan Feinberg
你说memcpy不起作用是什么意思?你使用的大小正确吗?src和d是双精度浮点数吗?如果是,你应该使用memcpy(&d, &src, sizeof(double)); ... 请更详细地说明memcpy的问题。 - BobbyShaftoe
3
为什么你认为对低级语言细节感到好奇的人都是在作弊?像我这样刚开始学习编程时总是对这些东西很好奇的人来说,我觉得这种态度真的很恼人。 - Jay Conrod
这个程序与许多答案不同,它实际上按位给出了结果,而不是按字节给出。请参见:https://gist.github.com/aspyct/3194882 - jamadagni
7个回答

6

在这里:

#include <stdio.h>
int main ()
{
    double decker = 1.0;
    unsigned char * desmond = (unsigned char *) & decker;
    int i;

    for (i = 0; i < sizeof (double); i++) {
         printf ("%02X ", desmond[i]);
    }
    printf ("\n");
    return 0;
}

你可以尝试一下: http://codepad.org/onHnAcnC

1
尽管拼写错误(应该是Dekker),但变量名+1。 - fvu
1
@Johannes Rössel:C的ISO标准没有定义这样的事情。它只要求每个float都可以精确表示为double,每个double都可以精确表示为long double。这仅意味着sizeof double至少等于sizeof float。实现可以使用任何大小。在具有FPU的机器上,大小很可能由硬件实现确定。在我目前使用的16位dsPIC编译器中,除非使用编译器选项指定64位,否则double为32位。 - Clifford
@fvu:不是DEKKER,是双层巴士。 - user181548
2
但是(也许有点令人困惑),它是德斯蒙德·德克尔:http://en.wikipedia.org/wiki/Desmond_Dekker - Steve Jessop
不错的例子,但实际上是“逐字节”而不是所要求的“逐位”。 - 463035818_is_not_a_number
显示剩余3条评论

2
union {
  double  d;
  unsigned char c[sizeof(double)];
} d;

int main(int ac, char **av) {
  int i;
  char s1[80], s2[80];

  d.d = 1.0;
  for(i = 0; i < sizeof d; ++i) {
    sprintf(s1 + i * 3, " %02x", d.c[i]);
    sprintf(s2 + i * 3, " %02x", d.c[sizeof d - 1 - i]);
  }
  printf("%s\n%s\n", s1, s2);
  return 0;
}

$ ./a.out
00 00 00 00 00 00 f0 3f
3f f0 00 00 00 00 00 00

或者,您可以阅读有关IEEE 754标准的内容,该标准规定了表示方法。

http://en.wikipedia.org/wiki/IEEE_754-1985


1

一个特定的位布局本身是没有意义的。假设我有以下内容:1101

也许我说它是无符号的,表示值为13。

也许它是带符号的,高位表示该值为负数,这意味着现在是-5。

进一步考虑,我认为高两位是基数,低两位是指数,那么我得到的值是3。

你看,它不是存储,而是解释。阅读有关浮点值如何表示和解释的文章,这比查看位的打包方式更有用。


0

这对我有效

#include <stdio.h>
#include <string.h> /* memmove */
int main(void) {
  unsigned char dontdothis[sizeof (double)];
  double x = 62.42;

  printf("%f\n", x);
  memmove(&dontdothis, &x, sizeof dontdothis);
  /* examine/change the array dontdothis */
  dontdothis[sizeof x - 1] ^= 0x80;
  /* examine/change the array dontdothis */
  memmove(&x, &dontdothis, sizeof dontdothis);
  printf("%f\n", x);
  return 0;
}

结果是

62.420000
-62.420000

我认为OP想要输出64个0或1,代表用于编码双精度浮点数的64位。 - user1899861

0

除非您也了解一些典型的IEEE FP表示方式,否则这并不会很有启发性。

很可能您的计算机表示双精度浮点数的方式在此处有所说明。


0

关键在于将 double 转换为 long long(假设 sizeof(double) == sizeof(long long)),而不改变二进制表示。以下是实现此任务的两种方法:

  • 强制类型转换:double a; long long b = *((long long *)&a);
  • 联合体:union { double a ; long long b };

强制类型转换由于类型混淆和严格别名规则而具有未定义的结果。有时这种情况确实会出错,如果优化足够多,一些编译器将在a被初始化之前读取b(假设它已经被初始化),尽管您可能对这种简单情况感到满意。通过联合体进行转换是未定义行为(除非是某些情况,但本例不属于此类情况),但实际上,没有明智的编译器编写者会让它崩溃,因为太多的代码使用它。 - Steve Jessop

0

另一个选择是使用位域。拥有这样的结构和对计算机存储double类型的了解,您可以非常容易地打印出双精度浮点数内部表示的不同部分。就像他们在这里所做的那样。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接