이것은 오래된 질문이지만 많은 anwsers가 잘 수행되지 않거나 큰 숫자에 대해 오버플로됩니다. 나는 D. Nesterov 대답이 가장 좋은 대답이라고 생각합니다. 강력하고 간단하며 빠릅니다. 2 센트 만 더하고 싶습니다. 나는 소수 를 가지고 놀았고 또한 소스 코드를 확인했다 . 로부터 public Decimal (int lo, int mid, int hi, bool isNegative, byte scale)
생성자 문서 .
Decimal 숫자의 이진 표현은 1 비트 부호, 96 비트 정수 및 정수를 나누고 소수 부분을 지정하는 데 사용되는 배율 인수로 구성됩니다. 배율 인수는 암시 적으로 0에서 28까지의 지수로 올린 숫자 10입니다.
이것을 알고, 나의 첫 번째 접근 방식은 decimal
내가 버리고 싶은 소수에 해당하는 스케일을 가진 다른 하나 를 만든 다음 잘라 내고 마지막으로 원하는 스케일로 소수를 만드는 것입니다.
private const int ScaleMask = 0x00FF0000;
public static Decimal Truncate(decimal target, byte decimalPlaces)
{
var bits = Decimal.GetBits(target);
var scale = (byte)((bits[3] & (ScaleMask)) >> 16);
if (scale <= decimalPlaces)
return target;
var temporalDecimal = new Decimal(bits[0], bits[1], bits[2], target < 0, (byte)(scale - decimalPlaces));
temporalDecimal = Math.Truncate(temporalDecimal);
bits = Decimal.GetBits(temporalDecimal);
return new Decimal(bits[0], bits[1], bits[2], target < 0, decimalPlaces);
}
이 방법은 D. Nesterov의 방법보다 빠르지 않고 더 복잡해서 조금 더 놀았습니다. 내 생각 엔 보조 장치를 만들고 decimal
비트를 두 번 검색 해야하는 것이 속도가 느려진다는 것입니다. 두 번째 시도에서 Decimal.GetBits (Decimal d) 메서드에서 반환 한 구성 요소를 직접 조작했습니다 . 아이디어는 필요한만큼 구성 요소를 10 배로 나누고 규모를 줄이는 것입니다. 코드는 Decimal.InternalRoundFromZero (ref Decimal d, int decimalCount) 메서드를 기반으로 합니다.
private const Int32 MaxInt32Scale = 9;
private const int ScaleMask = 0x00FF0000;
private const int SignMask = unchecked((int)0x80000000);
// Fast access for 10^n where n is 0-9
private static UInt32[] Powers10 = new UInt32[] {
1,
10,
100,
1000,
10000,
100000,
1000000,
10000000,
100000000,
1000000000
};
public static Decimal Truncate(decimal target, byte decimalPlaces)
{
var bits = Decimal.GetBits(target);
int lo = bits[0];
int mid = bits[1];
int hi = bits[2];
int flags = bits[3];
var scale = (byte)((flags & (ScaleMask)) >> 16);
int scaleDifference = scale - decimalPlaces;
if (scaleDifference <= 0)
return target;
// Divide the value by 10^scaleDifference
UInt32 lastDivisor;
do
{
Int32 diffChunk = (scaleDifference > MaxInt32Scale) ? MaxInt32Scale : scaleDifference;
lastDivisor = Powers10[diffChunk];
InternalDivRemUInt32(ref lo, ref mid, ref hi, lastDivisor);
scaleDifference -= diffChunk;
} while (scaleDifference > 0);
return new Decimal(lo, mid, hi, (flags & SignMask)!=0, decimalPlaces);
}
private static UInt32 InternalDivRemUInt32(ref int lo, ref int mid, ref int hi, UInt32 divisor)
{
UInt32 remainder = 0;
UInt64 n;
if (hi != 0)
{
n = ((UInt32)hi);
hi = (Int32)((UInt32)(n / divisor));
remainder = (UInt32)(n % divisor);
}
if (mid != 0 || remainder != 0)
{
n = ((UInt64)remainder << 32) | (UInt32)mid;
mid = (Int32)((UInt32)(n / divisor));
remainder = (UInt32)(n % divisor);
}
if (lo != 0 || remainder != 0)
{
n = ((UInt64)remainder << 32) | (UInt32)lo;
lo = (Int32)((UInt32)(n / divisor));
remainder = (UInt32)(n % divisor);
}
return remainder;
}
엄격한 성능 테스트를 수행하지는 않았지만 MacOS Sierra 10.12.6, 3,06GHz Intel Core i3 프로세서 및 .NetCore 2.1을 대상으로하는이 방법은 D. Nesterov보다 훨씬 빠른 것 같습니다. , 내가 언급했듯이 내 테스트는 엄격하지 않습니다). 추가 된 코드 복잡성에 대해 성능 향상이 보상을 받는지 여부를 평가하는 것은이를 구현하는 사람에게 달려 있습니다.