Определить точность и масштаб определенного числа в Python - PullRequest
6 голосов
/ 11 июня 2010

У меня есть переменная в Python, содержащая число с плавающей запятой (например, num = 24654.123), и я бы хотел определить значения точности и масштаба (в смысле Oracle), поэтому 123.45678 должно дать мне (8,5), 12,76 должен дать мне (4,2) и т. Д.

Сначала я думал об использовании строкового представления (через str или repr), но они не подходят для больших чисел (хотя я понимаю,теперь проблема заключается в ограничениях представления с плавающей точкой):

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

Редактировать:

Хорошие моменты ниже.Я должен уточнить.Номер уже является числом с плавающей точкой и передается в базу данных через cx_Oracle.Я пытаюсь сделать все возможное в Python, чтобы обрабатывать числа с плавающей точкой, которые слишком велики для соответствующего типа базы данных, за исключением выполнения INSERT и обработки ошибок Oracle (потому что я хочу иметь дело с числами поле, а не запись, ввремя).Я думаю, map(len, repr(num).split('.')) ближе всего я получу точность и масштаб поплавка?

Ответы [ 7 ]

14 голосов
/ 11 июня 2010

Получить количество цифр слева от десятичной точки очень просто:

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)
5 голосов
/ 11 июня 2010

Невозможно с переменными с плавающей точкой. Например, набрав

>>> 10.2345

дает:

10.234500000000001

Итак, чтобы получить 6,4 из этого, вам нужно будет найти способ отличить пользователя, вводящего 10.2345 и 10.234500000000001, что невозможно с помощью чисел с плавающей запятой. Это связано со способом хранения чисел с плавающей запятой. Используйте decimal.

import decimal
a = decimal.Decimal('10.234539048538495')
>>> str(a)
'10.234539048538495'
>>>  (len(str(a))-1, len(str(a).split('.')[1]))
(17,15)
3 голосов
/ 11 июня 2010

похоже, что str - лучший выбор, чем repr:

>>> r=10.2345678
>>> r
10.234567800000001
>>> repr(r)
'10.234567800000001'
>>> str(r)
'10.2345678'
3 голосов
/ 11 июня 2010

Я думаю, вам следует рассмотреть возможность использования типа decimal вместо float. Тип float приведет к ошибкам округления, поскольку числа представляются внутри двоичного числа, но многие десятичные числа не имеют точного двоичного представления.

2 голосов
/ 11 июня 2010

(0) Пожалуйста, подтвердите или опровергните: вам даны числа с плавающей запятой для использования, это неизбежно, вы не можете получить данные как десятичные, типы данных Oracle включают типы на основе десятичных чисел, и это фундаментальное несоответствие неизбежно. Пожалуйста, объясните любой полный или частичный отказ.

(1) Ваше замечание «сбой для больших чисел» вводит в заблуждение / не имеет значения / неверно - вы говорите, что отправной точкой является число с плавающей точкой, но 1234567890.0987654321 не может быть представлено как число с плавающей точкой, как показывает результат repr ().

(2) Возможно, вы могли бы использовать NEW repr (Python 2.7 и 3.1), который обеспечивает минимально возможную точность repr (x), которая все еще удовлетворяет float(repr(x)) == x

например. старый repr (1.1) выдает «1.1000000000000001», новый repr (1.1) выдает «1.1»

О «Я думаю, карта (len, repr (num) .split ('.')) - самая близкая к точности и масштабу плавания?": Вам нужна стратегия для обработки (a) отрицательные и нулевые числа (б), такие как 1.1e20

Копание в Objects / floatobject.c должно включить код C для нового repr () объекта float, если вам нужно использовать Python 2.6 или более раннюю версию.

(3) Возможно, если вы сообщите нам спецификации для соответствующих типов данных Oracle, мы могли бы помочь вам разработать проверки для выбора типа, который может содержать данное значение с плавающей запятой.

1 голос
/ 11 июня 2010

В принципе, вы не можете использовать числа с плавающей запятой. Использование десятичного типа поможет, и если вы хотите действительно большую точность, рассмотрите возможность использования gmpy, GNU Multiple Precision порт библиотеки для Python.

0 голосов
/ 18 июня 2019

Я нашел другое решение, которое кажется более простым, но я точно не уверен, будет ли оно работать во всех случаях.

   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
...