在 C/C++ 中进行数学运算时,我应该对哪些变量进行类型转换?
Posted
技术标签:
【中文标题】在 C/C++ 中进行数学运算时,我应该对哪些变量进行类型转换?【英文标题】:Which variables should I typecast when doing math operations in C/C++? 【发布时间】:2008-10-29 03:55:06 【问题描述】:例如,当我将两个整数相除并希望返回一个浮点数时,我迷信地写了这样的东西:
int a = 2, b = 3;
float c = (float)a / (float)b;
如果我不将 a
和 b
转换为浮点数,它将进行整数除法并返回一个 int。
同样,如果我想将一个有符号的 8 位数字与一个无符号的 8 位数字相乘,我会在相乘之前将它们转换为有符号的 16 位数字,以免溢出:
u8 a = 255;
s8 b = -127;
s16 = (s16)a * (s16)b;
当根本不进行转换或仅转换其中一个变量时,编译器在这些情况下的行为究竟如何?我真的需要显式转换所有变量,还是只转换左边的变量,还是右边的变量?
【问题讨论】:
【参考方案1】:问题一:浮点除法
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。 如果任一操作数是 float,则另一个操作数将转换为 float。 如果任一操作数是 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
【讨论】:
不错的概述 - 也许使用术语“提升”iso“转换”? “溢出总是一个问题”。除了你真正想要的模数运算这种千载难逢的情况。【参考方案2】:一般来说,如果操作数属于不同类型,编译器会将所有操作数提升为最大或最精确的类型:
如果一个数字是……而另一个是……编译器将提升为…… -------------------- -------- ------------ ------------------ char int int 有符号 无符号 无符号 char 或 int float 浮点数 浮动双双例子:
字符 + 整数 ==> 整数 有符号整数 + 无符号字符 ==> 无符号整数 浮点数 + 整数 ==> 浮点数但请注意,提升仅在每次中间计算所需时发生,因此:
4.0 + 5/3 = 4.0 + 1 = 5.0
这是因为先进行整数除法,然后将结果提升为浮点数进行加法。
【讨论】:
我想你的意思是“有符号 + 无符号 = 有符号”? Kasprzol:规则将有符号变量提升为无符号变量;推广一般是从小到大类型,然后从有符号到无符号。【参考方案3】:你可以只施放其中一个。不管是哪一个。
当类型不匹配时,“较小”类型会自动提升为“较大”类型,浮点比整数类型“更大”。
【讨论】:
【参考方案4】:整数除法:转换任意一个操作数,无需同时转换。如果两个操作数都是整数,则除法运算为整数除法,否则为浮点除法。
至于溢出问题,不需要显式强制转换,因为编译器会为您隐式执行此操作:
#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;
【讨论】:
【参考方案5】:在浮点除法的情况下,只要一个变量是浮点数据类型(float或double),那么另一个变量应该扩展为浮点类型,浮点除法应该发生;所以没有必要将两者都转换为浮点数。
话虽如此,无论如何,我总是将两者都转换为浮点数。
【讨论】:
【参考方案6】:我认为只要你只转换两个变量之一,编译器就会正常运行(至少在我知道的编译器上)。
所以所有的:
float c = (float)a / b;
float c = a / (float)b;
float c = (float)a / (float)b;
会有同样的结果。
【讨论】:
【参考方案7】:还有像我这样的老脑残类型,不得不使用老式语言,只是不假思索地写出类似
的东西int a;
int b;
float z;
z = a*1.0*b;
当然这不是通用的,仅适用于这种情况。
【讨论】:
【参考方案8】:在安全关键系统上工作过,我倾向于偏执并总是同时考虑两个因素:float(a)/float(b) - 以防万一以后有一些微妙的陷阱计划咬我。无论编译器被认为有多好,无论官方语言规范中的细节定义多么明确。妄想症:程序员最好的朋友!
【讨论】:
【参考方案9】:您需要投射一侧还是两侧?答案不是由编译器决定的。它必须知道确切的、精确的规则。相反,答案应该由稍后阅读代码的人来决定。仅出于这个原因,将双方都转换为相同的类型。隐式截断可能足够明显,因此强制转换可能是多余的。
例如这个 cast float->int 很明显。
int a = float(foo()) * float(c);
【讨论】:
以上是关于在 C/C++ 中进行数学运算时,我应该对哪些变量进行类型转换?的主要内容,如果未能解决你的问题,请参考以下文章