С# Проверьте, имеет ли десятичная запятая более трех знаков после запятой?
У меня есть ситуация, которую я не могу изменить: одна таблица базы данных (таблица A) принимает 6 знаков после запятой, а соответствующий столбец в другой таблице (таблица B) имеет только 3 десятичных знака.
Мне нужно скопировать из A в B, но если A имеет более трех десятичных знаков, дополнительные данные будут потеряны. Я не могу изменить определение таблицы, но я могу добавить обходное решение. Поэтому я пытаюсь выяснить, как проверить, имеет ли десятичная запятая более трех знаков после запятой или нет?
например,
Table A
Id, Qty, Unit(=6dp)
1, 1, 0.00025
2, 4000, 0.00025
Table B
Id, TotalQty(=3dp)
Я хочу узнать, будет ли Qty * Unit из таблицы A иметь более 3 десятичных знаков (строка 1 завершится неудачно, строка 2 пройдет):
if (CountDecimalPlaces(tableA.Qty * tableA.Unit) > 3)
{
return false;
}
tableB.TotalQty = tableA.Qty * tableA.Unit;
Как реализовать функцию CountDecimalPlaces(decimal value) {}
?
Ответы
Ответ 1
Это работает на 3 десятичных знака, и его можно адаптировать для общего решения:
static bool LessThan3DecimalPlaces(decimal dec)
{
decimal value = dec * 1000;
return value == Math.Floor(value);
}
static void Test()
{
Console.WriteLine(LessThan3DecimalPlaces(1m * 0.00025m));
Console.WriteLine(LessThan3DecimalPlaces(4000m * 0.00025m));
}
Для реального общего решения вам нужно "деконструировать" десятичное значение в своих частях - посмотрите Decimal.GetBits для получения дополнительной информации.
Обновление: это простая реализация универсального решения, которое работает для всех десятичных знаков, чья целая часть меньше long.MaxValue(вам понадобится нечто вроде "большого целого" для общей функции trully).
static decimal CountDecimalPlaces(decimal dec)
{
int[] bits = Decimal.GetBits(dec);
int exponent = bits[3] >> 16;
int result = exponent;
long lowDecimal = bits[0] | (bits[1] >> 8);
while ((lowDecimal % 10) == 0)
{
result--;
lowDecimal /= 10;
}
return result;
}
static void Foo()
{
Console.WriteLine(CountDecimalPlaces(1.6m));
Console.WriteLine(CountDecimalPlaces(1.600m));
Console.WriteLine(CountDecimalPlaces(decimal.MaxValue));
Console.WriteLine(CountDecimalPlaces(1m * 0.00025m));
Console.WriteLine(CountDecimalPlaces(4000m * 0.00025m));
}
Ответ 2
Можно сравнить значение числа, округленного до 3 знаков после запятой, с исходным значением.
if (Decimal.Round(valueDecimal, 3) != valueDecimal)
{
//Too many decimals
}
Ответ 3
Основы - знать, как тестировать, есть ли десятичные числа, это делается путем сравнения значения с его округлением
double number;
bool hasDecimals = number == (int) number;
Затем, чтобы считать 3 десятичных знака, вам просто нужно сделать то же самое для своего номера, умноженного на 1000:
bool hasMoreThan3decimals = number*1000 != (int) (number * 1000)
Ответ 4
Все предлагаемые решения пока не расширяемы... отлично, если вы никогда не будете проверять значение, отличное от 3, но я предпочитаю это, потому что если требование изменяет код для обработки, он уже написан. Также это решение не будет переполняться.
int GetDecimalCount(decimal val)
{
if(val == val*10)
{
return int.MaxValue; // no decimal.Epsilon I don't use this type enough to know why... this will work
}
int decimalCount = 0;
while(val != Math.Floor(val))
{
val = (val - Math.Floor(val)) * 10;
decimalCount++;
}
return decimalCount;
}
Ответ 5
Решение carlosfigueira должно будет проверять значение 0 в противном случае "while ((lowDecimal% 10) == 0)" будет вызывать цикл бесконечности при вызове с dec = 0
static decimal CountDecimalPlaces(decimal dec)
{
if (dec == 0)
return 0;
int[] bits = Decimal.GetBits(dec);
int exponent = bits[3] >> 16;
int result = exponent;
long lowDecimal = bits[0] | (bits[1] >> 8);
while ((lowDecimal % 10) == 0)
{
result--;
lowDecimal /= 10;
}
return result;
}
Assert.AreEqual(0, DecimalHelper.CountDecimalPlaces(0m));
Assert.AreEqual(1, DecimalHelper.CountDecimalPlaces(0.5m));
Assert.AreEqual(2, DecimalHelper.CountDecimalPlaces(10.51m));
Assert.AreEqual(13, DecimalHelper.CountDecimalPlaces(10.5123456978563m));
Ответ 6
Умножение числа с 3 десятичными знаками на 10 на мощность 3 даст вам число без десятичных знаков. Это целое число при модуле % 1 == 0
. Поэтому я придумал это...
bool hasMoreThanNDecimals(decimal d, int n)
{
return !(d * (decimal)Math.Pow(10, n) % 1 == 0);
}
Возвращает true, если n
меньше (не равно) числа десятичных знаков.
Ответ 7
bool CountDecimalPlaces(decimal input)
{
return input*1000.0 == (int) (input*1000);
}
Ответ 8
Возможно, есть более элегантный способ сделать это, но с головы до головы я бы попробовал
- a = умножить на 1000
- b = обрезать
- if (b!= a), то есть дополнительная точность, потерянная
Ответ 9
Это очень простой код с одной строкой, чтобы получить число десятичных знаков в десятичной системе:
decimal myDecimal = 1.000000021300010000001m;
byte decimals = (byte)((Decimal.GetBits(myDecimal)[3] >> 16) & 0x7F);
Ответ 10
можете ли вы преобразовать его в строку и просто выполнить функцию len или не покрыть вашу ситуацию?
следующий вопрос:
было бы 300.4 в порядке?
Ответ 11
Public Function getDecimalCount(decWork As Decimal) As Integer
Dim intDecimalCount As Int32 = 0
Dim strDecAbs As String = decWork.ToString.Trim("0")
intDecimalCount = strDecAbs.Substring(strDecAbs.IndexOf(".")).Length -1
Return intDecimalCount
End Function
Ответ 12
Еще один вариант, основанный на решении @RodH257, но переработанный как метод расширения:
public static bool HasThisManyDecimalPlacesOrLess(this decimal value, int noDecimalPlaces)
{
return (Decimal.Round(value, noDecimalPlaces) == value);
}
Затем вы можете вызвать это как:
If !(tableA.Qty * tableA.Unit).HasThisManyDecimalPlacesOrLess(3)) return;