在Python中确定特定数字的精度和小数位数

发布于 2024-09-05 00:33:10 字数 609 浏览 4 评论 0原文

我在 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('.')) 是我能得到的最接近浮点数的精度和规模的?

I have a variable in Python containing a floating point number (e.g. num = 24654.123), and I'd like to determine the number's precision and scale values (in the Oracle sense), so 123.45678 should give me (8,5), 12.76 should give me (4,2), etc.

I was first thinking about using the string representation (via str or repr), but those fail for large numbers (although I understand now it's the limitations of floating point representation that's the issue here):

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

Edit:

Good points below. I should clarify. The number is already a float and is being pushed to a database via cx_Oracle. I'm trying to do the best I can in Python to handle floats that are too large for the corresponding database type short of executing the INSERT and handling Oracle errors (because I want to deal with the numbers a field, not a record, at a time). I guess map(len, repr(num).split('.')) is the closest I'll get to the precision and scale of the float?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

眼趣 2024-09-12 00:33:11

似乎 strrepr (Python 2) 更好:

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

seems like str is better choice than repr (Python 2):

>>> r=10.2345678
>>> r
10.234567800000001
>>> repr(r)
'10.234567800000001'
>>> str(r)
'10.2345678'
疯了 2024-09-12 00:33:11

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

I think you should consider using the decimal type instead of a float. The float type will give rounding errors because the numbers are represented internally in binary but many decimal numbers don't have an exact binary representation.

零崎曲识 2024-09-12 00:33:11

基本上,你不能使用浮点数。 使用十进制类型会有所帮助,并且如果您想要非常高的精度,请考虑使用 gmpy, GNU 多精度库移植到 Python。

Basically, you can't with floating point numbers. Using the decimal type would help and if you want really large precision, consider using gmpy, the GNU Multiple Precision library's port to Python.

云巢 2024-09-12 00:33:11

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

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

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

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

关于“我猜map(len, repr(num).split('.')) 是我能得到的最接近的精度和规模浮点数?”:你需要一个策略来处理 (a) 负数和零数 (b) 像 1.1e20 这样的数字

深入 Objects/floatobject.c 应该会找到新 repr 的 C 代码( ) 的 float 对象,如果您需要使用 Python 2.6 或更早版本。

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

(0) Please confirm or deny: You are given floats to use, this is unavoidable, you can't get your data as decimal, the Oracle datatypes include decimal-based types, and this fundamental mismatch is unavoidable. Please explain any full or partial denial.

(1) Your "fail for large numbers" remark is misleading/irrelevant/wrong -- you say that your starting point is a float, but 1234567890.0987654321 can't be represented as a float, as shown by the result of repr().

(2) Perhaps you could use the NEW repr (Python 2.7 and 3.1) which provides the minimum possible precision of repr(x) that still satisfies float(repr(x)) == x

E.g. old repr(1.1) produces "1.1000000000000001", new repr(1.1) produces "1.1"

About "I guess map(len, repr(num).split('.')) is the closest I'll get to the precision and scale of the float?": You need a strategy to handle (a) negative and zero numbers (b) numbers like 1.1e20

Digging in Objects/floatobject.c should turn up the C code for the new repr() of a float object, should you need to use Python 2.6 or earlier.

(3) Perhaps if you told us the specs for the relevant Oracle data types, we could help you devise checks for choosing which type can contain a given float value.

娇柔作态 2024-09-12 00:33:11

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

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

A number of symbols after comma. Works with int, float and Decimal types.

def get_num_precision(num):
    count = 0
    while num * 10**count % 1 != 0:
        count += 1
    return count
迷雾森÷林ヴ 2024-09-12 00:33:11
def get_precision(f1):
    str1=str(f1)
    return len(str1.split(".")[1])

此答案仅返回小数点后的位数。它不能保证准确,因为浮点数学可以创建 0.99999 或 0.00001。

def get_precision(f1):
    str1=str(f1)
    return len(str1.split(".")[1])

This answer returns only the number of digits after the decimal place. It will not be guaranteed as accurate because floating point math can create .99999 or .00001 for example.

伪装你 2024-09-12 00:33:11

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

   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

I found another solution that seems to be simpler, but I'm not sure exactly if it will work for all cases.

   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
木格 2024-09-12 00:33:11

如果您需要检查(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

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

If you need to check the number of corresponding digits (of a and 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

Note that this doesn't work with the "Decimal" module.

夏有森光若流苏 2024-09-12 00:33:11

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

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

If you need to check precision, you can try:

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
稀香 2024-09-12 00:33:11

这是另一种Decimal方法,至少适用于某些用例。它是否总是有效取决于您正在寻找的

123.45678 应该给我 (8,5),12.76 应该给我 (4,2),

from decimal import Decimal


def get_precision_and_scale(num: float):
    # Cast float to string to get shortest round-trippable representation
    d_num = Decimal(str(num))
    sign, digits, exp = d_num.as_tuple()
    scale = len(digits)
    precision = abs(exp)
    return scale, precision


print(get_precision_and_scale(123.45678))
# (8, 5)

print(get_precision_and_scale(12.76))
# (4, 2)

Here's another Decimal approach that will work for at least some use cases. Whether it will always work depends on exactly what you're looking for.

123.45678 should give me (8,5), 12.76 should give me (4,2),

from decimal import Decimal


def get_precision_and_scale(num: float):
    # Cast float to string to get shortest round-trippable representation
    d_num = Decimal(str(num))
    sign, digits, exp = d_num.as_tuple()
    scale = len(digits)
    precision = abs(exp)
    return scale, precision


print(get_precision_and_scale(123.45678))
# (8, 5)

print(get_precision_and_scale(12.76))
# (4, 2)

2024-09-12 00:33:10

获取小数点左边的位数很容易:

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)

Getting the number of digits to the left of the decimal point is easy:

int(log10(x))+1

The number of digits to the right of the decimal point is trickier, because of the inherent inaccuracy of floating point values. I'll need a few more minutes to figure that one out.

Edit: Based on that principle, here's the complete code.

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)
枕花眠 2024-09-12 00:33:10

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

>>> 10.2345

如下:

10.234500000000001

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

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

Not possible with floating point variables. For example, typing

>>> 10.2345

gives:

10.234500000000001

So, to get 6,4 out of this, you will have to find a way to distinguish between a user entering 10.2345 and 10.234500000000001, which is impossible using floats. This has to do with the way floating point numbers are stored. Use decimal.

import decimal
a = decimal.Decimal('10.234539048538495')
>>> str(a)
'10.234539048538495'
>>>  (len(str(a))-1, len(str(a).split('.')[1]))
(17,15)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文