Domanda

I recently started learn C# and I've write a simple exercise that require to convert an input from Fahrenheit to Celsius and back again. The code is simple and this is my effort (I suppose that the user give numeric input):

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");
        }

    }
}

That's run, but if I try with input 0 when I back to Fahrenheit I obtain something like -1.525879E-06. I've thinked about an approximation error, maybe a cancellation. I've modified a little the previus code, in particular I've changed this

float far2 = cel * 9 / 5 + 32;

to this

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

and now the output is 0!

I suppose that this behaviour is relative to the c# compiler, that rearranged code for better performance. I think the first code should implement all the operation in CPU register, while the second code save them in memory. Am I correct? Can you explain what's happen and how approximation work in this case?

Thanks in advance!

È stato utile?

Soluzione

There are a number of issues here:

  1. float, or System.Single to give it its full name is a single precision floating point number. This means its precision is very limited. Many operations will lead to slight rounding errors (such as your -1.525879E-06: which is just over one part in a million).

    The first thing to do is switch to double (or System.Double). This is the default floating point type unless you specify otherwise.

    The second thing to do is understand (and accept) the limited precision inherent in floating point representation. (Eg. one never compares two floating point numbers directly for equality, rather check that the absolute difference is less than some small value, "epsilon": Math.Abs(a - b) < epsilon where epsilon is dependent on the scale of a and b.)

    Finally read What Every Computer Scientist Should Know About Floating-Point Arithmetic.

  2. 9/5 (and similar) will be performed as integers (ie. rounded down) because none of the values involved are not integers. Try (9.0 / 5.0). (Strictly because this is part of a larger expression that type of the first term will set the type (your cel), however it adds cality code code to make it clear that these operations are floating point and not integral.)

Altri suggerimenti

This is just a rounding error, which you will often get when using floating point calculations. It is a result of the fact that some numbers cannot be accurately represented in binary (think of a recurring number such as 1/3 = 0.333333 etc).

To fix this:

  1. Use a more accurate numeric type such as double or decimal instead of float.
  2. Even after doing that, you will want to limit the number of decimal places that you display.

To limit the number of decimal places you can specify how many to display in a ToString():

string numberToDisplay = far.ToString("n2"); 
// The "n2" means "format with 2 decimal places"
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top