我对隐式类型转换感到有点困惑。鉴于以下计划
float x = 4.23423451;
double y = 4.23423451;
float z = 101.9876;
float res1 = x * z;
float res2 = y * z;
std::cout << "res1 & res2 " << res1 << " & " << res2 << std::endl;
std::cout << "equality " << (res1 == res2) << std::endl;
输出
res1 & res2 431.839 & 431.839
equality 1
我的问题是“对于x,y&amp; z(x = y)的任何值以及任何编译器,等式是否总是正确的?”
在
res2 = y * z;
变量“y”是否会被类型转换为float或变量“z”是否被类型转换为double?
答案 0 :(得分:7)
请参阅my comments。
这是明确定义的。
z
的中间表达式将扩展为double
,因此y * z
将是double
表达式。然后,隐式缩小转换会将其转换为float
以存储在res2
中。同样的缩小适用于res1
。
这反映在C ++ 11标准的§5¶9表达式[expr] 中。
许多期望算术或枚举类型操作数的二元运算符会以类似的方式导致转换并产生结果类型。目的是产生一个通用类型,它也是结果的类型。此模式称为通常的算术转换,其定义如下:
...
- 否则,如果任一操作数为
double
,则另一个操作数应转换为double
。- 否则,如果任一操作数为
float
,则另一个操作数应转换为float
。...
然而,这并不能确保平等能够成立。
话虽如此,
res1
不一定等同于res2
- 它高度依赖于环境中float
和double
的精确度。这两个文字甚至可能不相等 -4.23423451f
甚至不需要等同于4.23423451
。您无法确定static_cast<double>(static_cast<float>(4.23423451))
是否等于4.23423451
。
参见§5.17¶3赋值和复合赋值运算符[expr.ass] 。
如果左操作数不是类类型,则表达式被隐式转换(第4节)到左操作数的cv-unqualified类型。
§4标准转化[转化] 声明如下:
标准转化是具有内置含义的隐式转化。第4条列举了全套此类转换。 标准转换序列是一系列标准转换,按以下顺序排列:
...
- 来自以下设置的零次或一次转换:整数促销,浮点促销,整数转换,浮点转换,浮点积分转换,指针转换,指向成员转换的指针以及布尔转换。
详见§4.6浮点促销[conv.fpprom] ,
- 类型
float
的prvalue可以转换为double
类型的prvalue。价值没有变化。- 此转化称为浮点促销。
醇>
...和§4.8浮点转换[conv.double] ,
浮点类型的prvalue可以转换为另一个浮点类型的prvalue。如果源值可以在目标类型中准确表示,则转换的结果就是精确表示。如果源值在两个相邻目标值之间,则转换结果是这些值中任一个的实现定义选择。否则,行为未定义。
- 醇>
从浮点转化设置中排除允许的转化次数。
这里的问题是我们有多种情况,我们的转化不是促销,而是缩小到可能更低精度的类型(double
到float
)。
基本上,只要您将double
转换为float
,就可能会失去精确度。
答案 1 :(得分:3)
你应该从不比较浮点值是否相等。
答案 2 :(得分:1)
不,这不能保证。 x
和y
不一定具有相同的值。 确实在表达式 double
和x * z
中将两者都提升为y * z
,但将x
提升为双倍的结果不必等于y
。x * z
被评估为float
,而y * z
表达式将z
提升为双倍,并且乘法结果不需要' t相等,以便转换回较窄的类型可能会导致不同的值。
答案 3 :(得分:0)
铸件应保持不变;但是,我看到处理器和操作系统高精度地影响了实际数学。
但是,除此之外,使用static_cast来明确:
float res2 = static_cast<float>(y * static_cast<double>(z));
通过这种方式,每个人都知道你的意思,并且你的意思是铸造它。