Почему я должен прибегать к строкам, чтобы получить точную обработку числа

При преобразовании данных в decimalИногда я получаю неверные результаты:

from decimal import *
D = Decimal
>>> D(5.2).quantize(D('.00000'), rounding=ROUND_DOWN)
Decimal('5.20000')
>>> D(5.3).quantize(D('.00000'), rounding=ROUND_DOWN)
Decimal('5.29999')

Я не думаю, что неточность с плавающей запятой является оправданием, поскольку я использую специализированный класс для работы с числами! Цитируется из документации по питону:

Десятичное число "основано на модели с плавающей запятой, которая была разработана с учетом потребностей людей и обязательно имеет первостепенный руководящий принцип - компьютеры должны обеспечивать арифметику, которая работает так же, как арифметика, которую люди изучают в школе". - выдержка из десятичная арифметическая спецификация

Это работает:

x=round(x - .0000049,5)
D(str(x) + (5-len(str(x).split('.')[1]))*'0')

2 ответа

Так как 5.3 само по себе уже неправильно. Когда вы передаете это Decimal(), это не будет волшебно исправить это. Вы должны использовать Decimal() на каждом шагу с самого начала. Это значит писать Decimal('5.3') вместо Decimal(5.3),

РЕДАКТИРОВАТЬ: OP заявил, что они используют JSON. Вот как вы анализируете десятичные числа из JSON:

import json, decimal
decimal_decoder = json.JSONDecoder(parse_float=decimal.Decimal)
parsed_json = decimal_decoder.decode(raw_json)

(где raw_json это JSON как строка). Это даст правильные десятичные числа.

Ваши предположения неверны. Литерал 5.3 недействителен (как уже сказал Кевин).

Проблема здесь в том, что вы не можете начать с неверного представления (с плавающей точкой), а затем предположить, что волшебство произойдет, и все исправить.

Легко построить нормальную среду:

a = D(53)
b = D(10)
c = a/b

А потом c является действительным номером.

Проблема в том, откуда берется ваше десятичное число? Если у вас есть 5.3 и вы хотите правильное представление, вы спрашиваете правильное представление числа, которое у вас есть в виде строки. Затем вам нужно использовать строку. Если у вас есть подразделение, воспользуйтесь этим и выполните операцию в decimal.Decimal среда. Если пользователь передал число, оставьте его в виде строки, а затем преобразуйте его в десятичное и т. Д.

Бонус редактировать: помните, что

$ 5.2999999999999999 == 5.3
> True

Вот что означает, что "буквальное представление неверно" (с вашей точки зрения и ограничений). С помощью 5.3 как поплавок подразумевает эту проблему.

Другие вопросы по тегам