2

我最近开始学习 C#,我写了一个简单的练习,需要将输入从华氏温度转换为摄氏温度,然后再转换回来。代码很简单,这是我的努力(我假设用户输入数字):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    class DegreeConversion
    {
        static void Main(string[] args)
        {
            Console.Write("Insert far -> ");
            float far = float.Parse(Console.ReadLine());
            float cel = (far - 32) / 9 * 5;
            Console.WriteLine(far   " degrees Fahrenheit is "   cel   " degrees Celsius");
            float far2 = cel * 9 / 5 + 32;
            Console.WriteLine(cel   " degrees Celsius is "   far2   " degrees Fahrenheit");
        }

    }
}

这是运行的,但是如果我在返回华氏温度时尝试输入0 ,我会得到类似 -1.525879E-06 的东西。我考虑过近似错误,也许是取消。我修改了一些以前的代码,特别是我改变了这个

float far2 = cel * 9 / 5 + 32;

对此

float far2 = cel * 9 / 5;
float newFar = far2 + 32;

现在输出为0!

我想这种行为与 c# 编译器有关,它重新排列了代码以获得更好的性能。我认为第一个代码应该在 CPU 寄存器中实现所有操作,而第二个代码将它们保存在内存中。我对么?你能解释一下在这种情况下发生了什么以及近似是如何工作的吗?

提前致谢!

4

2 回答 2

3

这里有很多问题:

  1. float,或者System.Single说它的全名是一个单精度浮点数。这意味着它的精度非常有限。许多操作会导致轻微的舍入误差(例如您的 -1.525879E-06:刚好超过百万分之一)。

    首先要做的是切换到double(或System.Double)。这是默认的浮点类型,除非您另外指定。

    第二件事是理解(并接受)浮点表示固有的有限精度。(例如,从不直接比较两个浮点数是否相等,而是检查绝对差是否小于某个小值“epsilon”: Math.Abs(a - b) < epsilon 其中 epsilon 取决于 和 的比例ab

    最后阅读每个计算机科学家应该知道的关于浮点运算的知识

  2. 9/5(和类似的)将作为整数执行(即向下舍入),因为所涉及的值都不是整数。试试 ( 9.0 / 5.0)。(严格来说,因为这是第一项的类型将设置类型(您的cel)的更大表达式的一部分,但是它添加了代码代码以清楚地表明这些操作是浮点数而不是整数。)

于 2013-09-22T09:28:52.093 回答
2

这只是一个舍入误差,在使用浮点计算时经常会遇到这种情况。这是因为某些数字无法用二进制准确表示(想想一个重​​复的数字,例如 1/3 = 0.333333 等)。

要解决这个问题:

  1. 使用更准确的数字类型,例如doubledecimal代替浮点数。
  2. 即使这样做了,您也需要限制显示的小数位数。

要限制小数位数,您可以指定在 a 中显示的位数ToString()

string numberToDisplay = far.ToString("n2"); 
// The "n2" means "format with 2 decimal places"
于 2013-09-22T09:26:43.523 回答