-DWORD * float保存到有符号的long long - 超出范围或错误的转换?

时间:2014-03-15 12:24:53

标签: c++ casting range

我有代码:

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 fDWORD d在调试器中有正确的值,signed long long有很多&# 34;更好&#34;范围,可以保存有符号和无符号值。

我的错误在哪里?

另外,如何保存(使用哪种类型)乘以两个DWORDDWORDfloat的结果(在每个-之前可以有一个{{1} } sign),例如:

-f * -f
-f * d
-d * d

3 个答案:

答案 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不关心。

相关问题