【问题标题】:Convert float to decimal, wrong value将浮点数转换为十进制,错误值
【发布时间】:2020-02-28 14:15:58
【问题描述】:

我有一个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");

我得到的结果是一个浮点值,所以我需要将它转换为十进制以匹配数据库中的数据类型。这就是我提出上述问题的原因。

【问题讨论】:

  • float 类型最多可以存储 7 位精度。所以你得到的是正确的。考虑 doubledecimal。显然你会喜欢后者,使用 10071072872302m 作为文字。
  • 10071072872302 不能在不损失精度的情况下存储在浮点数中。
  • number.ToString("F") 结果为 10071070000000.00。所以从一开始就错了。
  • 其实是我从第三方得到的float值,所以需要转换成十进制。但是太难了
  • 从哪里得到它并不重要。该值不适合浮点数。您不会通过转换找回丢失的精度。

标签: c# floating-point type-conversion decimal


【解决方案1】:

我有一个浮点数:10071072872302。

float number = 10071072872302f;

不幸的是,为时已晚,float 没有 10071072872302 的确切表示。所以最接近的机器编号是:10071073357824。这就是实际存储在变量中的值。

例如:FloatConverter

您必须努力获得另一个表示,而不是 floatstringdecimal 或其他 bigint 库)才能获得这个数字。

请注意,double 应该实际上可以对于这个精确的数字double number = 10071072872302),但我不会依赖浮点数类型(@987654330 @ 或 double) 在任何情况下都存储一个精确的值。

另请参阅此通用问答:Is floating point math broken?

【讨论】:

  • @MinhGiang 好的,这更准确,感谢您的编辑。我不知道 NCalc,但快速搜索显示有一个 Convert.ToDecimal() 函数,你可以尝试使用它而不是 ToDouble() 吗?
  • The result I get is a float value :这是我对 NCalc 的缺乏了解:你怎么知道它是 float (如 System.Float 的别名)?你的方法返回的签名是object,所以我不能确定到底发生了什么。
【解决方案2】:

我的问题已经解决了。我找到了Jint 库而不是 NCalc

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);
    }

它就像一个魅力:)

【讨论】:

    【解决方案3】:

    您在这里遇到了两个问题:

    • 十进制与二进制。 NCalc 似乎正在使用 float 值计算表达式。但是注意floatdouble是二进制浮点数类型,并不是所有的十进制数都可以用二进制数类型精确表示(不管它的长度);示例包括 0.1 和 0.3。 (相比之下,decimal 是十进制浮点数类型。)如果 NCalc 将表达式中的十进制数解析为二进制数类型,例如 floatdouble,则存在舍入错误的风险需要将某些十进制数转换为这些类型,即使表达式中的十进制数的位数相对较少。
    • 有限精度。floatdoubledecimal 是有限精度数字类型。例如,float 的精度为 24 位(大约 7 个十进制数字),decimal 的精度大约为 28 个十进制数字。如果您的表达式中的数字比数字类型可以处理的数字多,那么一旦转换为该类型,这些数字就会失去精度。另一方面,BigInteger 是一种实际上具有无限精度的数字类型,因此如果您只使用整数,这可能是可行的。

    我不熟悉 NCalc,但看看你是否可以设置 NCalc 来解析和处理 decimal 数字类型的数字(前提是你关心的所有数字都包含 28 位或更少的十进制数字)。如果您的表达式中的所有数字都是整数,并且您的表达式中的任何内容都不会导致计算中出现分数,您也可以使用BigInteger 而不是decimal(如果 NCalc 支持)。如果 NCalc 不支持decimalBigInteger,那么您可能需要使用另一个表达式解析库。

    【讨论】:

      猜你喜欢
      • 2014-03-01
      • 1970-01-01
      • 2018-03-26
      • 1970-01-01
      • 2011-03-05
      • 2014-11-24
      • 2010-12-08
      • 2016-06-02
      相关资源
      最近更新 更多