HiveBrain v1.2.0
Get Started
← Back to all entries
debugcsharpMinor

Fixing double arithmetic errors

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
errorsdoublefixingarithmetic

Problem

I am trying to account for the errors in double arithmetic in a C# program I am writing, and came up with this first solution that seems to work well for most cases:

double errorMargins = returnDouble * Double.Parse(Unit_Class_Library.Properties.Resources.AcceptedDeviationConstant);
    double roundedValue = 0;
    for (int i = 1; i <= returnDoubleAsString.Length; i++)
    {
        roundedValue = RoundToSignificantDigits(returnDouble, i);
        if (Math.Abs(roundedValue - returnDouble) < Double.Parse(Unit_Class_Library.Properties.Resources.AcceptedDeviationConstant))
        {
            returnDouble = roundedValue;
            i = returnDoubleAsString.Length;
        }

    }

    return returnDouble;
}

public static double RoundToSignificantDigits(double d, int digits)
{
    if (d == 0)
        return 0;

    bool isNegative = false;
    if (d < 0)
        isNegative = true;

    double scale = Math.Pow(10, Math.Floor(Math.Log10(Math.Abs(d))) + 1);
    double returnValue =  scale * Math.Round(d / scale, digits);

    if (isNegative)
        returnValue = -returnValue;

    return returnValue;
}


However, I found that when you convert a number to a string it rounds the number making the above function not work.

For example, when 720.72499999999991 is inputted (with a AcceptedDeviationConstant of 0.000000001), the double as string would be "720.725" which causes the above loop to exit prematurely and not do its job leaving the number as 720.72499999999991. So what I did was convert the double to a string and then back to a double (the code block below) and this seems to work how I wanted it too, returning 720.725 in the above example.

string returnDoubleAsString = "" + returnDouble;
returnDouble = double.Parse(returnDoubleAsString);


I was wondering if it is okay to just leave it as the second one but it seems "improper" to do it like this. If it is okay in practice, what kind of comment should I leave to make it clear to other programmers

Solution

Updated:

Seems like C# has its own format specifier "R" Round trip


The round-trip ("R") format specifier guarantees that a numeric value that is converted to a string will be parsed back into the same numeric value. This format is supported only for the Single, Double, and BigInteger types.

public static double RoundTrip(double d)
{
    return Double.Parse(d.ToString("R"));
}


Original:

The way to represent floating point numbers exactly on a stream in readable format is to print out there representation in hex format (rather than decimal):

Not sure how it is done in C# or Java but I bet if you dig the exact same method is used down deep in these languages.

In C++ and C the methods are the same.

The C++ version.

double x = 5.6789;

std::cout << x << "\n";   // Human readable dot format.
                          // Has loss of precision as number may not 
                          // be exactly representable in decimal.

// This the equivalent of using the "%a" string formatter.
// fprintf(stdout, "%a\n", x);
//
std::cout.flags(std::ios_base::fixed | std::ios_base::scientific);
std::cout << x << "\n";   // Hex Format human readable (well nearly human readable)
                          // Prints each part of the float as a hex value
                          // That can be represented exactly.
                          // on a stream of char


Updated after reading: Printing double without losing precision

// In C++11 there is an additional manipulator
// that achieves the same affect.
std::cout << std::hexfloat << x << "\n";


The results:

5.6789                    // Easy to read (not exact)

0x1.6b7318fc50481p+2      // Difficult to read (but exact).

0x1.6b7318fc50481p+2      // Difficult to read (but exact).

Code Snippets

public static double RoundTrip(double d)
{
    return Double.Parse(d.ToString("R"));
}
double x = 5.6789;

std::cout << x << "\n";   // Human readable dot format.
                          // Has loss of precision as number may not 
                          // be exactly representable in decimal.


// This the equivalent of using the "%a" string formatter.
// fprintf(stdout, "%a\n", x);
//
std::cout.flags(std::ios_base::fixed | std::ios_base::scientific);
std::cout << x << "\n";   // Hex Format human readable (well nearly human readable)
                          // Prints each part of the float as a hex value
                          // That can be represented exactly.
                          // on a stream of char
// In C++11 there is an additional manipulator
// that achieves the same affect.
std::cout << std::hexfloat << x << "\n";
5.6789                    // Easy to read (not exact)

0x1.6b7318fc50481p+2      // Difficult to read (but exact).

0x1.6b7318fc50481p+2      // Difficult to read (but exact).

Context

StackExchange Code Review Q#62651, answer score: 2

Revisions (0)

No revisions yet.