How to comparing two x significant doubles correctly [duplicate] - c#

This question already has answers here:
Round a double to x significant figures
(17 answers)
Closed 4 years ago.
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;
}

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.

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.

Related

converting int to decimal choosing where to put decimal place

I have an interesting problem, I need to convert an int to a decimal.
So for example given:
int number = 2423;
decimal convertedNumber = Int2Dec(number,2);
// decimal should equal 24.23
decimal convertedNumber2 = Int2Dec(number,3);
// decimal should equal 2.423
I have played around, and this function works, I just hate that I have to create a string and convert it to a decimal, it doesn't seem very efficient:
decimal IntToDecConverter(int number, int precision)
{
decimal percisionNumber = Convert.ToDecimal("1".PadRight(precision+1,'0'));
return Convert.ToDecimal(number / percisionNumber);
}
Since you are trying to make the number smaller couldn't you just divide by 10 (1 decimal place), 100 (2 decimal places), 1000 (3 decimal places), etc.
Notice the pattern yet? As we increase the digits to the right of the decimal place we also increase the initial value being divided (10 for 1 digit after the decimal place, 100 for 2 digits after the decimal place, etc.) by ten times that.
So the pattern signifies we are dealing with a power of 10 (Math.Pow(10, x)).
Given an input (number of decimal places) make the conversion based on that.
Example:
int x = 1956;
int powBy=3;
decimal d = x/(decimal)Math.Pow(10.00, powBy);
//from 1956 to 1.956 based on powBy
With that being said, wrap it into a function:
decimal IntToDec(int x, int powBy)
{
return x/(decimal)Math.Pow(10.00, powBy);
}
Call it like so:
decimal d = IntToDec(1956, 3);
Going the opposite direction
You could also do the opposite if someone stated they wanted to take a decimal like 19.56 and convert it to an int. You'd still use the Pow mechanism but instead of dividing you would multiply.
double d=19.56;
int powBy=2;
double n = d*Math.Pow(10, powBy);
You can try create decimal explictly with the constructor which has been specially designed for this:
public static decimal IntToDecConverter(int number, int precision) {
return new decimal(Math.Abs(number), 0, 0, number < 0, (byte)precision);
}
E.g.
Console.WriteLine(IntToDecConverter(2423, 2));
Console.WriteLine(IntToDecConverter(1956, 3));
Outcome:
24.23
1.956
Moving the decimal point like that is just a function of multiplying/dividing by a power of 10.
So this function would work:
decimal IntToDecConverter(int number, int precision)
{
// -1 flips the number so its a fraction; same as dividing below
decimal factor = (decimal)Math.Pow(10, -1*precision)
return number * factor;
}
number/percisionNumber will give you an integer which you then convert to decimal.
Try...
return Convert.ToDecimal(number) / percisionNumber;
Convert your method like as below
public static decimal IntToDecConverter(int number, int precision)
{
return = number / ((decimal)(Math.Pow(10, precision)));
}
Check the live fiddle here.

Customizing Math.Round() result in c# [duplicate]

This question already has answers here:
How to round up or down in C#?
(9 answers)
C# decimal take ceiling 2
(5 answers)
Closed 8 years ago.
Math.Round() will always return results according to mathematics rules.
For Example 0.124 will be rounded off to 0.12 if done for 2 places of decimal.
Can I make it to always give me next higher value, for example 0.124 Rounded off to 2 places of decimal should give 0.13 and likewise?
Try this:
var input = 0.124;
var decimals = 2;
var r = (input == Math.Round(input, decimals)) ?
Math.Round(input, decimals) :
Math.Round(input + Math.Pow(10, -decimals), decimals);
You could create an extension method:
public static double RoundUp(this double input, int decimals)
{
return (input == Math.Round(input, decimals)) ?
Math.Round(input, decimals) :
Math.Round(input + Math.Pow(10, -decimals), decimals);
}
and use it like:
double input = 0.1224;
var decimals = 3;
var r = input.RoundUp(decimals);
You could use Math.Ceiling as mentioned by Baszz. You need to multiply and divide by some factor to mimic the rounding behaviour:
var decimals = 2;
var fac = Math.Pow(10, decimals);
var result = ((int)Math.Ceiling(0.124 * fac)) / fac;
Console.WriteLine(result);
I think you should take a look at the Math.Ceiling() method:
http://msdn.microsoft.com/en-us/library/zx4t0t48(v=vs.110).aspx
Look at this post on SO where it is explained how to use it:
How to round a decimal up?
for this, you try math.ceiling() instead of using math.round()..!
Math.Round() --> Rounding values
Math.Ceiling() --> Next higher value
Math.Floor() --> Next lower value
Here you go, example for Math.Ceiling() concept
using System;
class Program
{
static void Main()
{
// Get ceiling of double value.
double value1 = 123.456;
double ceiling1 = Math.Ceiling(value1);
// Get ceiling of decimal value.
decimal value2 = 456.789M;
decimal ceiling2 = Math.Ceiling(value2);
// Get ceiling of negative value.
double value3 = -100.5;
double ceiling3 = Math.Ceiling(value3);
// Write values.
Console.WriteLine(value1);
Console.WriteLine(ceiling1);
Console.WriteLine(value2);
Console.WriteLine(ceiling2);
Console.WriteLine(value3);
Console.WriteLine(ceiling3);
}
}
Output
******
123.456
124
456.789
457
-100.5
-100

C# - Truncating after a position

I have a double typed variable. This variable stores information that is part of a more complex formula. Importantly, this variable can only include information up to the tenths location, or one decimal position (i.e. 10.1, 100.2, etc). However, when determining this value, it must be calculated such that anything past the tenths location is truncated, not rounded. For instance:
if the value equals 10.44, The variable value should be 10.4.
if the value equals 10.45, The variable value should also be set to 10.4
How do I truncate values in C# with respect to a decimal place?
Using an extension method:
public static double RoundDown(this double value, int digits)
{
int factor = Math.Pow(10,digits);
return Math.Truncate(value * factor) / factor;
}
Then you simply use it like this:
double rounded = number.RoundDown(2);
You have to do that by your own:
public static decimal Truncate(decimal value, int decimals)
{
if ((decimals < 0) || (decimals > 28))
{
throw new ArgumentOutOfRangeException("decimals", "The number of fractional decimals must be between 0 and 28.");
}
decimal integral = Math.Truncate(value);
decimal fractional = value - integral;
decimal shift = (decimal)Math.Pow(10, decimals);
fractional = Math.Truncate(shift * fractional);
fractional = fractional / shift;
return (integral + fractional);
}
System.Math.Truncate (d * 10) / 10
Generally, if you're working with numbers where the precise decimal representation is important, you should use decimal - not double.
With decimal, you can do something like...
decimal d = ...;
d = decimal.Truncate(d*10)/10;
If you use a double value, your truncated number will not generally be precisely representable - you may end up with excess digits or minor rounding errors. For example Math.Truncate((4.1-4.0)*10) is not 1, but 0.
While I would probably use Phillippe's answer, if you wanted to avoid scaling the number up (unlikely to be a problem for 1dp), you could:
public static double RoundDown(this double x, int numPlaces)
{
double output = Math.Round(x, numPlaces, MidpointRounding.AwayFromZero);
return (output > x ? output - Math.Pow(10, -numPlaces) : output);
}

What is the fastest way to round the digit after a decimal (double)?

Ex, I have number 345.38, 2323.805555, 21.3333. I want to get the number after the decimal and round it up.
345.38 --> 4
2323.805555 --> 8
21.3333 --> 3
multiply by 10
ceiling (always rounds up, use 'round' to round down if lower than 0.5)
find the result of modding by 10
Like:
float myFloat = 123.38f;
float myBiggerFloat = Math.Ceiling(myFloat * 10.0f);
int theAnswer = ((int)myBiggerFloat % 10);
Or just ask for help for your homework on SO, either way seems to work.
This avoids potential overflow issues:
decimal value;
string[] sep = new[] { NumberFormatInfo.CurrentInfo.NumberDecimalSeparator };
String.Format("{0:0.0}", Math.Round(value, 1)).Split(sep, StringSplitOptions.None)[1][0];
This avoids string conversions and overflow issues:
decimal value;
decimal absValue = Math.Abs(value);
decimal fraction = absValue - Math.Floor(absValue);
int lastDigit = Convert.ToInt32(10 * Math.Round(fraction, 1));
If you just want the digit immediately following the decimal...couldn't you do something like this?
float value;
int digit = (int)(((value % 1) * 10) + 0.5)
Get the fractional part, multiply by ten, and round:
double n = 345.38;
int digit = (int)Math.Round((n - Math.Floor(n)) * 10);
This avoids any overflow issues, as the result is already down to one digit when cast to an int.
I have verified that this gives the desired result for your examples.
This whole overflow discussion is a little academic, and most likely not the intention of your homework. But should you want to solve that problem:
decimal value = -0.25m;
decimal fractionalPart = Math.Abs(value - Math.Truncate(value));
int digit = (int)Math.Round(10 * fractionalPart, MidpointRounding.AwayFromZero);
Edit: after reading your question again, I noticed that numbers shouldn't always be rounded up like my original answer. However, most people using Math.Round here use the default banker's rounding (to an even number). It depends if you intended -0.25 to result in 2 or 3. The way I'm reading your description, it should be 3 like in this example.
float myNum = 10.11;
char c = myNum[myNum.ToString().IndexOf(".") + 1];

Truncate number of digit of double value in C#

How can i truncate the leading digit of double value in C#,I have tried Math.Round(doublevalue,2) but not giving the require result. and i didn't find any other method in Math class.
For example i have value 12.123456789 and i only need 12.12.
EDIT: It's been pointed out that these approaches round the value instead of truncating. It's hard to genuinely truncate a double value because it's not really in the right base... but truncating a decimal value is more feasible.
You should use an appropriate format string, either custom or standard, e.g.
string x = d.ToString("0.00");
or
string x = d.ToString("F2");
It's worth being aware that a double value itself doesn't "know" how many decimal places it has. It's only when you convert it to a string that it really makes sense to do so. Using Math.Round will get the closest double value to x.xx00000 (if you see what I mean) but it almost certainly won't be the exact value x.xx00000 due to the way binary floating point types work.
If you need this for anything other than string formatting, you should consider using decimal instead. What does the value actually represent?
I have articles on binary floating point and decimal floating point in .NET which you may find useful.
What have you tried? It works as expected for me:
double original = 12.123456789;
double truncated = Math.Truncate(original * 100) / 100;
Console.WriteLine(truncated); // displays 12.12
double original = 12.123456789;
double truncated = Truncate(original, 2);
Console.WriteLine(truncated.ToString());
// or
// Console.WriteLine(truncated.ToString("0.00"));
// or
// Console.WriteLine(Truncate(original, 2).ToString("0.00"));
public static double Truncate(double value, int precision)
{
return Math.Truncate(value * Math.Pow(10, precision)) / Math.Pow(10, precision);
}
How about:
double num = 12.12890;
double truncatedNum = ((int)(num * 100))/100.00;
This could work (although not tested):
public double RoundDown(this double value, int digits)
{
int factor = Math.Pow(10,digits);
return Math.Truncate(value * factor) / factor;
}
Then you simply use it like this:
double rounded = number.RoundDown(2);
This code....
double x = 12.123456789;
Console.WriteLine(x);
x = Math.Round(x, 2);
Console.WriteLine(x);
Returns this....
12.123456789
12.12
What is your desired result that is different?
If you want to keep the value as a double, and just strip of any digits after the second decimal place and not actually round the number then you can simply subtract 0.005 from your number so that round will then work. For example.
double x = 98.7654321;
Console.WriteLine(x);
double y = Math.Round(x - 0.005, 2);
Console.WriteLine(y);
Produces this...
98.7654321
98.76
There are a lot of answers using Math.Truncate(double).
However, the approach using Math.Truncate(double) can lead to incorrect results.
For instance, it will return 5.01 truncating 5.02, because multiplying of double values doesn't work precisely and 5.02*100=501.99999999999994
If you really need this precision, consider, converting to Decimal before truncating.
public static double Truncate(double value, int precision)
{
decimal power = (decimal)Math.Pow(10, precision);
return (double)(Math.Truncate((decimal)value * power) / power);
}
Still, this approach is ~10 times slower.
I'm sure there's something more .netty out there but why not just:-
double truncVal = Math.Truncate(val * 100) / 100;
double remainder = val-truncVal;
If you are looking to have two points after the decimal without rounding the number, the following should work
string doubleString = doublevalue.ToString("0.0000"); //To ensure we have a sufficiently lengthed string to avoid index issues
Console.Writeline(doubleString
.Substring(0, (doubleString.IndexOf(".") +1) +2));
The second parameter of substring is the count, and IndexOf returns to zero-based index, so we have to add one to that before we add the 2 decimal values.
This answer is assuming that the value should NOT be rounded
For vb.net use this extension:
Imports System.Runtime.CompilerServices
Module DoubleExtensions
<Extension()>
Public Function Truncate(dValue As Double, digits As Integer)
Dim factor As Integer
factor = Math.Pow(10, digits)
Return Math.Truncate(dValue * factor) / factor
End Function
End Module
I use a little formatting class that I put together which can add gaps and all sorts.
Here is one of the methods that takes in a decimal and return different amounts of decimal places based on the decimal display setting in the app
public decimal DisplayDecimalFormatting(decimal input, bool valueIsWeightElseMoney)
{
string inputString = input.ToString();
if (valueIsWeightElseMoney)
{
int appDisplayDecimalCount = Program.SettingsGlobal.DisplayDecimalPlacesCount;
if (appDisplayDecimalCount == 3)//0.000
{
inputString = String.Format("{0:#,##0.##0}", input, displayCulture);
}
else if (appDisplayDecimalCount == 2)//0.00
{
inputString = String.Format("{0:#,##0.#0}", input, displayCulture);
}
else if (appDisplayDecimalCount == 1)//0.0
{
inputString = String.Format("{0:#,##0.0}", input, displayCulture);
}
else//appDisplayDecimalCount 0 //0
{
inputString = String.Format("{0:#,##0}", input, displayCulture);
}
}
else
{
inputString = String.Format("{0:#,##0.#0}", input, displayCulture);
}
//Check if worked and return if worked, else return 0
bool itWorked = false;
decimal returnDec = 0.00m;
itWorked = decimal.TryParse(inputString, out returnDec);
if (itWorked)
{
return returnDec;
}
else
{
return 0.00m;
}
}
object number = 12.123345534;
string.Format({"0:00"},number.ToString());

Categories

Resources