20

例如,当我将两个整数相除并希望返回一个浮点数时,我迷信地写了这样的东西:

int a = 2, b = 3;
float c = (float)a / (float)b;

如果我不强制转换ab浮动,它将进行整数除法并返回一个 int。

同样,如果我想将带符号的 8 位数字与无符号的 8 位数字相乘,我会在相乘之前将它们转换为有符号的 16 位数字,以免溢出:

u8 a = 255;
s8 b = -127;
s16 = (s16)a * (s16)b;

当根本不进行转换或仅转换其中一个变量时,编译器在这些情况下的行为究竟如何?我真的需要显式地转换所有变量,或者只是左边的一个,还是右边的那个?

4

9 回答 9

28

问题一:浮点除法

int a = 2, b = 3;
float c = static_cast<float>(a) / b;  // need to convert 1 operand to a float

问题 2:编译器的工作原理

要记住的五个经验法则:

  • 算术运算总是对相同类型的值执行。
  • 结果类型与操作数相同(提升后)
  • 对 int 执行的最小类型算术运算。
  • ANSCI C(以及 C++)使用值保留整数提升。
  • 每个操作都是独立完成的

ANSI C 规则如下:
这些规则中的大多数也适用于 C++,尽管并非所有类型都得到官方支持(目前)。

  • 如果任一操作数是long double,则另一个操作数将转换为long double
  • 如果任一操作数是double,则另一个操作数将转换为double
  • 如果任一操作数是浮点数,则另一个操作数将转换为浮点数
  • 如果任一操作数是unsigned long long,则另一个操作数将转换为unsigned long long
  • 如果任一操作数是long long,则另一个操作数将转换为long long
  • 如果任一操作数是unsigned long,则另一个操作数将转换为unsigned long
  • 如果任一操作数为long,则将另一个操作数转换为long
  • 如果任一操作数是unsigned int,则另一个操作数将转换为unsigned int
  • 否则,两个操作数都将转换为int

溢出

溢出总是一个问题。笔记。结果的类型与输入操作数相同,因此所有操作都可能溢出,所以是的,您确实需要担心它(尽管该语言没有提供任何明确的方法来捕捉这种情况。

附带说明:
无符号除法不能溢出,但有符号除法可以。

std::numeric_limits<int>::max() / -1  // No Overflow
std::numeric_limits<int>::min() / -1  // Will Overflow
于 2008-10-29T06:42:47.827 回答
13

通常,如果操作数属于不同类型,编译器会将所有操作数提升为最大或最精确的类型:

如果一个数字是……而另一个是……编译器将提升为……
------------------- -------- ------------ ------------------
char int int
有符号 无符号 无符号
char 或 int float 浮点数
浮动双双

例子:

字符 + 整数 ==> 整数
有符号整数 + 无符号字符 ==> 无符号整数
浮点数 + 整数 ==> 浮点数

但请注意,该提升仅在每个中间计算所需时发生,因此:

4.0 + 5/3 = 4.0 + 1 = 5.0

这是因为首先执行整数除法,然后将结果提升为浮点数以进行加法。

于 2008-10-29T04:25:27.027 回答
5

你可以只施放其中一个。不过不管是哪一个。

每当类型不匹配时,“较小”类型会自动提升为“较大”类型,浮点比整数类型“更大”。

于 2008-10-29T04:02:00.477 回答
2

整数除法:强制转换任何一个操作数,不需要同时转换它们。如果两个操作数都是整数,则除法运算为整数除法,否则为浮点除法。

至于溢出问题,无需显式转换,因为编译器会为您隐式执行此操作:

#include <iostream>
#include <limits>

using namespace std;
int main()
{
    signed int a = numeric_limits<signed int>::max();
    unsigned int b = a + 1; // implicit cast, no overflow here
    cout << a << ' ' <<  b << endl;
    return 0;
}
于 2008-10-29T04:01:29.230 回答
1

在浮点除法的情况下,只要一个变量是浮点数据类型(float 或 double),那么另一个变量应该扩展为浮点类型,并且应该发生浮点除法;所以没有必要将两者都转换为浮点数。

话虽如此,无论如何,我总是将两者都转换为浮动。

于 2008-10-29T04:02:52.673 回答
1

我认为只要您只转换两个变量之一,编译器就会正常运行(至少在我知道的编译器上)。

所以所有的:

浮动 c = (浮动)a / b;

浮动 c = a / (float)b;

浮动 c = (float)a / (float)b;

将有相同的结果。

于 2008-10-29T04:04:39.393 回答
1

还有一些像我这样的大脑受损的人,他们不得不使用老式的语言,只是不假思索地写出类似的东西

int a;
int b;
float z;

z = a*1.0*b;

当然这不是通用的,仅适用于这种情况。

于 2008-10-29T05:45:34.900 回答
1

在从事安全关键系统工作后,我倾向于偏执并总是同时考虑两个因素:float(a)/float(b)——以防万一以后有一些微妙的陷阱计划咬我。无论编译器被认为有多好,无论官方语言规范中的细节定义多么明确。偏执狂:程序员最好的朋友!

于 2008-10-29T05:48:01.963 回答
0

您需要铸造一侧或两侧吗?答案不是由编译器决定的。它必须知道确切的、精确的规则。相反,答案应该由稍后阅读代码的人来决定。仅出于这个原因,将双方都转换为相同的类型。隐式截断可能足够明显,因此强制转换可能是多余的。

例如这个cast float->int 是显而易见的。

int a = float(foo()) * float(c); 
于 2008-10-29T11:33:56.040 回答