【问题标题】:Determine if a number can be precisely represented in float/double format确定一个数字是否可以用浮点/双精度格式精确表示
【发布时间】:2015-02-20 17:09:39
【问题描述】:

如何确定一个数字(例如 1.577)是否可以精确地表示为 float 或 double 格式?

这意味着它是真实的 1.577 而不是 1.566999999999994324 等。

编辑: 我正在寻找一种工具,我可以在其中键入一个数字,它会显示它的双精度/浮点表示。所以这不仅仅是 c# 相关的问题。

【问题讨论】:

  • 你有什么格式/类型的号码?
  • 你的号码是字符串吗?
  • 假设我设置了 double x = 1.000000001。我需要这样的函数: IsEqual (x, "1.000000001");
  • “我正在寻找工具” - 在这种情况下,您的问题是题外话,将被关闭。
  • 以编程方式检查数字是否可以精确表示,您可以查看:stackoverflow.com/questions/12097805/…

标签: c# floating-point


【解决方案1】:

您可以使用online decimal to floating-point converter。例如,输入 1.577,您会得到两个不准确的指示:

1) 选中“不精确”框

2) 以双精度浮点数转换为 1.5769999999999999573674358543939888477325439453125。

将其与 1.25 之类的数字进行对比,该数字打印为 1.25,并且未选中“不精确”框。

(该转换器还可以检查单精度数字。)

【讨论】:

  • 编写该工具的人从未学会如何打印浮点数。 cs.indiana.edu/~dyb/pubs/FP-Printing-PLDI96.pdf
  • @leppie:我做到了。可以正确打印出浮点数的算法不止一种。
  • @leppie:我认为这不是转换器的重点。它旨在显示转换后的二进制浮点数的 exact 值(除其他可能性外,以十进制表示),而不是 Burger 和 Dybvig 给出的(通常)近似值。使用 Burger 和 Dybvig 来展示在转换为二进制时如何近似以十进制表示的一般值是毫无意义的。
  • @leppie:只是为了呼应 Mark 所说的,转换器的主要目的不是打印像 Burger 和 Dybvig、Steele 和 White 或 David Gay 这样的舍入值。这就是让新手陷入困境的原因。他们输入 0.1,机器打印回 0.1。他们不知道在内部,该值不是 0.1。这就是我编写该工具的原因。
【解决方案2】:

您已经获得了有关如何明确检查准确表示的答案。此外,无需正式测试即可消除许多数字,并且可以立即检查短小数,这既可行又有用。

假设您的数字的十进制表示在 N 个小数位后终止。例如,对于 1.577,N 为 3。取小数点后的部分,将其视为整数 577。如果该数字可以精确地表示为二进制分数,则该部分必须是 5^N 的整数倍。 577 不是 125 的整数倍,所以 1.577 不能精确表示。

如果你有一个合理的数量级,其十进制表示中只有几个有效数字,那么如果它通过了这个测试,它就是完全可表示的。例如,我无需计算机测试就知道 1.625 是完全可表示的。

【讨论】:

    【解决方案3】:

    嗯,IEEE-754 双精度有 53 位精度。那么,唯一可以准确表示的数字是:

    • 有理数,
    • 具有终止二进制表示,
    • 以 53 位或更少位表示

    所以...要确定给定的十进制值是否可以精确表示,以下内容就足够了:

    public bool IsExactlyRepresentableAsIeee754Double( decimal value )
    {
      // An IEEE 754 double has 53 bits of precision (52 bits of which are explicitly stored).
      const decimal ieee754MaxBits = 0x001FFFFFFFFFFFFF ;
    
      // move the decimal place right until the decimal's absolute value is integral
      decimal n = Math.Abs(value) ;
      while ( Decimal.Floor(n) != n )
      {
        n *= 10m;
      }
    
      bool isRepresentable = n <= ieee754MaxBits ;
      return isRepresentable ;
    }
    

    这里有一个警告:decimal 跟踪尾随、小数零(有关详细信息,请参见下文),因此 1m1.0m 具有不同的二进制表示。所以像decimal x = (decimal)(double)1.00m ; 这样的往返将导致x 具有与1m 相同的二进制表示,而不是1.00m

    因为小数的内部表示是well documented in the .Net CLR documentation and specs。它的后备存储可通过Decimal.GetBits() 方法轻松获得,由 4 个 32 位字组成,指定如下:

    十进制数的二进制表示由一个 1 位符号组成, 一个 96 位整数,以及一个用于划分 整数并指定它的哪一部分是小数。 比例因子是隐含的数字 10,升为指数 范围从 0 到 28。

    bits 是一个由 32 位有符号整数组成的四元素长数组。

    • 位 [0]、位 1 和位 [2] 包含低、中和 96 位整数的高 32 位。

    • bits [3] 包含比例因子和符号,由 [the] 以下部分:

      • 位 0 到 15(低位字)未使用且必须为零。
      • 位 16 到 23 必须包含 0 到 28 之间的指数,表示整数除以 10 的幂。
      • 位 24 到 30 未使用,必须为零。
      • 位 31 包含符号; 0 表示积极,1 表示消极。

    一个数值可能有几种可能的二进制表示;全部是 同样有效且在数值上等价。注意位表示 区分负零和正零。这些值被处理 在所有操作中都是平等的。

    因此,您可能会变得聪明并通过一点点玩弄来加快速度。关键是 decimal 跟踪尾随零,因此 1m1.00m 的二进制表示不同 - 1m 表示为 +1 缩放 100 ; 1.00m 表示为 +100 缩放 102。这让事情有点复杂。

    【讨论】:

    • 这应该如何工作?您的代码似乎会说 0.1 是可表示的,这是错误的。
    猜你喜欢
    • 1970-01-01
    • 2013-03-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-26
    • 1970-01-01
    • 1970-01-01
    • 2013-05-07
    相关资源
    最近更新 更多