我最近开始学习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寄存器中的所有操作,而第二个代码将它们保存在内存中。 我对么? 你能解释一下这种情况会发生什么以及在这种情况下近似值如何工作?
提前致谢!
答案 0 :(得分:3)
这里有很多问题:
float
或System.Single
为其全名提供单精度浮点数。这意味着它的精度非常有限。许多操作会导致轻微的舍入错误(例如你的-1.525879E-06:这只是百万分之一)。
要做的第一件事是切换到double
(或System.Double
)。这是默认值
浮点类型,除非您另行指定。
要做的第二件事是理解(并接受)浮点固有的有限精度
表示。 (例如,一个人永远不会直接比较两个浮点数是否相等
检查绝对差值是否小于某个小值“epsilon”:
Math.Abs(a - b) < epsilon
其中epsilon取决于a
和b
的比例。)
最后阅读 What Every Computer Scientist Should Know About Floating-Point Arithmetic 。
9/5
(和类似的)将作为整数执行(即向下舍入),因为没有值
涉及的不是整数。试试(9.0 / 5.0
)。 (严格来说,因为这是第一个术语的类型将设置类型(您的cel
)的更大表达式的一部分,但是它添加了实用代码代码以清楚地表明这些操作是浮点而不是整数。)
答案 1 :(得分:2)
这只是一个舍入错误,在使用浮点计算时通常会得到这个错误。这是因为有些数字不能用二进制来精确表示(想想一个重复的数字,如1/3 = 0.333333等)。
解决此问题:
double
或decimal
,而非浮动。要限制小数位数,您可以指定在ToString()
中显示的小数位数:
string numberToDisplay = far.ToString("n2");
// The "n2" means "format with 2 decimal places"