简体   繁体   中英

How to round the precision digits for double?

I'm working with double in C#. Double has Precision ~15-17 digits.

In my program, the user enters: 0.011 - 0.001, and the result shows 0.0099999999999999985 --> that's ugly in the user's eye - they will question my math ability.

I'm okay with the result being 0.0099999999999999985 internal, but when display, I want to find a way to fix the precision digits. For example:

double a = 0.011;
double b = 0.001;
double result, display;

result = a - b;//result = 0.0099999999999999985

display = ConvertForDisplay(result);//I want display = 0.01

I see that the build-in Android app "Calculator" on Samsung smartphone (eg Galaxy Tab SM-T295) also use double (because I enter 10^309 and it gives error) but when I enter the math 0.011 - 0.001, it properly shows me 0.01.

I thought about Math.Round (double value, int digits) , but the digits must be in range [0,15], while I also need to display the numbers like 1E-200.

I've search around the internet but I just saw questions about how to deal with the floating point accuracy problems (eg don't use == but use Math.Abs(ab) < double.Epsilon , Math.Round ,...). But I can't find how to round the precision digits (eg the value 0.0099999999999999985 has the precision digits 99999999999999985 , when the those digits contains more than 15 "9"s, then it should be round for display).

So what is the proper way to fix this problem (eg convert 0.0099999999999999985 into 0.01)?

Extra: when I enter 9999999999999999d , C# gives me 1E+16, I know this happens because C# sees that all the precision digits, which it can hold, is 9 so it rounds up to 10, but is there any way I can make it keeps all the 9s - I see that the Android app "Calculator", which I mention above, also gives this behavior, so I count this as extra - not a must-fix, but I want to know if it's fixable just for fun.

Update:

I see the behavior: (1.021 - 1.01) = 0.010999999999999899

But (1.021 - 1.01) + 1 = 1.011

So when I add 1, double triggers its "internal round" feature (I guess) and it rounds to the number I want. Maybe this could lead to my solution?

Here's another interesting discover: cast the value to float can also give the number I want, eg: (float)(1.021 - 1.01) = 0.011

I re-think the issue, how about I use decimal as a tool to fix this problem. The idea is to use decimal to solve the math (if the range is suitable for decimal), then cast the decimal back to double. Here is the code:

const int LEFT = 0;
const int RIGHT = 1;

private static bool IsConvertableToDecimal(double value)
{
    const double MAX = (double)decimal.MaxValue;
    const double MIN = (double)decimal.MinValue;
    const double EXP_MIN = -1E-28;
    const double EXP_MAX = 1E-28;

    return
        (value >= EXP_MAX && value < MAX) ||
        value == 0 ||
        (value > MIN && value <= EXP_MIN);
}

private static double BasicFunc(double[] arr,
    Func<double, double, double> funcDouble,
    Func<decimal, decimal, decimal> funcDecima)
{
    var result = funcDouble(arr[LEFT], arr[RIGHT]);

    //Check if can convert to decimal for better accurancy
    if (IsConvertableToDecimal(result) &&
        IsConvertableToDecimal(arr[LEFT]) &&
        IsConvertableToDecimal(arr[RIGHT]))
        result = (double)(funcDecima((decimal)arr[LEFT], (decimal)arr[RIGHT]));

    return result;
}

public static double ADD(double[] arr)
    => BasicFunc(arr, (x, y) => x + y, decimal.Add);
public static double SUBTRACT(double[] arr)
    => BasicFunc(arr, (x, y) => x - y, decimal.Subtract);
public static double MULT(double[] arr)
    => BasicFunc(arr, (x, y) => x * y, decimal.Multiply);
public static double DIV(double[] arr)
    => BasicFunc(arr, (x, y) => x / y, decimal.Divide);
    
//Usage ================
void Test()
{
var arr = new double[2];
arr[0] = 0.011;
arr[1] = 0.001;
var result = SUBTRACT(arr);
Print(result);//Result = 0.01 ==> Success!!!
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM