【问题标题】:Determine precision and scale of particular number in Python在 Python 中确定特定数字的精度和小数位数
【发布时间】:2011-03-02 10:01:14
【问题描述】:

我在 Python 中有一个包含浮点数的变量(例如 num = 24654.123),我想确定数字的精度和比例值(在 Oracle 意义上),所以 123.45678 应该给我 (8,5 ), 12.76 应该给我 (4,2) 等等。

我最初考虑使用字符串表示(通过strrepr),但是对于大数字来说这些都失败了(尽管我现在明白这是浮点表示的限制,这就是这里的问题):

>>> num = 1234567890.0987654321
>>> str(num) = 1234567890.1
>>> repr(num) = 1234567890.0987654

编辑:

下面的好点。我应该澄清一下。该数字已经是一个浮点数,并且正在通过 cx_Oracle 推送到数据库。我正在尽我所能在 Python 中处理对于相应的数据库类型来说太大的浮点数,而不是执行 INSERT 和处理 Oracle 错误(因为我想处理字段中的数字,而不是记录,在一次)。我猜map(len, repr(num).split('.')) 是最接近浮点数的精度和比例的?

【问题讨论】:

    标签: python string floating-point scale precision


    【解决方案1】:

    逗号后的多个符号。适用于intfloatDecimal 类型。

    def get_num_precision(num):
        count = 0
        while num * 10**count % 1 != 0:
            count += 1
        return count
    

    【讨论】:

      【解决方案2】:

      如果需要检查精度,可以试试:

      def prec_check(a,b)
        a = str(a)
        b = str(b)
        do = bool(True)
        n = 0
        while do == True:
          if a and b and a[n] == a[b]:
            n += 1
          else:
            do = false
        return n
      

      【讨论】:

        【解决方案3】:

        如果需要检查对应数​​字的个数(a和b)

        def prec_check(a, b):
        
          a = str(a)
          b = str(b)
          do = bool(True)
          n = 0
        
          while do == True:
        
            if a and b and a[n] == a[b]:
              n += 1
        
            else:
              do = false
        
            return n
        

        请注意,这不适用于“十进制”模块。

        【讨论】:

          【解决方案4】:

          我找到了另一个似乎更简单的解决方案,但我不确定它是否适用于所有情况。

             import math
             x = 1.2345678
          
             def flip(string):
                 result = ""
                 for ch in string:
                     result = ch + result
                return result
          
             prec = int(math.log10(float(flip(str(x)))) + 1   # precision as int
          

          【讨论】:

            【解决方案5】:

            似乎strrepr 更好:

            >>> r=10.2345678
            >>> r
            10.234567800000001
            >>> repr(r)
            '10.234567800000001'
            >>> str(r)
            '10.2345678'
            

            【讨论】:

            • Lambda 函数,用于查找小数点右侧的位数lambda x: len(str(x).split('.')[-1]) if len(str(x).split('.')) > 1 else 0
            • 如果我们想为 100.0 输入返回 0 lambda x: len(str(x).split('.')[-1]) if int(x) 1 else 0
            • 从 Python 3.2 开始,reprstr 对于 float 数字不再有区别。见Why does str(float) return more digits in Python 3 than Python 2?
            【解决方案6】:

            获取小数点左边的位数很简单:

            int(log10(x))+1
            

            小数点右边的位数比较棘手,因为浮点值固有的不准确性。我还需要几分钟才能弄清楚这一点。

            编辑:基于这个原则,这里是完整的代码。

            import math
            
            def precision_and_scale(x):
                max_digits = 14
                int_part = int(abs(x))
                magnitude = 1 if int_part == 0 else int(math.log10(int_part)) + 1
                if magnitude >= max_digits:
                    return (magnitude, 0)
                frac_part = abs(x) - int_part
                multiplier = 10 ** (max_digits - magnitude)
                frac_digits = multiplier + int(multiplier * frac_part + 0.5)
                while frac_digits % 10 == 0:
                    frac_digits /= 10
                scale = int(math.log10(frac_digits))
                return (magnitude + scale, scale)
            

            【讨论】:

            • 出于好奇,数字 14 是从哪里来的?它是独立于平台的吗?
            • 取决于平台,但大多数平台将使用 IEEE-754。 en.wikipedia.org/wiki/… 我本可以做到 15,但我想保守一点,并确保我的四舍五入正常工作。
            • 可以从 Python 发行版中获得,而不是硬编码 14 个吗?
            • @aaragon 有可能,我不知道。由于我知道的每个 Python 版本都基于 IEEE-754,所以我没有动力去寻找。
            • @aaragon 好奇心终于战胜了我,所以我查了一下。您要查找的数字是 sys.float_info.dig,在我的 Python 版本上是 15。
            【解决方案7】:

            (0) 请确认或拒绝:给您使用浮点数,这是不可避免的,您无法将数据作为十进制获取,Oracle 数据类型包括基于十进制的类型,这种基本不匹配是不可避免的。请解释任何完全或部分否认。

            (1) 您的“fail for large numbers”评论具有误导性/不相关/错误——您说您的起点是浮点数,但 1234567890.0987654321 不能表示为浮点数,如 repr 的结果所示()。

            (2) 也许您可以使用新的 repr(Python 2.7 和 3.1),它提供了仍然满足 float(repr(x)) == x 的 repr(x) 的最小可能精度

            例如旧的 repr(1.1) 产生“1.1000000000000001”,新的 repr(1.1) 产生“1.1”

            关于“我猜 map(len, repr(num).split('.')) 是最接近浮点数的精度和比例的?”:您需要一种策略来处理 (a)负数和零数 (b) 数字,例如 1.1e20

            如果您需要使用 Python 2.6 或更早版本,则在 Objects/floatobject.c 中挖掘应该会为浮点对象的新 repr() 找到 C 代码。

            (3) 也许如果您告诉我们相关 Oracle 数据类型的规范,我们可以帮助您设计检查以选择哪种类型可以包含给定的浮点值。

            【讨论】:

            • 0。确认,基本上。一切都设置为使用浮点数,我不相信 cx_Oracle 会处理 Decimal 类型,至少我已经设置好了。 1. 编辑更正。 2.有趣,不幸的是我落后于2.7。 3. 我正在尝试编写能够动态工作的代码,即使 Oracle 列类型的精度或比例发生变化。 (哎呀,抱歉格式不好)
            【解决方案8】:

            浮点变量不可能。例如,键入

            >>> 10.2345
            

            给予:

            10.234500000000001
            

            因此,要从中获得 6,4,您必须找到一种方法来区分输入 10.234510.234500000000001 的用户,而使用浮点数是不可能的。这与存储浮点数的方式有关。使用decimal

            import decimal
            a = decimal.Decimal('10.234539048538495')
            >>> str(a)
            '10.234539048538495'
            >>>  (len(str(a))-1, len(str(a).split('.')[1]))
            (17,15)
            

            【讨论】:

            • 可以使用浮点数;看我的回答。
            • 我的回答也有效。关键是将位数限制为浮点类型的已知精度,并对结果进行四舍五入。
            【解决方案9】:

            我认为您应该考虑使用decimal 类型而不是floatfloat 类型会产生舍入错误,因为数字在内部以二进制表示,但许多十进制数字没有精确的二进制表示。

            【讨论】:

              【解决方案10】:

              Basically, you can't with floating point numbers. 使用小数类型会有所帮助,如果您想要非常高的精度,请考虑使用gmpy,这是 GNU 多精度库的 Python 端口。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2013-07-17
                • 1970-01-01
                • 1970-01-01
                • 2014-07-15
                • 2015-05-25
                • 2011-03-31
                • 1970-01-01
                相关资源
                最近更新 更多