我有代码:
float f = -6;
DWORD d = 7;
signed long long ll = -d * f; //should be 42
std::cout << ll;
但是当我编译它时(Visual Studio 2012, Windows 7 x64 ),我看到了输出:
-25769803776
调试器说:
ll -25769803776 __int64
d 7 unsigned long
f -6.00000000 float
我不明白为什么结果(ll
)不是42:float f
和DWORD d
在调试器中有正确的值,signed long long
有很多&# 34;更好&#34;范围,可以保存有符号和无符号值。
我的错误在哪里?
另外,如何保存(使用哪种类型)乘以两个DWORD
或DWORD
和float
的结果(在每个-
之前可以有一个{{1} } sign),例如:
-f * -f
-f * d
-d * d
答案 0 :(得分:2)
d
是一个32位无符号整数。根据C标准,-d
是一个非常大的正数。对于相同大小的整数计算,这工作正常,因为有符号和无符号值具有相同的形式,-d
确实是-6作为整数的“正确”形式。
您可以假设只使用低31位,将其转换为有符号值。如果设置了最高位(使用assert((d & 0x80000000) == 0)
来检查),那么首先需要将其转换为64位值。
或者,您可以添加括号来否定最终结果,即float:
signed long long ll = -(d * f);
Casting to float也会起作用,因为无论如何计算都在float
完成,无论你是否将d
转换为float,结果值都同样精确/不精确 - 编译器会做无论哪种方式。
以下是clang以LLVM-IR形式生成的代码:
define i32 @main() #0 {
entry:
%f = alloca float, align 4
%d = alloca i32, align 4
%ll = alloca i64, align 8
store float -6.000000e+00, float* %f, align 4
store i32 7, i32* %d, align 4
%0 = load i32* %d, align 4
%sub = sub i32 0, %0
%conv = uitofp i32 %sub to float
%1 = load float* %f, align 4
%mul = fmul float %conv, %1
%conv1 = fptosi float %mul to i64
store i64 %conv1, i64* %ll, align 8
ret i32 0
}
请注意uitofp i32 %sub to float
- 这会使d
成为float
值。
(侧面评论:我个人会选择14&amp; 3作为给出42的例子,但无论如何都可以选择好的结果)。
答案 1 :(得分:1)
你否定了一个未签名的号码。请尝试改为-float(d) * f
。如果执行DWORD e = -d;
并在调试器中检查e
,您应该能够看到效果。
答案 2 :(得分:0)
当它在右侧进行计算时,C不会注意赋值左侧的变量类型。
也就是说,在你的例子中,只看“unsigned long”乘以“float”否定一个减号,并且因为规则说已完成作为无符号乘法位补码,它将是。 稍后它会将结果放入ll
,但这不相关!计算在分配之前完成,因此它们是完全分开的。
使用与您在问题中使用的相同类型,在某个时间咬住屁股上的每个C黑客的一个例子:
__int64 x = 1 << 60;
看起来应该在x
中设置一个 1 位 1 位的数字,对吗? (数学上,这个数字是2到60的幂。)但是因为裸1被认为是int
,通常是32位宽,1 << 60
被计算为 0 ,因为32位整数中没有60个位置!
“但我会把它存放在__int64 x
,”你说。 C不关心。