.NET 中的二进制浮点类型

  大多数人会对他们在.NET中的算术的"出错"首先感到惊讶。使用一些称为”浮点”算术来表示非整型数字不是.NET 相比其他大多数语言/平台特殊的地方。在.NET 内部是没问题的,但是你需要知道一些底层正在发生什么,否则你将会对一些结果感到惊讶。

  我在这个事情上不是一个专家这不重要。虽然写了这篇文章,我也发现了另外一篇 - 这次是一个真正的专家写的,杰弗里 萨克斯(Jeffrey Sax)。我强烈建议你也同时读他的浮点文章

  什么是浮点数?

  计算机总是需要一些表示数据的方式,最终这些表示数据的方式总是归结为二进制(0,1组合)。整数很容易表示(对负数有合适的转换,有确定好的范围可以知道表示从多大开始)但是非整数有一些复杂。不管你想出什么方法,总是有一个问题。例如,使用我们自己的十进制方式写数字: 仍然(在十进制内部)不能表达三分之一,你只是在一个3循环中结束。无论你使用多少进制,一些数字都会产生同样的问题 - 特别的,“无理数”的数字(那些不能用以分数表示的数字)如常量PI(音: pai)和e(指数e)总是有一些问题。

  你可以将所有有理数用精确的两个整型数表示,第一个数被第二个数除的结果 - 但是即便是一个非常”简单”的操作整数都可以增长的非常大且非常快,平方根操作也会趋向产生无理数。有很多其他的因素会导致导致,但是最常用的解决问题的方式就是使用一种格式或其他格式的浮点类型。思想就是基础有可以用来扩展表达的一些数字(尾数),另外(指数)用来表示规模是多大,以“小数点要去哪里”的形式表示。例如,34.5可以用”十进制浮点类型”3.45加上一个指数1来表示,同样的3450也可以有同样的尾数和一个指数3(34.5是3.45x101,3450是 3.45x103)来表示。现在,为了简单起见例子使用十进制表示,但是大多数浮点类型是二进制表示的。例如,二进制尾数1.1加上尾数-1将意味着十进制0.75(二进制1.1==十进制1.5,在二进制中指数-1意味着”被2除”,十进制同样的指数-1表示”被10除”,二进制1.1==20.2-1==1.5(译者注)).

  理解在同样的方式你不能通过一个十进制扩充(无限)来精确表达三分之一是很重要的,有很多数字在十进制形式看起来很简单,但是在二进制表示中却有长的或者无限的扩展。这意味着(举例)一个二进制浮点变量不能有精确的十进制值0.1。相反,假设你又一些如下代码:

  double x = 0.1d;

  变量x实际上将存储最接近那个值的double型值。一旦你脑子里可以转过弯儿,那么为什么一起计算结果看起来是”错误”的将会变得很明显。如果你被要求计算1/3 + 1/3,这两个数相加的结果是0.666,而不是0.667(更接近两个1/3 的和)。一个二进制浮点类型的表达式是3.65d+0.05d != 3.7d(尽管在一些情况下它显示成3.7)

  .NET 中的浮点类型是什么样子的?

  C#标准仅列出double和float作为可用的浮点类型(这些是C#中System.Double和System.Single的速记表示),但是decimal类型(速记表示为System.Decimal)实际上也是一个浮点类型 - 它仅是十进制浮点类型,但是指数的范围很有趣。decimal类型在另外一篇文章中描述,所以这篇文章不会做任何深入探讨 - 我们关注double和float.这两个都是二进制浮点类型,参照IEEE 754(一个多种浮点类型的标准定义)。float是一个32位类型(1个符号位, 23位的尾数和8位指数), double是一个64位类型(1个符号位, 52位尾数和11位指数)。

  结果不是我期望的是不好的结果吗?

  好吧,那取决于情况。如果你在写财务软件,你可能要非常严格的定义处理错误的方式,数量也是直觉上用10进制表示 - 在这种情况decimal类型更加与float或者double类型相似。如果,然而,如果你在写一个科学应用程序,使用十进制浮点表示法可能会有一点弱,你也可能想要开始处理一些低精度的数目(一美元就是一美元,但是如果你在测量一个单位是米的长度,你可能开始有一些不精确。)

  比较浮点数字

  所有这些可以得出一个推论,你应该非常,非常少的去直接比较浮点数间是否相等。通常比较大于或者小于会好些,但是当你对相等感兴趣时你应该总是考虑是否你实际上想要的接近相等:一个数字总是与另外一个相同。做这个的一个简单的方式是用一个数减去另外一个数,使用Math.Abs来找到绝对值的不同,然后检查是否这个误差是否低到可以忍受的级别。

也有一些情况是病理的,这些是由于JIT优化导致。查看下面的代码:

using System;class Test{    static float f;    static void Main(string[] args)    {        f = Sum (0.1f, 0.2f);        float g = Sum (0.1f, 0.2f);        Console.WriteLine (f==g);
       //g = g + 1;
    }    static float Sum (float f1, float f2)    {        return f1+f2;    }}

NET技术.NET 中的二进制浮点类型,转载需保留来源!

郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。