Reputation: 331062
I know .NET has one built-in but it's an external call. Anyone knows why?
But the actual question is how to implement a truncate from scratch where the user will be able to specify how many digits to keep? Is multiplying a number by say 100 and then dividing it by the same amount enough? Or is there a better implementation?
Something like:
Truncate(12.3456789, 3);
// returns 12.345
Upvotes: 2
Views: 904
Reputation: 210515
You'd probably want to look at IEEE floating-point integers.
You can then use unsafe
code to modify the numbers, like:
unsafe
{
double* pValue = &value;
var asLong = *(long*)pValue;
do whatever you want with asLong, e.g. bit-masking it, etc.;
}
As to the 'why': I have no idea, though the Shared Source CLI may provide clues. My guess would be that it might be because of performance optimizations.
Upvotes: 2
Reputation: 3875
Here is how I would do it. In C++, and I think in C# as well, you could get the integer part of a floating point number by casting it to an integer type.
double Truncate (double num, int dig)
{
if (dig > 15) dig = 15; // Don't overflow
long p = Math.Pow (10, dig);
// Save the integer part, so that we don't overflow
long integer_part = (long)num;
// Fractional part * 10^dig
double frac = (num - Convert.ToDouble(integer_part)) * p;
long frac_trunc = (long)frac;
// Final result
double result = Convert.ToDouble(integer_part) + (Convert.ToDouble(frac_trunc) / p);
return result;
}
Is multiplying a number by say 100 and then dividing it by the same amount enough?
That should work, but be careful because with large numbers, or high number of digits, you can easily overflow, and it will give you weird results.
Upvotes: 1
Reputation: 2551
It is not clear the reason you think that Truncate should keep the decmial value.
The default method within .NET is described by the following statement:
The integral part of d; that is, the number that remains after any fractional digits have been discarded.
It seems like what you want to use is either to format the output string of an double/decmial value and/or use the Math.Round(double, int) function instead.
You could just use:
double num = 2.22939393; num = Convert.ToDouble(num.ToString("#0.000"));
From one of the duplicate questions:
public static decimal TruncateToDecimalPlace(this decimal numberToTruncate, int decimalPlaces)
{
decimal power = (decimal)(Math.Pow(10.0, (double)decimalPlaces));
return Math.Truncate((power * numberToTruncate)) / power;
}
I understand this still uses the Truncate
method. I only provided this code since you wanted a Truncate
method that would keep the decmial value of a number and the default built-in Truncate
method does not.
You could always just use this:
Math.Round does NOT call the SplitFractionDouble
from what I can tell
private static unsafe double InternalRound(double value, int digits, MidpointRounding mode) {
if (Abs(value) < doubleRoundLimit) {
Double power10 = roundPower10Double[digits];
value *= power10;
if (mode == MidpointRounding.AwayFromZero) {
double fraction = SplitFractionDouble(&value);
if (Abs(fraction) >= 0.5d) {
value += Sign(fraction);
}
}
else {
// On X86 this can be inlined to just a few instructions
value = Round(value);
}
value /= power10;
}
return value;
}
public static double Round(double value, int digits)
{
if ((digits < 0) || (digits > maxRoundingDigits))
throw new ArgumentOutOfRangeException("digits", Environment.GetResourceString("ArgumentOutOfRange_RoundingDigits"));
return InternalRound(value, digits, MidpointRounding.ToEven);
}
public static double Round(double value, MidpointRounding mode) {
return Round(value, 0, mode);
}
public static double Round(double value, int digits, MidpointRounding mode) {
if ((digits < 0) || (digits > maxRoundingDigits))
throw new ArgumentOutOfRangeException("digits", Environment.GetResourceString("ArgumentOutOfRange_RoundingDigits"));
if (mode < MidpointRounding.ToEven || mode > MidpointRounding.AwayFromZero) {
throw new ArgumentException(Environment.GetResourceString("Argument_InvalidEnumValue", mode, "MidpointRounding"), "mode");
}
return InternalRound(value, digits, mode);
}
public static Decimal Round(Decimal d) {
return Decimal.Round(d,0);
}
public static Decimal Round(Decimal d, int decimals) {
return Decimal.Round(d,decimals);
}
public static Decimal Round(Decimal d, MidpointRounding mode) {
return Decimal.Round(d, 0, mode);
}
public static Decimal Round(Decimal d, int decimals, MidpointRounding mode) {
return Decimal.Round(d, decimals, mode);
}
public static Decimal Floor(Decimal d) { return Decimal.Floor(d); }
[MethodImplAttribute(MethodImplOptions.InternalCall)] public static extern double Floor(double d);
Upvotes: 0
Reputation: 8482
The classic way:
var x = 1.2345678;
var tr = 4;
var truncated = (int) (x * Math.Pow(10, tr)) / Math.Pow(10, tr);
would give 1.2345;
Upvotes: 2
Reputation: 12904
var result = Math.Round(12.3456789, 3);
Math.Round Method (Double, Int32)
Upvotes: 0