-2

This is not a duplicate question. There is an answer posted in the question. Hope it can help.

There are two doubles with the same value with decimals.

(Sorry, this is not a good case. because it will return false sometimes, but I can't find the case. If you try this case, it may not have any problem. So don't waste time to test it.)

double a = 0.70448;
double b = 0.70441;

I want to compare them with only 4 decimals.

I have this helper function to round them down to 4 decimals first.

public static double RoundDown(this double value, int decimals)
{
    var multiplier = Math.Pow(10, decimals);
    return Math.Floor(value * multiplier) / multiplier;
}

And then I want to check if a is larger than b like this: RoundDown(a, 4) > RoundDown(b, 4)

Sometimes, for some cases, it will return true even they look equal. I understand very well this is floating issue, so I would like to know if there any elegant solution to compare them.

Updates:

I have tried to multiply it and compare them in integer. However, for this solution, I need to handle double infinity and NAN.

    private static CompareResult Compare(double a, double b, double decimals = 0)
    {
        var multiplier = Math.Pow(10, decimals);
        var aInt = Convert.ToInt32(a * multiplier);
        var bInt = Convert.ToInt32(b * multiplier);
        return aInt > bInt ? CompareResult.Greater : aInt < bInt ? CompareResult.Less : CompareResult.Equal;
    }

    private enum CompareResult
    {
        Greater,
        Less,
        Equal
    }

System.OverflowException is thrown if one of the double is larger than int max or infinity. Also, this is not an elegant way to compare double.

Importants:

I am not going to round down with x significant figures. I have already provide this solution in my question, my question is: Even round down to x significant figures, it will return true when comparing them.

Again

I am not finding a way to round down or truncate the doubles to x significant digits. I have no problem on this part.

Answer

Thanks for @m88 answer. But it still cannot solve my problem. I finally solve this issue using sigma. (Reference: http://forums.codeguru.com/showthread.php?506300-float-double-value-comparison-significant-figures.)

Thanks to some people misunderstand the problem and vote it as a duplicated question. I can't post my answer for others facing the same problem. So I post the answer in my question. I hope it can help others.

public static int CompareTo(this double value1, double value2, int decimals)
{
    var diff = value1 - value2;
    var sigma = Math.Pow(10, -decimals - 1);
    return Math.Abs(diff) < sigma ? 0 : diff > 0 ? 1 : -1;
}
shtse8
  • 1,092
  • 12
  • 20
  • @Equalsk Please read my question carefully. I have already told you the way to round a double to x significant figures. what I want is to compare them correctly. – shtse8 Jun 25 '18 at 14:31
  • Please read the linked duplicate carefully instead of only the title. It demonstrates _truncation_ which is what you're after over _rounding_. – Equalsk Jun 25 '18 at 14:41
  • @Equalsk I have already read it. What I want is **comparing** correctly, but not **truncating** or **rounding**. Check this: http://forums.codeguru.com/showthread.php?506300-float-double-value-comparison-significant-figures – shtse8 Jun 25 '18 at 14:44
  • @Equalsk Or you want to say the problem is on the round down method? or Math.Floor is making the comparison failed? – shtse8 Jun 25 '18 at 14:51
  • @Servy Anyway, I have read that possible duplicate question again and again. First of all, it has no accepted answer. Second, the most voted answer is already my current solution to round down the values. Please ready my question carefully instead of just reading the first paragraph. I want to compare them but not rounding/truncating them. – shtse8 Jun 25 '18 at 14:59
  • Why are you against truncation? It answers your question with the examples you gave. Unless you're aware of a very good reason to not truncate and compare (some example inputs would be best) then the duplicate is legitimate. – Equalsk Jun 25 '18 at 15:02
  • @Equalsk I haven't said I am against truncation and actually I am using it (same as your duplicate question's answer). As my question is already said. I have truncated/round down the values. But the problem is "After truncation/rounding down" when comparing them using greater than. – shtse8 Jun 25 '18 at 15:08
  • No, you're using rounding, that is not the same as truncation. Can you prove with some example inputs that truncation doesn't work? – Equalsk Jun 25 '18 at 15:10
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/173757/discussion-between-shtse8-and-equalsk). – shtse8 Jun 25 '18 at 15:13
  • @Equalsk I have found a solution by comparing the doubles using sigma. But I cannot post the answer. – shtse8 Jun 25 '18 at 15:48
  • Unfortunately I don't have the power to re-open the question for you. – Equalsk Jun 25 '18 at 15:50
  • @Equalsk Nevermind, I posted in my question. Hope it can help others. – shtse8 Jun 25 '18 at 15:55

2 Answers2

1

If you use the Math.Round method to round a and b to 4 decimals, a (0.7045) will always be greater than b (0.7044):

const double a = 0.70448;
const double b = 0.70441;
if (Math.Round(a, 4) > Math.Round(b, 4))
...

If you want to truncate the values, you need to be aware of the fact that not all fractions can be accurately represented in a double. If you want "exact" truncating, you might consider converting the double value to a string, truncate the string and then convert the truncated string value back to double. Something like this:

private static double Truncate(double d, int decimals)
{
    string s = d.ToString(System.Globalization.CultureInfo.InvariantCulture);
    int index = s.IndexOf(System.Globalization.CultureInfo.InvariantCulture.NumberFormat.NumberDecimalSeparator);
    if (index > -1)
        return Convert.ToDouble($"{s.Substring(0, index + 1)}{s.Substring(index + 1, decimals)}", System.Globalization.CultureInfo.InvariantCulture);

    return d;
}

Usage:

const double a = 0.70448;
const double b = 0.70441;

if (Truncate(a, 4) >= Truncate(b, 4))
....

Obviously, if you don't want any "floating issues" as you said in the chat, you cannot work with floating point data types.

mm8
  • 163,881
  • 10
  • 57
  • 88
  • I need to check `greater than` without `equal` case with rounding down to 4 decimals. – shtse8 Jun 25 '18 at 14:10
  • @shtse8 what's the difference? `a` is still greater than `b`, just remove the `=` from the check. – trashr0x Jun 25 '18 at 14:11
  • after rounding down to 4 decimals, they should be equal. the conditional should return false. But for some cases (with floating issue, it's hard to find the cases), it will return true even they look equal. – shtse8 Jun 25 '18 at 14:12
  • @shtse8: As mentioned, `a` will always be greater than `b` given these values. – mm8 Jun 25 '18 at 14:12
  • "after rounding down to 4 decimals, they should be equal". No. Why would they be equal? – mm8 Jun 25 '18 at 14:13
  • 0.7044 == 0.7044, I need rounding down, not just rounding. – shtse8 Jun 25 '18 at 14:14
  • This is not rounding. It's truncating: https://stackoverflow.com/questions/3143657/truncate-two-decimal-places-without-rounding – mm8 Jun 25 '18 at 14:14
  • https://www.journalofaccountancy.com/issues/2002/mar/thedifferencebetweenroundingandtruncating.html – shtse8 Jun 25 '18 at 14:15
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/173756/discussion-between-shtse8-and-mm8). – shtse8 Jun 25 '18 at 14:16
  • I am trying using this truncation method instead my original one. Actually, I think the main problem is due to comparing floating point numbers instead of truncation/rounding down method problem. Anyway, I will try for awhile, and mark it as an answer if it is working fine. – shtse8 Jun 25 '18 at 15:27
  • System.ArgumentOutOfRangeException is thrown when the double is shorter than the decimals. It works failed when the values are negative. – shtse8 Jun 25 '18 at 15:31
0

You want to truncate, not round:

double a = Math.Truncate(100 * 0.70448) / 100;
double b = Math.Truncate(100 * 0.70441) / 100;

if (a > b)
{
    // ...
}

Note that fractions cannot be accurately represented in a double, as per @mm8's comment.

trashr0x
  • 6,457
  • 2
  • 29
  • 39
  • I am using similar solution, but I found it will return true for some special cases. (I am failed to find the case as it's hard to produce the same floating values.) – shtse8 Jun 25 '18 at 14:20
  • Fractions cannot be accurately represented in a double. If you want "exact" truncating, you might consider converting the double values to strings, truncate the string and then convert the truncated value back to double. – mm8 Jun 25 '18 at 14:22
  • @mm8 agreed, incorporated the remark in the answer. – trashr0x Jun 25 '18 at 14:26
  • Yes, it is my direction too. Referring to my question, I have posted the another solution to compare them in integer and also explained why the solution is not good enough. I would like to know if there are better way to handle the case - comparing two x significant doubles correctly. (not 15). – shtse8 Jun 25 '18 at 14:30
  • http://forums.codeguru.com/showthread.php?506300-float-double-value-comparison-significant-figures – shtse8 Jun 25 '18 at 14:43
  • @shtse8: See my updated answer. – mm8 Jun 25 '18 at 15:02
  • @mm8 Thanks for your updated answer. Is it any difference using string to truncate instead of multiply and division? – shtse8 Jun 25 '18 at 15:12
  • 1
    Yes, a string is just a string. You get no loss of precision when you truncate the value. You just "cut" the string off a certain index. – mm8 Jun 25 '18 at 15:14
  • I understand. For my understand, using multi/div should not lose precision. for double, it can store up to 15 digits. 0.70448 =(multi 10000)=> 7044.8 =(floor)=> 7044 =(div 10000)=> 0.7044. So all precisions should be preserved. – shtse8 Jun 25 '18 at 15:21
  • @trashr0x truncate is not working correct when it is negative. – shtse8 Jun 25 '18 at 15:50
  • That shouldn't matter if you truncate their `string` equivalents, like what mm8 did. – trashr0x Jun 25 '18 at 15:53