打印 float/double 的二进制表示对于两个不同的数字给出相同的结果
Printing a binary representation of float/double gives the same result for two different numbers
我必须(从内存中)打印浮点数/双精度数的二进制表示形式。虽然它对整数工作得很好,但我对一些浮点数有奇怪的行为。
我的意思是,3.14 和 21.37 的结果相同(双倍)。对于 8.5,我得到 0。
21.37 双倍:
0101000111101011100001010001111101010001111010111000010100011111
3.14 为双:
0101000111101011100001010001111101010001111010111000010100011111
8.5 为双倍:
0000000000000000000000000000000000000000000000000000000000000000
#include <stdio.h>
int printBit(int c, int i) {
return (c & (1 << (i - 1))) ? 1 : 0;
}
int main()
{
double f;
int *b;
scanf("%lf", &f);
b = &f;
int i;
printf("Jako double: \n");
for (i = sizeof(f) * 8; i > 0; i--)
{
printf("%i", printBit(*b, i));
}
printf("\n");
}
"pragmatic" 的答案是 sizeof(int)
和 sizeof(double)
在您的平台上可能不一样;后者可能更大,所以你只读回 double
的一部分,这在两种情况下都是相同的。
但为了更深入地研究,语句 b = &f;
违反了 严格别名 ,因此您的程序的行为在 b
点未定义被取消引用。
一个很好的方法是通过将 &f
强制转换为 const unsigned char*
来读取内存(这是严格别名规则的一个例外),并对该指针使用指针算法直到 sizeof(f)
.
你的功能肯定没有打印正确的数据。
问题是您系统中的 sizeof(int) 小于 sizeof(double)。也存在严格的别名问题。这实际上是严格理论上的,但一般来说根本不要使用指针双关语。
那么如何双关类型:
- 使用联合
- 使用 memcpy
- 使用字符数组。
方法一和方法三如下所示。
联盟双关 (https://godbolt.org/z/qxi8sK)
void prinFloatAsUnsigned(float x)
{
union
{
uint32_t u;
float f;
}u32 = {.f = x};
printf("sizeof(float) = %zu flaot as unsigned = 0x%x\n", sizeof(float), u32.u);
}
int main(void)
{
prinFloatAsUnsigned(3.14f);
}
memcpy 方法
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
void printasbin(void *buf)
{
uint64_t val;
memcpy(&val, buf, sizeof(val));
for(int x = 63; x >=0; x--)
printf("%c", '0' + !!(val & (1ULL << x)));
printf("\n");
}
int main(void)
{
double x = 3.14;
printf("%f = \t",x);
printasbin(&x);
x = 21.37;
printf("%f = \t",x);
printasbin(&x);
x = 8.5;
printf("%f = \t",x);
printasbin(&x);
}
结果:
3.140000 = 0100000000001001000111101011100001010001111010111000010100011111
21.370000 = 0100000000110101010111101011100001010001111010111000010100011111
8.500000 = 0100000000100001000000000000000000000000000000000000000000000000
或按您的方式打印(稍作修改 - 在 IT 中,我们从零开始计算位)
int printBit(void *buff, int i)
{
unsigned char *data = buff;
return !!(data[i / 8] & (1 << (i & 7)));
}
int main(void)
{
double f = 3.14;
printf("Jako double: %f\n", f);
for (int i = sizeof(f) * 8 - 1; i >= 0; i--)
{
printf("%d", printBit(&f, i));
}
printf("\n");
f = 21.37;
printf("Jako double: %f\n", f);
for (int i = sizeof(f) * 8 - 1; i >= 0; i--)
{
printf("%d", printBit(&f, i));
}
printf("\n");
f = 8.5;
printf("Jako double: %f\n", f);
for (int i = sizeof(f) * 8 - 1; i >= 0; i--)
{
printf("%d", printBit(&f, i));
}
printf("\n");
结果:
Jako double: 3.140000
0100000000001001000111101011100001010001111010111000010100011111
Jako double: 21.370000
0100000000110101010111101011100001010001111010111000010100011111
Jako double: 8.500000
0100000000100001000000000000000000000000000000000000000000000000
我必须(从内存中)打印浮点数/双精度数的二进制表示形式。虽然它对整数工作得很好,但我对一些浮点数有奇怪的行为。
我的意思是,3.14 和 21.37 的结果相同(双倍)。对于 8.5,我得到 0。
21.37 双倍:
0101000111101011100001010001111101010001111010111000010100011111
3.14 为双:
0101000111101011100001010001111101010001111010111000010100011111
8.5 为双倍:
0000000000000000000000000000000000000000000000000000000000000000
#include <stdio.h>
int printBit(int c, int i) {
return (c & (1 << (i - 1))) ? 1 : 0;
}
int main()
{
double f;
int *b;
scanf("%lf", &f);
b = &f;
int i;
printf("Jako double: \n");
for (i = sizeof(f) * 8; i > 0; i--)
{
printf("%i", printBit(*b, i));
}
printf("\n");
}
"pragmatic" 的答案是 sizeof(int)
和 sizeof(double)
在您的平台上可能不一样;后者可能更大,所以你只读回 double
的一部分,这在两种情况下都是相同的。
但为了更深入地研究,语句 b = &f;
违反了 严格别名 ,因此您的程序的行为在 b
点未定义被取消引用。
一个很好的方法是通过将 &f
强制转换为 const unsigned char*
来读取内存(这是严格别名规则的一个例外),并对该指针使用指针算法直到 sizeof(f)
.
你的功能肯定没有打印正确的数据。
问题是您系统中的 sizeof(int) 小于 sizeof(double)。也存在严格的别名问题。这实际上是严格理论上的,但一般来说根本不要使用指针双关语。
那么如何双关类型:
- 使用联合
- 使用 memcpy
- 使用字符数组。
方法一和方法三如下所示。
联盟双关 (https://godbolt.org/z/qxi8sK)
void prinFloatAsUnsigned(float x)
{
union
{
uint32_t u;
float f;
}u32 = {.f = x};
printf("sizeof(float) = %zu flaot as unsigned = 0x%x\n", sizeof(float), u32.u);
}
int main(void)
{
prinFloatAsUnsigned(3.14f);
}
memcpy 方法
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
void printasbin(void *buf)
{
uint64_t val;
memcpy(&val, buf, sizeof(val));
for(int x = 63; x >=0; x--)
printf("%c", '0' + !!(val & (1ULL << x)));
printf("\n");
}
int main(void)
{
double x = 3.14;
printf("%f = \t",x);
printasbin(&x);
x = 21.37;
printf("%f = \t",x);
printasbin(&x);
x = 8.5;
printf("%f = \t",x);
printasbin(&x);
}
结果:
3.140000 = 0100000000001001000111101011100001010001111010111000010100011111
21.370000 = 0100000000110101010111101011100001010001111010111000010100011111
8.500000 = 0100000000100001000000000000000000000000000000000000000000000000
或按您的方式打印(稍作修改 - 在 IT 中,我们从零开始计算位)
int printBit(void *buff, int i)
{
unsigned char *data = buff;
return !!(data[i / 8] & (1 << (i & 7)));
}
int main(void)
{
double f = 3.14;
printf("Jako double: %f\n", f);
for (int i = sizeof(f) * 8 - 1; i >= 0; i--)
{
printf("%d", printBit(&f, i));
}
printf("\n");
f = 21.37;
printf("Jako double: %f\n", f);
for (int i = sizeof(f) * 8 - 1; i >= 0; i--)
{
printf("%d", printBit(&f, i));
}
printf("\n");
f = 8.5;
printf("Jako double: %f\n", f);
for (int i = sizeof(f) * 8 - 1; i >= 0; i--)
{
printf("%d", printBit(&f, i));
}
printf("\n");
结果:
Jako double: 3.140000
0100000000001001000111101011100001010001111010111000010100011111
Jako double: 21.370000
0100000000110101010111101011100001010001111010111000010100011111
Jako double: 8.500000
0100000000100001000000000000000000000000000000000000000000000000