0

是的,我知道,对双精度值进行按位运算似乎是个坏主意,但我确实需要它。

你不需要阅读我的问题的下一段,只是为了你们的好奇:

我实际上尝试了 Mozilla Tamarin(Actionscript 虚拟机)的特殊模块。在其中,任何对象都为其类型保留前 3 位(例如,双精度为 7)。这些位降低了原始数据类型(仅 29 位等)的精度。对于我的模组,我需要将此区域扩大 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 中运行时,第一个输出是正确的。第二个。3rd 对十六进制和浮点表示都产生 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)!

所以,虽然我有点迷路,但还是有很多困惑。printf 坏了吗?我在这里有大/小端混乱吗?我是否不小心创建了某种缓冲区溢出?

如果有人有兴趣,在原来的问题(狨猴的事情,见上文)它几乎是相反的。在那里,最后三位已经设置(代表双精度)。将它们设置为零可以正常工作(这是原始实现)。将 2 更多设置为零具有与上述相同的效果(整体值被降至零)。顺便说一句,这不是特定于输出的,而且数学运算似乎也适用于那些底值(这样获得的 2 个值的 mul 会导致 0)。

任何帮助,将不胜感激。问候。

4

3 回答 3

4

好吧,他们显示 15.25,但应该是 15.2500000000000550670620214078

默认情况下,%f显示 6 位精度,因此您不会看到差异。您还需要使用修饰符指定第一个参数是long long而不是; 否则,它可能会打印垃圾。如果您修复该问题并使用更高的精度,例如,您应该会看到预期的结果:intll%.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

是一个演示。

于 2012-07-27T10:46:18.737 回答
0

你在printf的参数有错误。如果你传递一个 8 字节的值,你必须使用 %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);

你的代码会起作用

于 2012-07-27T10:35:45.107 回答
0

在 32 位上,常数不能大于 long(32 位),所以你不能这样做:

*l |= 0x10000000000;

您必须创建一个变量然后对其进行移位。

long long ll = 1;
ll <= 32;
*l |= ll;
于 2012-07-27T10:46:08.473 回答