我的编程空间,编程开发者的网络收藏夹
学习永远不晚

C#中的小数运算怎么理解

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

C#中的小数运算怎么理解

这篇文章主要介绍“C#中的小数运算怎么理解”,在日常操作中,相信很多人在C#中的小数运算怎么理解问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C#中的小数运算怎么理解”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

0x01 先从一个“错误”的答案说起

既然要聊一聊机器是怎么把算术题做错的,那么我们自然要先来看一个机器运算错误的小例子。

#include <stdio.h>  void main(){     float num;     int i ;     num = 0;     for(i = 0; i < 100; i++)     {         num += 0.1;     }     printf("%f\n", num); }

这是一份C语言写成的小程序,逻辑十分简单易懂,所要实现的结果无非是将0.1相加100次之后再输出。我想不需要计算机来计算,我们自己心算就能立刻得出答案&mdash;&mdash;10。那么计算机会交给我们一份什么样的答案呢?下面我们将这份C代码编译并且运行一下。

C#中的小数运算怎么理解

答案一输出,就让人大吃一惊。怎么计算机还不如人的心算吗?如果按照慕容在前言中提到的那些朋友这时可能就开始纠结是否是代码写错了,亦或者是慕容 的电脑出现了什么问题。但事实上代码是正确的,机器也是运行如常的。那么究竟是为什么计算机的运算输给了人的心算呢?这就引出了下一个问题,计算机是如何 处理小数的呢?如果我们了解一下计算机处理小数的机制,那么这一切的迷就能够解开了。(当然如果有朋友用C#来对0.1加100次,之后的结果是10。但 那是C#在幕后为我们做的一些隐藏工作,在计算机处理小数的问题上,本质是一样的)。

0x02 数字的格式

一个程序可以看做是现实世界的一个数字化的模型。现实世界中的一切都可以转化为数字在计算机的世界中重新复活。因此,一个不得不解决的问题就是数字是如何在计算机中表达的。这也是数字格式出现的意义。

众所周知,机器语言全部都是数字,但是本文自然不会关心全部的二进制格式。这里我们只关心现实中有意义的数字是如何在计算机中被表示的。简单而言,有意义的数字大体可以分为以下三种格式。

整数格式

我们在开发的过程中遇到的大部分的数字其实都是整数。而整数在计算机中也是最容易表示的一种。我们遇到的整数都可以使用32位有符号整数来表示(Int32)。当然,如果需要,还有有符号 64 位整数数据类型(Int64)可供选择。至于和整数相对应的便是小数,而小数主要有两种表示方式。

定点格式

所谓定点格式,即约定机器中所有数据的小数点位置是固定不变的。而定点小数的最常见的例子是SQL  Server中的money类型。事实上定点小数已经很不错了,它显然能够适合很多需要处理小数的情况。但是它有一个与生俱来的缺点,那就是由于小数点的 位置固定,因此它能表示范围是受限的。因此下面我们本文的主角就要出场了。

浮点格式

解决定点格式先天问题的方案便是浮点格式的出现。而浮点格式的组成则包括符号、尾数、基数和指数,通过这四部分来表示一个小数。由于计算机内部是二 进制的,因此基数自然而然是2(就如十进制的基数是10一样)。因此计算机在数据中往往无需记录基数(因为总是2),而是只用符号、尾数、指数这三部分来 表示。很多编程语言都至少提供了两种使用浮点格式表示小数的数据类型,即我们常常能见到的双精度浮点数double和单精度浮点数float。同样,在我 们的C#语言中也存在着这两种使用了浮点格式表示小数的数据类型&mdash;&mdash;按照C#语言标准双精度浮点数和单精度浮点数在C#中对应的是 System.Double和System.Single。但是事实上在C#语言中还存在着第三种使用了浮点格式表示小数的数据类型,那就是 decimal类型&mdash;&mdash;System.Decimal。需要注意的是,浮点格式的表示形式有很多,而在C#中遵循的是IEEE 754标准:

  • float单精度浮点数为32位。32位的构造为:符号部分1bit、指数部分8bit以及尾数部分23bit。

  • double双精度浮点数为64位。64位的构造为:符号部分1bit、指数部分11bit以及尾数部分52bit。

C#中的小数运算怎么理解

0x03 表示范围、精度和准确度

既然聊完了数字在计算机中的几种表示形式,那么接下来我们就不得不提一下在选择数字格式时的一些指标。最常见的无非是这几点:表示范围、精度、准确度。

数字格式的表示范围

顾名思义,数字格式的表示范围指的就是这种数字格式所能表示的最小的值***的值范围。 例如一个16位有符号整数的表示范围是从-32768到32767。如果要被表示的数字的值超出了这个范围,那么使用这种数字格式就不能正确的表示这个数 字了。当然在这个范围内的数字也有可能无法被正确的表示,例如16位有符号整数是无法准确表示一个小数的,但是总有一个接近的值是可以用16为有符号整数 格式来表示的。

数字格式的精度

实话实说,精度和准确度让很多人都有一种十分模糊的感觉,似乎是一样的却又有区别。但慕容需要提醒各位注意的是,精度和准确度是两个有巨大差距的概念。

通俗的来讲,数字格式的精度可以认为是该格式有多少信息用来表示一个数字。更高的精度通常意味着能够表示更多的数字,一个最明显的例子便是精度越高 那么这种格式所能表示的数字就越接近真实的数字。例如我们知道1/3如果换算成小数0.3333....是无穷尽的,那么它在五位精度的情况下可以写成 0.3333,而在七位的情况下就又变成了0.333333(当然,如果用七位表示五位,那么就是0.333300)。

数字格式的精度还会影响到计算的过程。举一个简单的例子,如果在计算中我们使用的是一位精度。那么整个计算可能就变成了下面的这种情况:

0.5 * 0.5 + 0.5 * 0.5 = 0.25 + 0.25                                  = 0.2 + 0.2                                  =0.4

而如果我们使用的是两位精度,那么计算过程又会变成下面的情况。

0.5 * 0.5 + 0.5*0.5 = 0.25 + 0.25   =0.5

对比两种精度情况下的计算结果,一位精度情况下的计算结果和正确的结果差了0.1。而使用了两位精度的情况则正常的计算出了结果。因此可以发现在计算的过程中保证精度是一件多么有意义的事情。

数字格式的准确度

数字格式的表示范围、精度都已经介绍完了,那么接下来我们就来介绍一下数字格式的准确度。刚刚已经说过了,准确度和精度是一对经常让人混淆的概念。

那么我们再通俗的给准确度来个注释,简单的说它表示的是该数字格式(特定环境)所表示的数字和真实数字的误差。准确度越高,则意味着数字格式所表示的数字和真实数字的值之间的误差越小。准确度越低,则意味着数字格式所表示的数字和真实数字的值之间的误差越大。

需要注意的一点是数字格式的精度和数字格式的准确度并没有直接的关系,这一点也是很多朋友在概念上常常会混淆的地方。使用低精度的数字格式表示的数字,并不一定要比使用高精度的数字格式所表示的数字的准确度低。

举一个简单的例子:

Byte   num = 0x05; Int16  num1 = 0x0005; Int32  num2 = 0x00000005; Single num3 = 5.000000f; Double num4 = 5.000000000000000;

此时,我们分别使用5种不同的数字格式表示同一个数字5,虽然数字格式的精度(从8位到64位)不同,但是通过数字格式所表示出来的数和真实的数是一样的。也就是说对于数字5,这5种数字格式的准确度相同。

0x04 取整误差

了解了计算机中常见的几种数字格式之后,现在我们再来聊一聊计算机是如何通过数字格式来表示现实世界中的数字的。众所周知,计算机中使用的是0和 1,即二进制,使用二进制表示整数是十分容易的一件事情,不过在使用二进制表示小数时,我们往往会产生一些疑问。例如二进制小数1110.1101换算成 十进制是多少呢?***眼看上去多了一个小数点,似乎让人十分困惑。事实上它的处理和整数是一样的,即将各个数位的数值和位权相乘结果求和。小数点前的位 权,大家都已经十分熟悉了,从右向左分别是0次幂、1次幂、2次幂以此递增,因此小数点前的二进制换算为十进制便是:

1 * 8 + 1 * 4 + 1 * 2 + 0 = 14

而在小数点之后的位权,相应的从左向右分别是-1次幂、-2次幂依次递减。因此小数点之后的二进制转换为十进制便是:

1 * 0.5 + 1 * 0.25 + 0 * 0.125 + 1 * 0.0625 = 0.8125

因此1110.1101这个二进制小数转换为十进制便是14.8125。

通过观察小数点之后的二进制转换为十进制的过程,各位看官是否发现了很有趣的一个事实呢?那就是小数点之后的二进制并不能表示所有的十进制数,换言 之有一些十进制数是无法转换成二进制的。这个很好理解,因为小数点之后,二进制的位权按照除以2的节奏递减,而十进制却是按照除以10的节奏递减。因此如 果小数点后4位用二进制表示,即从.0000~.1111这个范围内连续的二进制数值事实上对应的十进制数是不连续的,所有可能的结果也不过是各个位权 (0.5、0.25、0.125以及0.0625)相加的组合而已。

因此,一个在十进制中十分简单的数字如果用二进制来准确无误的表示,所使用的位数可能会十分长甚至是***的。一个很好的例子便是使用二进制浮点数来表示十进制中的0.1:

double x = 0.1d;

事实上变量x中所保存的值并不是真正的十进制中的0.1,而是一个最接近十进制0.1的二进制浮点数。这是因为无论小数点之后有多少位二进制的数,2的负数次幂都无法相加得到0.1这个结果,因此0.1这个十进制数在二进制中会变成一个***小数。

当然二进制有可能无法准确的表示一个十进制小数很好理解,因为这有点类似于在十进制中我们同样无法准确表示1/3这样的循环小数。

此时,我们便不得不和计算机妥协了。因为我们现在知道了在计算机中使用的数值可能并不等于真实世界中的数值,而是计算机使用某种数字格式表示的一个 十分接近原始数字的一个值。而在整个程序运行的过程中,我们的计算机就要一直使用这个仅仅是近似的数值来参与计算,我们假设真实的数值是n,而计算机事实 上会使用另一个数值n + e(当然e是一个可正可负且十分小的数)来参与计算机中的运算。此时,这个数值e便是取整误差。

而这还仅仅是一个数字在计算机中使用近似值来表示,如果该数值参与到计算中去,那么显然会带来更多误差。这也是本文一开始那个c程序之所以计算错误 的原因,因为无法正确的表示参与计算的值,到***都变成了近似值。当然C#语言相对而言要“高级”了很多,虽然在计算机中也是近似值,但是展示在大家眼前 的至少还是更加符合人们“预期”的值。不过在C#中,小数计算真的是不会出错的吗?毕竟,这一切似乎仅仅是障眼法。

0x05 取与舍,C#的小数

比比是否相等

不知道各位看官在使用一些关系运算符时,有没有留意到直接使用等号比较两个小数是否相等时是否会出现一些意想不到的问题。我身边的朋友使用关系运算 符直接比较两个小数大小的情况比较多,而直接比较两个小数是否相等的情况却不太多。同时我在此也想提醒各位***不要轻易比较两个小数是否相等,即便在C# 这种高级语言中仍然可能得到让人感觉“错误”的答案,这是因为我们事实上比较的是两个小数是否“接近”于相等,而不是两个数是否是真正的相等。下面这个例 子可能会更好的说明这一点:

  1. using System; 

  2.  

  3. class Test 

  4. {    

  5.     static void Main() 

  6.     { 

  7.         double f = Sum (0.1d, 0.2d); 

  8.         double g = 0.3d; 

  9.         Console.WriteLine (f); 

  10.         Console.WriteLine (f==g); 

  11.     } 

  12.     

  13.     static double Sum (double f1, double f2) 

  14.     { 

  15.         return f1+f2; 

  16.     } 

  17. }

我们编译并且运行这段代码,可以看到输出了如下的内容:

C#中的小数运算怎么理解

比较这两个小数的结果并不是true,这和我们的预期并不一样。

浮点数的真模样

我们知道,像上文中的那个二进制小数1110.1101事实上也是按照人类习惯表达出来的,但是计算机可是不能识别这种带小数点的东西的哦。所以计 算机会使用之前介绍的数字格式来表示这样一个数字,那么一个二进制浮点数在计算机中到底是如何表现的呢?其实在上文介绍数字格式的部分已经介绍过了,但是 没有实际看一眼终究是不能有一个直观的认识,那在本文的***,我们就来看一个二进制浮点数的在计算机中真实的样子。

0100000001000111101101101101001001001000010101110011000100100011

这是一个64位的二进制数。如果把它作为一个双精度浮点数,那么它的各部分都分别表示了什么呢?

按照上文介绍浮点数的部分,我们可以将它分成如下几部分:

符号:0

指数部分:10000000100(二进制,可以转换为十进制的1028)

尾数部分:0111101101101101001001001000010101110011000100100011

因此,将它转换为一个用二进制表示的小数,则是:

(-1)^0 * 1.0111101101101101001001001000010101110011000100100011  x 2^(1028-1023)

= 1.0111101101101101001001001000010101110011000100100011  x 2^5

= 101111.01101101101001001001000010101110011000100100011

如果各位读者观察足够仔细的话,是否发现了有趣的一点呢?那就是在这个在计算机中用来表示双精度浮点数的64位数中,尾数部分的几位数字是:0111101101101101001001001000010101110011000100100011

但是经过从计算机中的形式转化成人类使用二进制表示小数的形式之后,数字却变成了1.0111101101101101001001001000010101110011000100100011x 2^5,小数点之前为什么会多出了一个1呢?

这是因为在尾数部分,为了将表现形式多样的浮点数统一为同一种表示方式而规定要将小数点前的值固定为1。由于小数点前的数永远是1,因此为了节省一个数据位,这个1在计算机中并不需要被保存。

那么应该如何保证一个二进制小数的小数点前的值是1呢?这就需要对二进制小数进行逻辑移位了,通过左移或右移若干次后,将整数部分变为1。例如上文中的这个二进制小数:1110.1101,我们就来试试如何把它变成计算机可以识别的浮点数的尾数吧。

1110.1101(原始数据)&mdash;&mdash;>0001.1101101(通过右移将整数部分变为1)&mdash;&mdash; >0001.11011010000000000000....(拓展位数,使之符合数字格式的规定)&mdash;&mdash; >11011010000000000000....(去掉整数部分,仅保留小数部分)

到此,关于“C#中的小数运算怎么理解”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程网网站,小编会继续努力为大家带来更多实用的文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

C#中的小数运算怎么理解

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

C#中的小数运算怎么理解

这篇文章主要介绍“C#中的小数运算怎么理解”,在日常操作中,相信很多人在C#中的小数运算怎么理解问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C#中的小数运算怎么理解”的疑惑有所帮助!接下来,请跟着小编一起来
2023-06-17

C++运算符重载怎么理解

这期内容当中小编将会给大家带来有关C++运算符重载怎么理解,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。C++当中除了函数可以重载之外,其实运算符也是可以重载的。我们之前已经接触过一些,可能大家没有意识到
2023-06-22

Lua中的三目运算怎么理解

这篇文章主要讲解了“Lua中的三目运算怎么理解”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Lua中的三目运算怎么理解”吧!Lua 是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开
2023-06-27

C#中怎么通过运算符重载实现复数运算

今天就跟大家聊聊有关C#中怎么通过运算符重载实现复数运算,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。C#运算符重载实现复数运算的由来:函数的重载——同名函数,不同的参数(包括参数个
2023-06-18

C#中??运算符怎么用

小编给大家分享一下C#中??运算符怎么用,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!C#??运算符可能是一个被遗忘的运算符,很少看到有人用它,它的用法很简单却很
2023-06-17

C#中的位运算符怎么用

本文小编为大家详细介绍“C#中的位运算符怎么用”,内容详细,步骤清晰,细节处理妥当,希望这篇“C#中的位运算符怎么用”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。实例请看下面的实例,了解 C# 中所有可用的位运算
2023-06-17

如何理解C ++中的范围解析运算符

本篇内容介绍了“如何理解C ++中的范围解析运算符”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!1.当存在具有相同名称的局部变量时,要访问全
2023-06-16

理解C语言中+=运算符的重要性

+= 运算符的重要性:+= 运算符在 c 语言中用于将一个变量的值与一个表达式相加并存储到该变量中,它可以简化代码并提高可读性。它省略了使用多个赋值语句进行累加的需要,使代码更简洁易懂。理解 C 语言中 += 运算符的重要性在 C 语言中
理解C语言中+=运算符的重要性
2024-04-04

C#中?、?.、??、??=运算符怎么使用

本文小编为大家详细介绍“C#中?、?.、??、??=运算符怎么使用”,内容详细,步骤清晰,细节处理妥当,希望这篇“C#中?、?.、??、??=运算符怎么使用”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。1. 可空
2023-07-06

C#中的赋值运算符怎么用

这篇文章主要介绍“C#中的赋值运算符怎么用”,在日常操作中,相信很多人在C#中的赋值运算符怎么用问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C#中的赋值运算符怎么用”的疑惑有所帮助!接下来,请跟着小编一起来
2023-06-17

C#的算术运算符怎么用

这篇文章主要介绍“C#的算术运算符怎么用”,在日常操作中,相信很多人在C#的算术运算符怎么用问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C#的算术运算符怎么用”的疑惑有所帮助!接下来,请跟着小编一起来学习吧
2023-06-17

C++的运算符怎么用

这篇文章将为大家详细讲解有关C++的运算符怎么用,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。运算符作用:用于执行代码的运算运算符类型作用算术运算符用于处理四则运算赋值运算符用于将表达式的值赋给变量比较运
2023-06-25

C++中运算符重载怎么用

这篇文章主要介绍C++中运算符重载怎么用,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!运算符重载为什么要使用运算符重载-C/C++的运算符,支持的数据类型,仅限于基本数据类型。问题:一头牛+一头马 = ?(牛马神兽?
2023-06-29

C#的sizeof运算符和三元运算符怎么用

本文小编为大家详细介绍“C#的sizeof运算符和三元运算符怎么用”,内容详细,步骤清晰,细节处理妥当,希望这篇“C#的sizeof运算符和三元运算符怎么用”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。实例usi
2023-06-17

C++中怎么实现重载运算符

C++中怎么实现重载运算符,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。一。重载运算符的时机1。需要在定义的对象间相互赋值时,重载赋值运算符2。需要在数字类型增
2023-06-17

C++中怎么重载赋值运算符

这篇文章给大家介绍C++中怎么重载赋值运算符,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。重载赋值运算符在类中重载赋值运算符的格式如下:void operator = (const Date&)后面我们回加以改进。目前
2023-06-17

C++11中noexcept运算符怎么使用

本篇内容主要讲解“C++11中noexcept运算符怎么使用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“C++11中noexcept运算符怎么使用”吧!异常符说明的实参前一篇文章没有提到,实际
2023-06-19

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录