2012-07-27 58 views
0

是的我知道,对double值进行按位操作似乎是一个坏主意,但我确实需要它。C++通过长长指针中断输出双位输入

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

其实我尝试一种特殊的MOD给Mozilla的Tamarin(ActionScript虚拟机)。其中,任何对象都有为其类型保留的前3位(例如,double是7)。这些位降低了原始数据类型的精度(int只有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,这显然是错误的。 4th和5th对于十六进制和浮点数也都为零,但修改后的比特以十六进制值显示。所以我想,也许这个类型混淆了这里的事情。所以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被破坏了吗?我在这里有一个大/小端的困惑吗?我无意中创建了某种缓冲区溢出?

如果有人有兴趣,在原来的问题(ta thing的事情,见上文),这几乎是相反的。在那里,最后三位已经被设置(表示双倍)。将它们设置为零可以正常工作(这是最初的实现)。将2更多地设置为零具有与上述相同的效果(总体值被置为零)。顺便说一下,它不是特定于输出的,而且数学运算似乎可以与这些地形数值一起工作(得到的2个数值的mul结果为0)。

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

回答

4

很好,他们表现出15.25,但它应该是15.2500000000000550670620214078

默认情况下,%f显示精度6位,所以你会看不出来区别。您还需要使用ll修饰符指定第一个参数是long long而不是int;否则,它可能会打印垃圾。如果你解决这个问题,并使用更高的精度,如%.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是一个演示。

+0

谢谢迈克。那个流氓=实际上是一个复制粘贴错误,我发现在写这个问题的时候并没有在这里纠正它。 LL是我以前不知道的东西,所以非常感谢。 有什么奇怪的是:当我把这些%.30f放在printf的时候,他们没有再显示0.0,但实际上是15.25。 – 2012-07-27 11:04:15

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); 

,你的代码将工作

+0

取消,我有一个litte错误,你的权利!谢谢! – 2012-07-27 11:20:31

0

在32位常量不能大于长(32位),所以你不能这样做:

*l |= 0x10000000000;

您必须创建一个变量,然后将其移位。

long long ll = 1; 
ll <= 32; 
*l |= ll; 
+0

这种说法是错误的。 – 2012-07-27 10:49:18

+0

@ManuelAmstutz:这对于C++ 03是正确的;没有定义“long long”,所以整数文字不能大于long,这在很多平台上都是32位。在C++ 11中,如果对于较小类型的值太大,整数文字可以具有“long long”类型。 – 2012-07-27 10:53:29

+0

好的,我的评论是非常不准确的。 Sascha写道他正在使用VS2008。对于所有VC版本从2003 - 2010 long long是相当于__int64: – 2012-07-27 11:10:57