วิธีการปัดเศษตัวเลขเป็นตัวเลขสำคัญใน Python


148

ฉันต้องปัดเศษทศนิยมเพื่อให้แสดงใน UI เช่นกับบุคคลสำคัญ:

1234 -> 1,000

0.12 -> 0.1

0.012 -> 0.01

0.062 -> 0.06

6253 -> 6000

1999 -> 2000

มีวิธีที่ดีในการทำเช่นนี้โดยใช้ห้องสมุด Python หรือฉันต้องเขียนเอง?


2
คุณเพียงแค่จัดรูปแบบผลลัพธ์หรือไม่ คุณกำลังถามเกี่ยวกับเรื่องนี้? docs.python.org/library/stdtypes.html#string-formattingหรือนี่คืออะไร docs.python.org/library/string.html#string-formatting
S.Lott

ผลลัพธ์แบบใดที่คุณคาดหวังสำหรับ 0.062 และ 6253
lamirap

แพคเกจที่มีความแม่นยำในขณะนี้ทำเช่นนี้ รายละเอียดคำตอบที่โพสต์ของฉันจะนำไปใช้อย่างไร
William Rusnack

คำตอบ:


146

คุณสามารถใช้ตัวเลขติดลบเพื่อปัดเศษจำนวนเต็ม:

>>> round(1234, -3)
1000.0

ดังนั้นหากคุณต้องการตัวเลขที่สำคัญที่สุดเท่านั้น:

>>> from math import log10, floor
>>> def round_to_1(x):
...   return round(x, -int(floor(log10(abs(x)))))
... 
>>> round_to_1(0.0232)
0.02
>>> round_to_1(1234243)
1000000.0
>>> round_to_1(13)
10.0
>>> round_to_1(4)
4.0
>>> round_to_1(19)
20.0

คุณอาจต้องดูแลการเปลี่ยนโฟลตเป็นจำนวนเต็มถ้ามันใหญ่กว่า 1


3
นี่คือทางออกที่ถูกต้อง การใช้log10เป็นวิธีการที่เหมาะสมเพียงอย่างเดียวในการกำหนดวิธีการปัดเศษ
Wolph

73
round_to_n = lambda x, n: round (x, -int (พื้น (log10 (x))) + (n - 1))
Roy Hyunjin Han

28
คุณควรใช้log10(abs(x))มิฉะนั้นตัวเลขที่ติดลบจะล้มเหลว (และรักษาx == 0แยกของหลักสูตร)
โทเบียส KIENZLER

2
ฉันได้สร้างแพ็คเกจที่ทำได้ในตอนนี้และอาจจะง่ายกว่าและแข็งแกร่งกว่าแพ็คเกจนี้ โพสต์ลิงค์ , Repo การเชื่อมโยง หวังว่านี่จะช่วยได้!
William Rusnack

2
round_to_n = lambda x, n: x if x == 0 else round(x, -int(math.floor(math.log10(abs(x)))) + (n - 1))ป้องกันx==0และx<0ขอขอบคุณ @RoyHyunjinHan และ @TobiasKienzler ไม่ได้รับการปกป้องจากไม่ได้กำหนดเช่น math.inf หรือขยะอย่าง None ฯลฯ
AJP

98

% g ในการจัดรูปแบบสตริงจะจัดรูปแบบทศนิยมที่ปัดเศษเป็นตัวเลขสำคัญจำนวนหนึ่ง บางครั้งมันจะใช้สัญลักษณ์ทางวิทยาศาสตร์ 'e' ดังนั้นแปลงสตริงที่ปัดเศษกลับเป็นทศนิยมจากนั้นจึงใช้การจัดรูปแบบสตริง% s

>>> '%s' % float('%.1g' % 1234)
'1000'
>>> '%s' % float('%.1g' % 0.12)
'0.1'
>>> '%s' % float('%.1g' % 0.012)
'0.01'
>>> '%s' % float('%.1g' % 0.062)
'0.06'
>>> '%s' % float('%.1g' % 6253)
'6000.0'
>>> '%s' % float('%.1g' % 1999)
'2000.0'

7
ข้อกำหนดของ OP สำหรับปี 1999 ที่จะจัดรูปแบบเป็น '2000' ไม่ใช่เป็น '2000.0' ฉันไม่เห็นวิธีที่ไม่สำคัญที่จะเปลี่ยนวิธีการของคุณเพื่อให้บรรลุเป้าหมายนี้
ทิมมาร์ติ

1
มันเป็นสิ่งที่ฉันต้องการเสมอ! คุณจะพบสิ่งนี้ที่ไหน
djhaskin987

12
โปรดทราบว่าพฤติกรรมของ% g นั้นไม่ถูกต้องเสมอไป โดยเฉพาะอย่างยิ่งมันจะจดจ้องศูนย์ต่อท้ายแม้ว่าพวกเขาจะมีความสำคัญ หมายเลข 1.23400 มีตัวเลขนัยสำคัญ 6 หลัก แต่ "% .6g"% (1.23400) จะส่งผลให้ "1.234" ซึ่งไม่ถูกต้อง รายละเอียดเพิ่มเติมในโพสต์บล็อกนี้: randlet.com/blog/python-signential-figures-format
randlet

3
เช่นเดียวกับวิธีการในคำตอบของ Evgeny นี้ล้มเหลวในการได้อย่างถูกต้องรอบไป0.075 0.08มันกลับมา0.07แทน
Gabriel

1
round_sig = lambda f,p: float(('%.' + str(p) + 'e') % f)ช่วยให้คุณสามารถปรับจำนวนตัวเลขที่มีนัยสำคัญ!
denizb

49

หากคุณต้องการให้มีทศนิยมมากกว่า 1 อย่าง (เช่นเดียวกับ Evgeny):

>>> from math import log10, floor
>>> def round_sig(x, sig=2):
...   return round(x, sig-int(floor(log10(abs(x))))-1)
... 
>>> round_sig(0.0232)
0.023
>>> round_sig(0.0232, 1)
0.02
>>> round_sig(1234243, 3)
1230000.0

8
round_sig (-0.0232) -> ข้อผิดพลาดโดเมนคณิตศาสตร์คุณอาจต้องการเพิ่ม abs () ในนั้น;)
dgorissen

2
เช่นเดียวกับวิธีการในการตอบ Evgeny และปีเตอร์เกรแฮมนี้ล้มเหลวในการได้อย่างถูกต้องรอบไป0.075 0.08มันกลับมา0.07แทน
Gabriel

3
นอกจากนี้ยังล้มเหลวสำหรับ round_sig (0)
Yuval Atzmon

2
@ กาเบรียลนั่นคือ "ฟีเจอร์" ของไพ ธ อนที่ทำงานอยู่ในคอมพิวเตอร์ของคุณและแสดงออกถึงการทำงานของroundมัน docs.python.org/2/tutorial/floatingpoint.html#tut-fp-issues
สามเณร C

1
@Gabriel ฉันได้เพิ่มคำตอบที่อธิบายว่าทำไมคุณควรคาดหวังว่าจะได้ 0.7 กลับมาจากการปัดเศษ "0.075"! ดูstackoverflow.com/a/56974893/1358308
Sam Mason

30
f'{float(f"{i:.1g}"):g}'
# Or with Python <3.6,
'{:g}'.format(float('{:.1g}'.format(i)))

วิธีนี้แตกต่างจากโซลูชันอื่นทั้งหมดเนื่องจาก:

  1. มันตรงแก้คำถามสหกรณ์
  2. มันไม่ได้จำเป็นต้องมีแพคเกจพิเศษ
  3. มันไม่จำเป็นต้องมีฟังก์ชั่นเสริมที่ผู้ใช้กำหนดหรือการดำเนินการทางคณิตศาสตร์

สำหรับตัวเลขnสำคัญใด ๆ คุณสามารถใช้:

print('{:g}'.format(float('{:.{p}g}'.format(i, p=n))))

ทดสอบ:

a = [1234, 0.12, 0.012, 0.062, 6253, 1999, -3.14, 0., -48.01, 0.75]
b = ['{:g}'.format(float('{:.1g}'.format(i))) for i in a]
# b == ['1000', '0.1', '0.01', '0.06', '6000', '2000', '-3', '0', '-50', '0.8']

หมายเหตุ : ด้วยวิธีนี้มันเป็นไปไม่ได้ที่จะปรับจำนวนตัวเลขที่สำคัญแบบไดนามิกจากอินพุตเนื่องจากไม่มีวิธีมาตรฐานในการแยกแยะตัวเลขด้วยตัวเลขที่แตกต่างกันของศูนย์ต่อท้าย ( 3.14 == 3.1400) หากคุณต้องการทำเช่นนั้นจำเป็นต้องใช้ฟังก์ชั่นที่ไม่ได้มาตรฐานเช่นเดียวกับที่อยู่ในแพ็คเกจเพื่อความแม่นยำ


FYI: ฉันพบวิธีนี้เป็นอิสระจาก eddygeek ในขณะที่ฉันพยายามที่จะแก้ปัญหาเดียวกันในรหัสของฉัน ตอนนี้ฉันรู้แล้วว่าวิธีแก้ปัญหาของฉันนั้นชัดเจนเกือบจะเหมือนกับเขา (ฉันเพิ่งสังเกตเห็นผลลัพธ์ที่ผิดพลาดและไม่ต้องอ่านรหัสผิดพลาด) ความคิดเห็นสั้น ๆ ข้างใต้คำตอบของเขาน่าจะเพียงพอแล้วแทนที่จะเป็นคำตอบใหม่ ... ความแตกต่างเพียงอย่างเดียวคือการใช้:gฟอร์แมตเตอร์สองเท่าที่สงวนจำนวนเต็ม
Falken

ว้าวคำตอบของคุณจะต้องอ่านจากบนลงล่างจริง ๆ ) เคล็ดลับดับเบิลแคสต์นี้สกปรก แต่เรียบร้อย (โปรดทราบว่าในปี 1999 การจัดรูปแบบตามที่2000.0 แนะนำ 5 หลักสำคัญดังนั้นจึงต้องผ่าน{:g}อีกครั้ง) โดยทั่วไปแล้วเลขจำนวนเต็มที่มีศูนย์ต่อท้ายจะคลุมเครือสำหรับตัวเลขที่มีนัยสำคัญยกเว้นว่าจะใช้เทคนิคบางอย่าง
Tomasz Gandor

8

ฉันได้สร้างแพ็คเกจเพื่อความแม่นยำที่ทำในสิ่งที่คุณต้องการ จะช่วยให้คุณให้ตัวเลขที่สำคัญมากขึ้นหรือน้อยลง

นอกจากนี้ยังแสดงสัญลักษณ์มาตรฐานวิทยาศาสตร์และวิศวกรรมด้วยตัวเลขที่ระบุจำนวนมาก

ในคำตอบที่ยอมรับมีบรรทัด

>>> round_to_1(1234243)
1000000.0

ที่ระบุจริงมะเดื่อ 8 sig สำหรับหมายเลข 1234243 ห้องสมุดของฉันจะแสดงเพียงตัวเลขที่สำคัญ:

>>> from to_precision import to_precision
>>> to_precision(1234243, 1, 'std')
'1000000'
>>> to_precision(1234243, 1, 'sci')
'1e6'
>>> to_precision(1234243, 1, 'eng')
'1e6'

นอกจากนี้ยังจะปัดเศษตัวเลขที่มีความสำคัญครั้งสุดท้ายและสามารถเลือกเครื่องหมายที่จะใช้โดยอัตโนมัติหากไม่ได้ระบุสัญกรณ์:

>>> to_precision(599, 2)
'600'
>>> to_precision(1164, 2)
'1.2e3'

ตอนนี้ฉันกำลังมองหาเหมือนกัน แต่นำไปใช้กับหมีแพนด้า df
mhoff

@hhoff คุณสามารถใช้แผนที่แพนด้ากับแลมบ์ดา lambda x: to_precision(x, 2)
William Rusnack

เพิ่มไป (PyPI) [ pypi.org/] ไม่มีอะไรแบบนี้ที่มีอยู่เท่าที่ฉันสามารถบอกได้
Morgoth

นี่เป็นแพ็คเกจที่ยอดเยี่ยม แต่ฉันคิดว่าคุณสมบัติส่วนใหญ่ตอนนี้อยู่ในโมดูล
sigfig

1
มันมีข้อผิดพลาด: std_notation (9.999999999999999e-05, 3) ให้: '0.00010' ซึ่งมีเพียง 2 เลขนัยสำคัญ
Boris Mulder

5

ในการปัดเศษจำนวนเต็มเป็น 1 ตัวเลขสำคัญแนวคิดพื้นฐานคือการแปลงให้เป็นทศนิยมด้วยตัวเลข 1 หลักก่อนจุดและปัดเศษจากนั้นแปลงกลับเป็นขนาดจำนวนเต็มดั้งเดิม

ในการทำเช่นนี้เราจำเป็นต้องรู้พลังที่ใหญ่ที่สุดของ 10 น้อยกว่าจำนวนเต็ม เราสามารถใช้ฟังก์ชัน floor ของ log 10 สำหรับสิ่งนี้

from math import log10, floor
def round_int(i,places):
    if i == 0:
        return 0
    isign = i/abs(i)
    i = abs(i)
    if i < 1:
        return 0
    max10exp = floor(log10(i))
    if max10exp+1 < places:
        return i
    sig10pow = 10**(max10exp-places+1)
    floated = i*1.0/sig10pow
    defloated = round(floated)*sig10pow
    return int(defloated*isign)

1
อีกทางเลือกหนึ่งสำหรับการแก้ปัญหาที่ทำงานโดยไม่ต้องใช้รอบของงูใหญ่ (.. , หลัก) และไม่มีการเชื่อมต่อ!
Steve Rogers

5

หากต้องการตอบคำถามโดยตรงนี่คือรุ่นของฉันที่ใช้การตั้งชื่อจากฟังก์ชัน 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 นี่คือเนื่องจากตามที่ชี้ให้เห็นโดย "สามเณร C" เพื่อการรวมกันของเลขคณิตจุดลอยที่มีทั้งความแม่นยำแน่นอนและการเป็นตัวแทนฐาน -2ความแม่นยำแน่นอนและเป็นตัวแทนฐาน-2ตัวเลขที่ใกล้ที่สุดถึง 0.075 ที่สามารถแสดงได้จริงนั้นมีขนาดเล็กกว่าเล็กน้อยดังนั้นการปัดเศษจึงแตกต่างจากที่คุณคาดไม่ถึง

นอกจากนี้โปรดทราบว่าสิ่งนี้ใช้กับการใช้เลขทศนิยมใด ๆ ที่ไม่ใช่ทศนิยมเช่น C และ Java ทั้งคู่มีปัญหาเดียวกัน

เพื่อแสดงรายละเอียดเพิ่มเติมเราขอให้ Python จัดรูปแบบตัวเลขในรูปแบบ "hex":

0.075.hex()

0x1.3333333333333p-4ซึ่งจะช่วยให้เรา: เหตุผลในการทำเช่นนี้คือการแทนทศนิยมปกติมักเกี่ยวข้องกับการปัดเศษและไม่ใช่วิธีที่คอมพิวเตอร์ "เห็น" จำนวนจริง หากคุณไม่คุ้นเคยกับรูปแบบนี้การอ้างอิงที่เป็นประโยชน์สองสามประการคือPython docsและมาตรฐาน Cประการ

เพื่อแสดงให้เห็นว่าตัวเลขเหล่านี้ทำงานได้ดีเพียงใดเราสามารถกลับไปที่จุดเริ่มต้นของเราได้โดยทำดังนี้

0x13333333333333 / 16**13 * 2**-4

0.075ซึ่งควรควรจะพิมพ์ออกมา 16**13เป็นเพราะมีเลขฐานสิบหก 13 หลักหลังจุดทศนิยมและ2**-4เป็นเพราะเลขชี้กำลังเลขฐานสิบหกเป็นฐาน -2

ตอนนี้เรามีความคิดว่าการเป็นตัวแทนของลอยนั้นสามารถใช้decimalโมดูลเพื่อให้ความแม่นยำมากขึ้นแสดงให้เราเห็นสิ่งที่เกิดขึ้น:

from decimal import Decimal

Decimal(0x13333333333333) / 16**13 / 2**4

ให้: 0.07499999999999999722444243844และหวังว่าจะอธิบายว่าทำไมround(0.075, 2)ประเมินถึง0.07


1
นี่คือคำอธิบายที่ดีว่าเหตุใด 0.075 จึงถูกปัดเศษลงเป็น 0.07 ที่ระดับรหัสแต่เรา (ในวิทยาศาสตร์กายภาพ) ได้รับการสอนให้ทำการปัดเศษขึ้นเสมอ ดังนั้นพฤติกรรมที่คาดหวังจึงเป็นจริงที่มี 0.08 ดังนั้นประเด็นความแม่นยำของจุดลอยตัวจึงยังคงมีอยู่
Gabriel

1
ฉันไม่แน่ใจว่าความสับสนของคุณอยู่ที่ไหน: เมื่อคุณป้อน 0.075 คุณกำลังเข้าสู่ ~ 0.07499 (ดังกล่าวข้างต้น) ซึ่งจะวนรอบตามกฎคณิตศาสตร์ปกติ ถ้าคุณใช้ชนิดข้อมูล (เช่นทศนิยมทศนิยม ) ที่สามารถเป็นตัวแทนของ 0.075 แล้วมันควรจะเป็นรอบ 0.08
Sam Mason

ฉันไม่สับสน เมื่อฉันป้อน 0.075 ฉันกำลังเข้าจริง 0.075 ไม่ว่าจะเกิดอะไรขึ้นในคณิตศาสตร์จุดลอยตัวในรหัสฉันไม่สนใจ
Gabriel

@Gabriel: และถ้าคุณได้ป้อนจงใจ0.074999999999999999สิ่งที่คุณจะคาดหวังที่จะได้รับในกรณีที่ว่า?
Mark Dickinson

@ MarkDickinson ที่ขึ้นอยู่กับ รูปสำคัญ: 0.07, สอง: 0.075
Gabriel

4
def round_to_n(x, n):
    if not x: return 0
    power = -int(math.floor(math.log10(abs(x)))) + (n - 1)
    factor = (10 ** power)
    return round(x * factor) / factor

round_to_n(0.075, 1)      # 0.08
round_to_n(0, 1)          # 0
round_to_n(-1e15 - 1, 16) # 1000000000000001.0

หวังว่าจะใช้คำตอบที่ดีที่สุดทั้งหมดข้างต้น (ลบด้วยความสามารถที่จะใส่มันเป็นแลมบ์ดาบรรทัดเดียว)) ยังไม่ได้สำรวจอย่าลังเลที่จะแก้ไขคำตอบนี้:

round_to_n(1e15 + 1, 11)  # 999999999999999.9

4

ฉันแก้ไขวิธีแก้ปัญหาของ indgar เพื่อจัดการกับจำนวนลบและตัวเลขเล็ก ๆ (รวมศูนย์)

from math import log10, floor
def round_sig(x, sig=6, small_value=1.0e-9):
    return round(x, sig - int(floor(log10(max(abs(x), abs(small_value))))) - 1)

ทำไมไม่ลองทดสอบดูx == 0ล่ะ? return 0 if x==0 else round(...)ถ้าคุณรักหนึ่งซับเพียง
pjvandehaar

2
@pjvandehaar คุณถูกต้องสำหรับกรณีทั่วไปและฉันควรจะใส่สิ่งต่อไปนี้นอกจากนี้สำหรับการคำนวณเชิงตัวเลขฉันต้องดำเนินการเป็นบางครั้งเราจะได้รับหมายเลขเช่น 1e-15 ในใบสมัครของเราเราต้องการเปรียบเทียบตัวเลขสองตัวเล็ก ๆ (หนึ่งในนั้นอาจเป็นศูนย์) ได้รับการพิจารณาอย่างเท่าเทียมกัน บางคนต้องการปัดเศษตัวเลขเล็ก ๆ (อาจเป็น 1e-9, 1e-15 หรือแม้แต่ 1e-300) เป็นศูนย์
ryan281

1
น่าสนใจ ขอบคุณที่อธิบายว่า ในกรณีนี้ฉันชอบวิธีนี้มาก
pjvandehaar

@Morgoth นี่เป็นปัญหาที่น่าสนใจและยาก ค่าที่พิมพ์ออกมาจะไม่แสดงตัวเลขนัยสำคัญ 3 ตัว แต่ค่านั้นถูกต้อง (เช่น0.970 == 0.97) ฉันคิดว่าคุณสามารถใช้โซลูชันการพิมพ์อื่น ๆ เช่นf'{round_sig(0.9701, sig=3):0.3f}'ถ้าคุณต้องการให้พิมพ์ศูนย์
ryan281

3

หากคุณต้องการปัดเศษโดยไม่เกี่ยวข้องกับสตริงลิงก์ที่ฉันพบในความคิดเห็นด้านบน:

http://code.activestate.com/lists/python-tutor/70739/

นัดฉันดีที่สุด จากนั้นเมื่อคุณพิมพ์ด้วยตัวอธิบายการจัดรูปแบบสตริงคุณจะได้ผลลัพธ์ที่สมเหตุสมผลและคุณสามารถใช้การแทนค่าตัวเลขเพื่อวัตถุประสงค์ในการคำนวณอื่น ๆ

โค้ดที่ลิงก์คือสามซับ: def, doc และ return มีข้อบกพร่อง: คุณต้องตรวจสอบหาลอการิทึมที่ระเบิด นั่นเป็นเรื่องง่าย sys.float_info.minเปรียบเทียบการป้อนข้อมูลเพื่อ ทางออกที่สมบูรณ์คือ:

import sys,math

def tidy(x, n):
"""Return 'x' rounded to 'n' significant digits."""
y=abs(x)
if y <= sys.float_info.min: return 0.0
return round( x, int( n-math.ceil(math.log10(y)) ) )

มันใช้งานได้กับค่าตัวเลขเซนต์คิตส์และเนวิสอาจเป็นfloatถ้าคุณต้องการที่จะตอบสนองด้วยเหตุผลบางอย่าง คุณสามารถผลักดันขีด จำกัด ไปที่:

sys.float_info.min*sys.float_info.epsilon

โดยไม่มีการยั่วยุข้อผิดพลาดถ้าด้วยเหตุผลบางอย่างคุณกำลังทำงานกับค่า miniscule


2

ฉันไม่สามารถคิดอะไรที่สามารถจัดการกับเรื่องนี้ได้ แต่ก็มีการจัดการที่ค่อนข้างดีสำหรับตัวเลขจุดลอย

>>> round(1.2322, 2)
1.23

จำนวนเต็มค่อนข้างยุ่งยาก พวกเขาไม่ได้ถูกจัดเก็บเป็นฐาน 10 ในหน่วยความจำสถานที่สำคัญจึงไม่ใช่เรื่องธรรมชาติ มันค่อนข้างง่ายที่จะติดตั้งใช้งานเมื่อมันเป็นสตริง

หรือสำหรับจำนวนเต็ม:

>>> def intround(n, sigfigs):
...   n = str(n)
...   return n[:sigfigs] + ('0' * (len(n)-(sigfigs)))

>>> intround(1234, 1)
'1000'
>>> intround(1234, 2)

หากคุณต้องการสร้างฟังก์ชั่นที่จัดการหมายเลขใด ๆ การตั้งค่าของฉันคือการแปลงทั้งสองเป็นสตริงและค้นหาตำแหน่งทศนิยมเพื่อตัดสินใจว่าจะทำอย่างไร:

>>> def roundall1(n, sigfigs):
...   n = str(n)
...   try:
...     sigfigs = n.index('.')
...   except ValueError:
...     pass
...   return intround(n, sigfigs)

อีกทางเลือกหนึ่งคือการตรวจสอบประเภท สิ่งนี้จะมีความยืดหยุ่นน้อยกว่ามากและอาจไม่เล่นกับตัวเลขอื่น ๆ เช่นDecimalวัตถุ:

>>> def roundall2(n, sigfigs):
...   if type(n) is int: return intround(n, sigfigs)
...   else: return round(n, sigfigs)

เพียงแค่ล้อเล่นกับสตริงจะไม่ปัดเศษตัวเลข 1999 ปัดเศษเป็นตัวเลขสำคัญ 1 อันคือ 2000 ไม่ใช่ 1,000
Peter Graham

มีการสนทนาที่ดีของปัญหานี้ที่ถูกเก็บถาวรที่ ActiveState code.activestate.com/list/python-tutor/70739
Tim McNamara

2

คำตอบที่โพสต์นั้นดีที่สุดเมื่อได้รับ แต่มีข้อ จำกัด มากมายและไม่ได้สร้างตัวเลขที่มีความหมายทางเทคนิคที่ถูกต้อง

numpy.format_float_positionalสนับสนุนพฤติกรรมที่ต้องการโดยตรง แฟรกเมนต์ต่อไปนี้ส่งคืนโฟลว์ที่xจัดรูปแบบเป็นตัวเลขที่มีนัยสำคัญ 4 ตัวพร้อมกับระงับสัญลักษณ์ทางวิทยาศาสตร์

import numpy as np
x=12345.6
np.format_float_positional(x, precision=4, unique=False, fractional=False, trim='k')
> 12340.

เอกสาร (ย้ายไปที่numpy.org/doc/stable/reference/generated/… ) ระบุว่าฟังก์ชั่นนี้ใช้อัลกอริทึม Dragon4 (ของ Steele & White 1990, dl.acm.org/doi/pdf/10.1145/93542.93559 ) print(*[''.join([np.format_float_positional(.01*a*n,precision=2,unique=False,fractional=False,trim='k',pad_right=5) for a in [.99, .999, 1.001]]) for n in [8,9,10,11,12,19,20,21]],sep='\n')มันก่อให้เกิดผลลัพธ์ที่น่ารำคาญเช่น ฉันไม่ได้ตรวจสอบตัว Dragon4
Rainald62

0

ฉันวิ่งเข้าไปในนี้เช่นกัน แต่ฉันต้องการการควบคุมประเภทการปัดเศษ ดังนั้นฉันจึงเขียนฟังก์ชั่นด่วน (ดูรหัสด้านล่าง) ที่สามารถนำค่าประเภทการปัดเศษและตัวเลขที่สำคัญที่ต้องการเข้าบัญชี

import decimal
from math import log10, floor

def myrounding(value , roundstyle='ROUND_HALF_UP',sig = 3):
    roundstyles = [ 'ROUND_05UP','ROUND_DOWN','ROUND_HALF_DOWN','ROUND_HALF_UP','ROUND_CEILING','ROUND_FLOOR','ROUND_HALF_EVEN','ROUND_UP']

    power =  -1 * floor(log10(abs(value)))
    value = '{0:f}'.format(value) #format value to string to prevent float conversion issues
    divided = Decimal(value) * (Decimal('10.0')**power) 
    roundto = Decimal('10.0')**(-sig+1)
    if roundstyle not in roundstyles:
        print('roundstyle must be in list:', roundstyles) ## Could thrown an exception here if you want.
    return_val = decimal.Decimal(divided).quantize(roundto,rounding=roundstyle)*(decimal.Decimal(10.0)**-power)
    nozero = ('{0:f}'.format(return_val)).rstrip('0').rstrip('.') # strips out trailing 0 and .
    return decimal.Decimal(nozero)


for x in list(map(float, '-1.234 1.2345 0.03 -90.25 90.34543 9123.3 111'.split())):
    print (x, 'rounded UP: ',myrounding(x,'ROUND_UP',3))
    print (x, 'rounded normal: ',myrounding(x,sig=3))

0

การใช้การจัดรูปแบบรูปแบบใหม่ของไพ ธ อน 2.6+ (เนื่องจาก% -style เลิกใช้แล้ว):

>>> "{0}".format(float("{0:.1g}".format(1216)))
'1000.0'
>>> "{0}".format(float("{0:.1g}".format(0.00356)))
'0.004'

ใน python 2.7+ คุณสามารถละทิ้ง0s


หลามแบบไหน? Python 3.6.3 | Anaconda, Inc. | (ค่าเริ่มต้นคือ 13 ต.ค. 2017, 12:02:49) มีปัญหาการปัดเศษแบบเดิม "{0}". รูปแบบ (float ("{0: .1g}". รูปแบบ (0.075))) ให้ผลเป็น '0.07', ไม่ใช่ '0.08'
Don Mclachlan

@DonMclachlan ฉันได้เพิ่มคำอธิบายว่าทำไมจึงเป็นเช่นนี้ในstackoverflow.com/a/56974893/1358308
Sam Mason

0

ฟังก์ชั่นนี้ทำรอบปกติถ้าจำนวนมากกว่า 10 ** (- decimal_positions) มิฉะนั้นจะเพิ่มทศนิยมอีกจนกว่าจะถึงจำนวนตำแหน่งทศนิยมที่มีความหมาย:

def smart_round(x, decimal_positions):
    dp = - int(math.log10(abs(x))) if x != 0.0 else int(0)
    return round(float(x), decimal_positions + dp if dp > 0 else decimal_positions)

หวังว่ามันจะช่วย


0

https://stackoverflow.com/users/1391441/gabrielต่อไปนี้แก้ไขข้อกังวลของคุณเกี่ยวกับ rnd (.075, 1) หรือไม่ Caveat: ส่งคืนค่าเป็นแบบลอย

def round_to_n(x, n):
    fmt = '{:1.' + str(n) + 'e}'    # gives 1.n figures
    p = fmt.format(x).split('e')    # get mantissa and exponent
                                    # round "extra" figure off mantissa
    p[0] = str(round(float(p[0]) * 10**(n-1)) / 10**(n-1))
    return float(p[0] + 'e' + p[1]) # convert str to float

>>> round_to_n(750, 2)
750.0
>>> round_to_n(750, 1)
800.0
>>> round_to_n(.0750, 2)
0.075
>>> round_to_n(.0750, 1)
0.08
>>> math.pi
3.141592653589793
>>> round_to_n(math.pi, 7)
3.141593

0

ส่งคืนสตริงเพื่อให้ผลลัพธ์ที่ไม่มีส่วนที่เป็นเศษส่วนและค่าขนาดเล็กซึ่งจะปรากฏในรูปแบบ E แสดงอย่างถูกต้อง:

def sigfig(x, num_sigfig):
    num_decplace = num_sigfig - int(math.floor(math.log10(abs(x)))) - 1
    return '%.*f' % (num_decplace, round(x, num_decplace))

0

ให้คำถามที่ตอบอย่างละเอียดทำไมไม่เพิ่มอีก

สิ่งนี้เหมาะกับสุนทรียศาสตร์ของฉันดีขึ้นเล็กน้อยถึงแม้ว่าสิ่งที่กล่าวมาข้างต้นจะเปรียบเทียบกันได้

import numpy as np

number=-456.789
significantFigures=4

roundingFactor=significantFigures - int(np.floor(np.log10(np.abs(number)))) - 1
rounded=np.round(number, roundingFactor)

string=rounded.astype(str)

print(string)

วิธีนี้ใช้ได้กับตัวเลขแต่ละตัวและอาร์เรย์ numpy และควรทำงานได้ดีสำหรับจำนวนลบ

มีอีกหนึ่งขั้นตอนที่เราอาจเพิ่ม - np.round () ส่งกลับเลขทศนิยมแม้ว่าการปัดเศษเป็นจำนวนเต็ม (เช่นสำหรับตัวเลขสำคัญ = 2 เราอาจคาดหวังว่าจะได้รับกลับ -460 แต่แทนที่จะได้รับ -460.0) เราสามารถเพิ่มขั้นตอนนี้เพื่อแก้ไข:

if roundingFactor<=0:
    rounded=rounded.astype(int)

น่าเสียดายที่ขั้นตอนสุดท้ายนี้ใช้ไม่ได้กับอาร์เรย์ของตัวเลข - ฉันจะปล่อยให้คุณเป็นที่รักของผู้อ่านเพื่อคิดออกหากคุณต้องการ


0

sigfigแพคเกจ / ห้องสมุดครอบคลุมนี้ หลังจากติดตั้งคุณสามารถทำสิ่งต่อไปนี้:

>>> from sigfig import round
>>> round(1234, 1)
1000
>>> round(0.12, 1)
0.1
>>> round(0.012, 1)
0.01
>>> round(0.062, 1)
0.06
>>> round(6253, 1)
6000
>>> round(1999, 1)
2000

0
import math

  def sig_dig(x, n_sig_dig):
      num_of_digits = len(str(x).replace(".", ""))
      if n_sig_dig >= num_of_digits:
          return x
      n = math.floor(math.log10(x) + 1 - n_sig_dig)
      result = round(10 ** -n * x) * 10 ** n
      return float(str(result)[: n_sig_dig + 1])


    >>> sig_dig(1234243, 3)
    >>> sig_dig(243.3576, 5)

        1230.0
        243.36
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.