是的,我知道,对双值进行按位运算似乎是一个坏主意,但实际上我需要它。
你不需要为我的问题阅读下一段,只是为了好奇你们:
我实际上尝试了一个特殊的mod到Mozilla Tamarin(Actionscript虚拟机)。在其中,任何对象都有为其类型保留的前3位(例如,double为7)。这些位降低了原始数据类型的精度(int仅为29位等)。对于我的mod,我需要将此区域扩展2位。这意味着,当您添加2个双精度数时,需要将最后5位设置为零,进行数学计算,然后在结果上重置它们。这就是为什么^^
现在回到代码中。 这是一个显示非常类似问题的最小例子:
double *d = new double;
*d = 15.25;
printf("float: %f\n", *d);
//forced hex output of double
printf("forced bitwise of double: ");
unsigned char * c = (unsigned char *) d;
int i;
for (i = sizeof (double)-1; i >=0 ; i--) {
printf ("%02X ", c[i]);
}
printf ("\n");
//cast to long long-pointer, so that bitops become possible
long long * l = (long long*)d;
//now the bitops:
printf("IntHex: %016X, float: %f\n", *l, *(double*)l); //this output is wrong!
*l = *l | 0x07;
printf("last 3 bits set to 1: %016X, float: %f\n", *l, *d);//this output is wrong!
*l = *l | 0x18;
printf("2 bits more set to 1: %016X, float: %f\n", *l, *d);//this output is wrong!
在VisualStudio2008中运行时,第一个输出是正确的。第二个。对于十六进制和浮点表示,第三次产生0,这显然是错误的。对于十六进制和浮点数,第4和第5也为零,但修改后的位显示在十六进制值中。所以我想,也许这种类型的东西搞砸了。另外2个输出:
printf("float2: %f\n", *(double*)(long long*)d); //almost right
printf("float3: %f\n", *d); //almost right
好吧,他们显示15.25,但应该是15.2500000000000550670620214078。所以我想,嘿,这只是输出中的精确问题。让我们进一步修改:
*l = *l |= 0x10000000000;
printf("float4: %f\n", *d);
再次,输出为15.25(0000),而不是15.2519531250000550670620214078。奇怪的是,另一个强制十六进制输出(见上面的代码)显示根本没有修改d。所以我修改了一下,并意识到第31位(0x80000000)是我可以手动设置的最后一个。和圣洁的莫莉,它实际上对输出有影响(15.250004)!
所以,虽然我有点误入歧途,但仍然存在很多困惑。打印出来了吗?我在这里有一个大/小端混淆吗?我是否意外地创建某种缓冲区溢出?
如果有人感兴趣,在最初的问题(t猴的事情,见上文)中,它几乎是相反的。在那里,最后三位已经设置(代表一个双)。将它们设置为零可以正常工作(这是原始实现)。将2设置为零具有与上述相同的效果(总值被置为零)。顺便说一句,它不是特定于输出的,但是数学操作似乎也适用于那些被覆盖的值(多达2个值的mul,因此得到0)。
任何帮助将不胜感激。 问候。
答案 0 :(得分:4)
好吧,他们显示15.25,但应该是15.2500000000000550670620214078
默认情况下,%f
显示6位数的精度,因此您不会看到差异。您还需要使用long long
修饰符指定第一个参数为int
而不是ll
;否则,它可能会打印垃圾。如果你修复它并使用更高的精度,例如%.30f
,你应该看到预期的结果:
printf("last 3 bits set to 1: %016llX, float: %.30f\n", *l, *d);
printf("2 bits more set to 1: %016llX, float: %.30f\n", *l, *d);
last 3 bits set to 1: 0000000000000007, float: 15.250000000000012434497875801753
2 bits more set to 1: 000000000000001F, float: 15.250000000000055067062021407764
让我们进一步修改:
*l = *l |= 0x10000000000;
printf("float4: %f\n", *d);
你有一个流氓=
提供未定义的行为,因此该值可能会或可能不会被修改(并且程序可能会或可能不会崩溃,打电话给披萨,或破坏宇宙)。此外,如果您的编译器不符合C ++ 11,则整数文字的类型可能不大于long
,这可能只是32位;在这种情况下,它(可能)将变为零。
修复那些(在我的情况下,使用你的代码),我得到了预期的结果:
*l = *l | 0x10000000000LL; // just one assignment, and "LL" to force "long long"
printf("float4: %f\n", *d);
float4: 15.251953
Here是一个示范。
答案 1 :(得分:0)
printf的参数有误。如果传递8byte值,则必须使用 %llx而不是%x。
使用
printf("last 3 bits set to 1: %llX, float: %f\n", *l, *d);
*l = *l | 0x18;
printf("2 bits more set to 1: %llX, float: %f\n", *l, *d);
您的代码将正常工作
答案 2 :(得分:0)
在32位上,常量不能大于长(32位),所以你不能这样做:
*l |= 0x10000000000;
你必须创建一个变量然后转移它。
long long ll = 1;
ll <= 32;
*l |= ll;