Чтобы прямо ответить на вопрос, вот моя версия с использованием именования из функции R :
import math
def signif(x, digits=6):
if x == 0 or not math.isfinite(x):
return x
digits -= math.ceil(math.log10(abs(x)))
return round(x, digits)
Моей главной причиной для публикации этого ответа являются комментарии с жалобами на то, что «0,075» округляется до 0,07, а не 0,08. Это связано с тем, что «Новичок С» указывает на комбинацию арифметики с плавающей запятой, имеющей как конечную точность, так и представление с основанием-2 . Ближайшее к 0,075 число, которое действительно может быть представлено, немного меньше, поэтому округление получается не так, как вы могли наивно ожидать.
Также обратите внимание, что это относится к любому использованию недесятичной арифметики с плавающей запятой, например, C и Java имеют одинаковую проблему.
Чтобы показать более подробно, мы просим Python отформатировать число в шестнадцатеричном формате:
0.075.hex()
который дает нам: 0x1.3333333333333p-4
. Причина этого заключается в том, что нормальное десятичное представление часто включает в себя округление и, следовательно, не то, как компьютер фактически «видит» число. Если вы не привыкли к этому формату, пара полезных ссылок - это документы по Python и стандарт Си .
Чтобы показать, как эти числа работают немного, мы можем вернуться к нашей отправной точке, выполнив:
0x13333333333333 / 16**13 * 2**-4
который должен распечатать 0.075
. 16**13
это потому, что после десятичной запятой есть 13 шестнадцатеричных цифр, и 2**-4
потому, что шестнадцатеричные показатели являются base-2.
Теперь у нас есть некоторое представление о том, как представлены числа с плавающей точкой, и мы можем использовать decimal
модуль для большей точности, показывая нам, что происходит:
from decimal import Decimal
Decimal(0x13333333333333) / 16**13 / 2**4
давать: 0.07499999999999999722444243844
и, надеюсь, объяснить, почему round(0.075, 2)
оценивает0.07