Преобразовать число с плавающей точкой в ​​десятичное, неправильное значение - PullRequest
1 голос
/ 28 февраля 2020

У меня есть float число: 10071072872302, и мне нужно преобразовать его в decimal.

float number = 10071072872302f;
var convertNumber = Convert.ToDecimal(number);

Я получаю значение: 10071070000000, что Неверно .

Если сначала преобразовать его в double, а затем преобразовать в decimal:

float number = 10071072872302f;
var convertNumber = Convert.ToDecimal(Convert.ToDouble(number));

, я получу значение: 10071073357824, которое также Неправильно .

Если я просто конвертирую float в double:

float number = 10071072872302f;
var convertNumber = Convert.ToDouble(number);

, я получаю значение: 10071073357824, равное Неправильно .

Я действительно смущен. База данных моей компании разработана с типом данных decimal. Мне нужно программно рассчитать значение данных в соответствии с предварительно сконфигурированной формулой, мне нужно использовать библиотеку NCalc, которая возвращает тип данных с плавающей запятой. К сожалению, я не могу преобразовать float в десятичную`.

Может ли кто-нибудь помочь мне с этим тупиком?

РЕДАКТИРОВАТЬ Я хочу уточнить, почему мне нужно преобразовать число с плавающей точкой в ​​десятичное число.

Чтобы получить значение , Мне нужно вычислить выражение как: ISA16+ISA8+ISNULL(MAX((ABS(BSMS10)-ABS(BSMS10_)),0),0)

После взятия данных в базе данных для замены выражения, у меня есть такое выражение: 10071072872302+872302+MAX((ABS(-072872302)-ABS(2302)),0)

Чтобы вычислить значение В этом выражении я использую библиотеку NCalc

private static object CalculateString(string formula)
    {
        var expression = new Expression(formula);

        expression.EvaluateFunction += (name, args) =>
        {
            switch (name)
            {
                case "ABS":
                    args.Result = Math.Abs(Convert.ToDouble(args.Parameters[0].Evaluate()));
                    break;
                case "MAX":
                    args.Result = Math.Max(Convert.ToDouble(args.Parameters[0].Evaluate()), Convert.ToDouble(args.Parameters[1].Evaluate()));
                    break;
            }
        };

        var value = expression.Evaluate();
        return value;
    }

Выражения бывают разных форм. Однако, чтобы проверить значение, я попытался проверить с помощью числа 10071072872302

var result = CalculateString("10071072872302");

В результате я получил значение с плавающей запятой, поэтому мне нужно преобразовать его в десятичное значение, чтобы соответствовать типу данных в база данных. Вот почему я поставил вопрос выше.

Ответы [ 3 ]

6 голосов
/ 28 февраля 2020

У меня есть число с плавающей точкой: 10071072872302.

float number = 10071072872302f;

К сожалению, слишком поздно, float не имеет точного представления для 10071072872302. Таким образом, ближайший номер машины , который является наиболее близким, равен: 10071073357824. То есть это значение, которое фактически хранится в вашей переменной.

см., Например: FloatConverter

Вы должны стремиться получить другое представление, а не float (string, decimal или другую библиотеку bigint), чтобы получить это число.

Обратите внимание, что double должно быть действительно нормально для этого точного числа (double number = 10071072872302), но я бы не стал полагаться на тип числа с плавающей запятой (float или double) для хранения точного значения в любом case.

См. также этот шаблон c Q & A: Не работает ли математика с плавающей запятой?

1 голос
/ 29 февраля 2020

Я уже решил мою проблему. Я нашел Jint библиотеку вместо NCal c

https://www.nuget.org/packages/Jint

static Engine engine = new Engine()
             .Execute("function ABS(number){return Math.abs(number);}")
             .Execute("function MAX(a,b){return Math.max(a,b);}")
             .Execute("function Max(a,b){return Math.max(a,b);}")
             .Execute("function ISNULL(a,b){return a==null?b:a;}")
             .Execute("function Divide(a,b){return a==null||b==null||b==0?null:a/b;}");

    private static object CalculateString(string formula)
    {
        var result = engine
            .Execute(formula)
            .GetCompletionValue()
            .ToObject();
        return result == null ? null : (object)Convert.ToDecimal(result);
    }

Это работает как шарм:)

0 голосов
/ 29 февраля 2020

Здесь вы столкнулись с двумя проблемами:

  • Десятичное число против двоичного. NCal c, по-видимому, вычисляет выражения с использованием значений float. Но обратите внимание, что float и double являются двоичными типами чисел с плавающей запятой, и не все десятичные числа могут быть точно представлены в виде двоичного числа (независимо от его длины); примеры включают 0,1 и 0,3. (Для сравнения, decimal - это десятичный тип чисел с плавающей запятой.) Если NCal c разбирает десятичные числа в ваших выражениях на двоичные типы чисел, такие как float или double, то существует риск ошибка округления из-за необходимости преобразования определенных десятичных чисел в эти типы, даже если десятичные числа в ваших выражениях имеют относительно мало цифр.
  • Ограниченная точность. float, double и decimal - это числовые типы с ограниченной точностью. Например, float имеет точность 24 бита (около 7 десятичных цифр), а decimal имеет точность около 28 десятичных цифр. Если в ваших выражениях есть числа с большим количеством цифр, чем может обработать числовой тип, то произойдет потеря точности этих чисел после преобразования в этот тип. С другой стороны, BigInteger - это числовой тип с практически неограниченной точностью, поэтому это может быть целесообразно, если вы работаете только с целыми числами.

Я не знаком с NCal c , но посмотрите, можете ли вы установить NCal c для анализа и обработки чисел в типе номера decimal (при условии, что все интересующие вас номера имеют 28 десятичных цифр или менее). Если все числа в ваших выражениях являются целыми числами, и ничто в ваших выражениях не может привести к появлению дробей в вычислениях, вы также можете использовать BigInteger вместо decimal, если NCal c поддерживает это. Если NCal c не поддерживает decimal или BigInteger, то вам может потребоваться использовать другую библиотеку синтаксического анализа выражений.

...