欢迎光临散文网 会员登陆 & 注册

第 9 讲:运算符(一):算术运算符

2021-03-25 08:52 作者:SunnieShine  | 我要投稿

Part 1 运算符是什么?

为了简化使用 C# 程序,C# 使用一些类似于数学符号的东西来表示一些运算,进而使得代码更具有可读性。

可读性(Readability)是一种神奇的玩意儿。当我们使用英语单词的时候,我们会觉得更好看懂代码,毕竟翻译过来就是实际的意思了;使用符号反而不一定能明白。但是,从小学到大,数学符号就一直陪伴我们,以至于我们使用运算符号反而比一些单词要更好看懂,所以,运算符是一个不可或缺的、表达复杂逻辑的一种东西。

C# 里提供了众多的运算符,只有你想不到的,没有它做不到的。大体上 C# 把运算符分成如下若干类:

  • 算术运算符(+-*/%)和正负号(+-);

  • 自增自减运算符(++--);

  • 比较运算符(>>=<<===!=);

  • 逻辑运算符(&&||&|^!)和逻辑元运算符(truefalse);

  • 位运算符(&|^~>><<);

  • 赋值运算符(=+=-=*=/=%=&=|=^=>>=<<=);

  • 条件运算符(?:);

  • 类型判断运算符(isas);

  • 溢出校验运算符(checkedunchecked);

  • 默认运算符(default(T));

  • 强制转换运算符((T))。

按照顺序,我们一个一个来讲解。

为了保证调理清晰,我们按照运算符的类型进行挨个讲解,一种运算符用一篇文章来介绍。虽然内容可能没那么多,但是也不必放在一起,这样文章太长了。

另外,C# 还有一些别的运算符号,比如 .->[]*& 等等。这些运算符具有特殊用途和用法,且并不一定代表一种“运算”,因此我们不单独介绍它们,而是通过贯穿教程的方式对这些内容进行讲解;还有一些在之前的内容也说过了,所以就不用多说了。

Part 2 没啥可讲的?我不信

算术运算符是运算符里最基础、最容易学习的一种运算符类型。它不需要专业编程知识就可以掌握,因为它确实长得很像是数学运算符。

下面我们来针对上述给的这个例子作介绍。我们先不必考虑除法运算,剩下的 +-* 均和数学运算的算法没有区别。

2-1 整数取模运算

最后一个叫做取模运算(%),它虽然写成数学的百分号,但实际上是除法运算。不过取模运算是将数据进行整除,然后取出余数作为结果的一种运算模型。它等价于下面的这个数学公式:

如果我们有式子

a%20%5Cdiv%20b%20%3D%20c%20%5Ctext%7B...%7D%20d%20%5Ctag%7B1%7D

则有数学表达式

a%5C%20%5Ctext%7BMod%7D%5C%20b%20%3D%20d%20%5Ctag%7B2%7D

数学运算符 Mod 等价于 C# 里的取模运算符 %,故上述式子也可以表示为

a%5C%20%5C%25%5C%20b%20%3D%20d%20%5Ctag%7B3%7D

举个例子:

我们可以轻松地得到,17 除以 3 的结果是 5,余数为 2,因此 c 的结果是 2。

另请注意,当取模运算符 % 的左侧这个数我们称为被取模数。整数取模运算一定用的是取模运算的两个计算数值的绝对值。比如 -13 % 7 得当成 13 % 7 进行计算。而结果的正负一定取决于被取模数。换句话说,只有被取模数是负数,结果才会是负数;否则怎么着,结果都是正的。比如 -13 % -7 的结果一定是 -(13 % 7),即 -6。

2-2 整数除法运算

前面我们说到了取模运算,如果说取模运算是取出余数的话,整数除法运算就刚好取的是商。还是看到前面的式子 (1)。这个式子里的 是这个除法式子的商,所以整数除法运算的结果就是这个

C# 里,如果除号 / 两侧的数字全部是整数的话,这个除号就应当看作是一个整数除法运算。因此,对照前面的例子,3 / 4 的结果应该就是 0,因为 3 根本不够被 4 除。如果是 17 / 3 呢?那就是 5 了。

2-3 浮点数除法运算

我们稍微换一下顺序。这次浮点数我们先说除法运算,然后再来讲解取模运算。

当我们依旧使用符号 /,但左右两侧包含浮点数类型的数据的时候,我们就不能套用整数的除法运算了。因为浮点数是包含小数部分的,因此整体的结果就一定会是一个小数。

C# 里约定,当除号 / 的左右两侧数值里,至少有一个是 floatdoubledecimal 的其一的话,我们就认为除法式子里包含小数。此时,就必须照着浮点数除法运算的规则(就是这一部分的内容)来计算。浮点数除法和数学除法没有区别,并不是带余除法。比如说

由于 3 和 4 均是整数,因此我们无法得到小数结果。我们必须得到 0.75 的话,就需要把至少其中一边改成小数,才能得到小数结果。从这则示例里可以看到,我们将 3 强制转换为 double 类型,然后得到一个浮点数除法运算的结果 0.75,最后赋值给 result 变量。

稍微注意一下的是,小括号没有必要写这么多。按照 C# 的习惯,就算你不打括号,C# 也知道这里 (double)3 / 4 是看成 ((double)3) / 4 而不是 (double)(3 / 4) 的。那么,它们有什么区别呢?

如果是前者,那么式子就好比是 3.0 / 4 一样。我们将 3 认为是 3.0。虽然 3 和 3.0 在数学上没区别,但是 C# 里因为字面量类型的问题,3 是 int 类型(整数),而 3.0 是 double 类型(浮点数),因此类型影响着整个运算是整数除法还是浮点数除法。

而如果是后者,式子就相当于是把 3 / 4 的结果得到后,才转换成 double。前文说到,如果除号的两侧没有浮点数的话,那么结果就一定是按整数除法来计算的。那么 3 / 4 的结果就必然为 0,故式子就好比是 (double)0,即 0.0,没有意义的转换。

因此,强制转换运算符仅和它旁边这个数值进行“结合”

这就是浮点数的除法。

2-4 浮点数取模运算

C# 里除了可以对两个整数进行取模运算,还可以对浮点数计算。不过概念稍微和整数的取模运算有一点不一样,但它和数学上的余数运算是一样的算法。

比如 13.3 % 6.3。我们只需要找到一个合适的商,得到 2 这个结果;那么商乘以除数可以得到 12.6 这个结果;那么余数自然就是 13.3 - 12.6 = 0.7。

Part 3 浮点数运算的精度问题

我们尝试看一下如下的计算:

我们可以得到的结果是 0.3。但我们将 float 改成 double

即使我们去掉 F 字面量后缀

你都不会得到 0.3 这个结果。实际上,后面两个的结果的小数位很后面依然有非 0 的数据信息。这是因为什么呢?

这并非是 C# 的计算错误,而是一个“预期的”结果。这我们得回到 IEEE 754 规范上来。这个规范规定和约定,数据需要先转换为二进制的科学计数法表示(即 的格式)。这个式子里,由于一个十进制数要想表达成这个格式,且内存是有限大小的,因此我们无法准备表达一个数值信息,因而会丢失精度。

因此,在存储和赋值的时候,fg 就已经不是真正的 0.1 和 0.2 了,因此结果必然不是真正的 0.3。因此,我们一定要注意浮点数的精度问题。

如果需要避免精度错误,我们需要用 decimal 类型。

这样输出的结果就肯定是 0.3 了,且不会出现精度的问题。

下面我们来思考一个问题。为什么第一个例子(float 类型运算)里,结果依旧是 0.3 呢?精度会导致数据不准确,但为什么答案依旧是准确的 0.3 呢?

答案是因为,0.1 和 0.2 在存储的时候,误差其实很小,以至于比 float 类型的最低精度还要小,所以算出来看起来没有问题,但是实际上答案应当和第二个例子里显示的结果一致(只是误差比精度还小,因此后续的部分被舍弃了),因为等于 0.300000... 的后面就全部为 0 了,自然显示的时候就把全部的小数部分的 0 全给省略了。

Part 4 字符串的加法

由于 C# 里有一种新的数据类型(字符串),这是 C 语言里没有的数据类型,因此它有特殊的运算规则:字符串的加法。

字符串的加法说白了,就是字符串的拼接。

我们通过加法运算符 + 来拼接两个字符串。s + t 将字符串拼接起来,因此输出的内容就是 Hello, Sunnie!

Part 5 总结

本节我们学习了基本的五个运算符。一定要注意浮点数和整数对除法运算的不同行为。稍微注意一下,字符串的加法运算。


第 9 讲:运算符(一):算术运算符的评论 (共 条)

分享到微博请遵守国家法律