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);
}
Related
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.
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.
I know we can display decimals up to certain no of places (if that no of places is fixed). For example, we can display up to 2 places using String.Format:
String.Format("{0:0.00}", 123.4567);
But our requirement is, we have to fetch no of decimal places from database and display the decimal value up to that place. For example:
int n=no of decimal places
I want to write something like:
String.Format("{0:0.n}", 123.4567);
Any suggestions would be of great help.
Added Note: String.Format rounds off the digit. I am looking for something to omit the remaining digits.
Perhaps:
int n = 3;
string format = String.Format("{{0:0.{0}}}", new string('0', n));
Console.Write(String.Format(format, 123.4567)); // 123,457
as method:
public static string FormatNumber(double d, int decimalPlaces)
{
string format = String.Format("{{0:0.{0}}}", new string('0', decimalPlaces));
return String.Format(format, d);
}
or even simpler, using ToString + N format specifier:
public static string FormatNumber(double d, int decimalPlaces)
{
return d.ToString("N" + decimalPlaces);
}
if you don't want the default rounding behaviour but you just want to truncate the remaining decimal places:
public static string FormatNumberNoRounding(double d, int decimalPlaces)
{
double factor = Math.Pow(10, decimalPlaces);
double truncated = Math.Floor(d * factor) / factor;
return truncated.ToString();
}
If you prefer less string formatting, this is perhaps simpler:
decimal d = 123.4567
Console.Write("rounded: {0}", decimial.Round(d, 3));
Additionally, you can control the type of rounding used:
decimial.Round(d, 3, MidpointRounding.AwayFromZero)
Since not many people realise that .NET's default rounding method is ToEven, which rounds to the nearest even number. So values like 2.5 actually round to 2, not 3.
double a = 135.24; // a is set to 135.24000000000001 actually
double b = Math.Round(a, 0); // set to 135.0
double c = Math.Round(a, 1); // set to 135.19999999999999
double d = Math.Round(a, 2); // set to 135.24000000000001
double e = Math.Round(a, 3); // set to 135.24000000000001
double f = Math.Round(a, 4); // set to 135.24000000000001
double g = Math.Round(a, 5); // set to 135.24000000000001
double h = Math.Round(a, 10); // set to 135.24000000000001
double i = Math.Round(a, 14); // set to 135.24000000000001
double j = Math.Round(a, 2
, MidpointRounding.AwayFromZero ); // set to 135.24000000000001
double k = Math.Round(a, 2
, MidpointRounding.ToEven ); // set to 135.24000000000001
Sooooo, this means that 135.24 cannot be represented with a double, right?
Yes, 135.24 cannot be represented by double since double uses binary exponential notation.
That is: 135.24 can be represented exponentially in base of 2 as 1.0565625 * 128 = ( 1 + 1/32 + 1/64 + 1/128 + 1/1024 + ... ) * (2**7).
The representation cannot be done exactly, because 13524 does not divide by 5. Let's look:
135.24 = 13524/(10**2)
representation is finite <=> exist whole x and n satisfying 135.24 = x/(2**n)
135.24 = x/(2**n)
13524 / (10**2) = x / (2**n)
13524 * (2**n) = (10**2) * x
13524 * (2**n) = 2*2*5*5 * x
there is no "5" on the left side, so it cannot be done
(known as the Fundamental Theorem of Arithmetic)
In general, finite binary representation is exact only if there is sufficient number of "fives" in prime factorization of the decimal number.
Now the fun part:
double delta = 0.5;
while( 1 + delta > 1 )
delta /= 2;
Console.WriteLine( delta );
Precision of double is different near 1, different near 0, and different for some big numbers. Some binary representation examples on Wikipedia: Double precision floating point format
But the most important thing is that internal processor floating-point stack may have much better precision than 8 bytes (double). If number does not have to be transferred to RAM and stripped down to 8 bytes we can get a really nice precision.
Testing something like this on different processors (AMD, Intel), languages (C, C++, C#, Java) or compiler optimization levels can give results can be around 1e-16, 1e-20, or even 1e-320
Take a look at CIL / assembler / jasmin code to see exactly what is going on (eg: for C++ g++ -S test.cpp creates test.s file with assembler code in it)
That is generally a problem with floating point numbers. If you need an exact representation of numbers (e.g. for billing, ...) then you should use Decimal.
Try following piece of code and you will see that you do not have the output 0, 0.1, 0.2, ... 1.0.
for(double i = 0; i <= 1.0; i += 0.001)
{
Console.WriteLine(i);
}
Try using decimal instead. Floating points are not very precise (thus some numbers can't be represented) :)
Yes, it cannot. This is why there's another nonintegra datatype called decimal. It takes different amount of memory and has different min/max numerical ranges than double, and is NOT bit-convertible*) to double, but in turn it can held numbers precisely without any distortions.
*) That is, you cannot i.e. copy it as bytes and push to C++ code. However, you can still cast it to double and back. Just mind that the cast will NOT be precise, as double cannot hold some numbers that decimal can, and vice versa
You can see the definition.The Round function defined as-
public static double Round(double value, int digits, MidpointRounding mode)
{
if (digits < 0 || digits > 15)
throw new ArgumentOutOfRangeException("digits", Environment.GetResourceString("ArgumentOutOfRange_RoundingDigits"));
if (mode >= MidpointRounding.ToEven && mode <= MidpointRounding.AwayFromZero)
return Math.InternalRound(value, digits, mode);
throw new ArgumentException(Environment.GetResourceString("Argument_InvalidEnumValue", (object) mode, (object) "MidpointRounding"), "mode");
}
private static unsafe double InternalRound(double value, int digits, MidpointRounding mode)
{
if (Math.Abs(value) < Math.doubleRoundLimit)
{
double num1 = Math.roundPower10Double[digits];
value *= num1;
if (mode == MidpointRounding.AwayFromZero)
{
double num2 = Math.SplitFractionDouble(&value);
if (Math.Abs(num2) >= 0.5)
value += (double) Math.Sign(num2);
}
else
value = Math.Round(value);
value /= num1;
}
return value;
}
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());