การเก็บเงินในคอลัมน์ทศนิยม - ความแม่นยำและมาตราส่วนอะไร


173

ฉันใช้คอลัมน์ทศนิยมเพื่อเก็บค่าเงินไว้ในฐานข้อมูลและวันนี้ฉันสงสัยว่าความแม่นยำและขนาดที่ใช้

เนื่องจากว่าคอลัมน์ถ่านที่มีความกว้างคงที่นั้นมีประสิทธิภาพมากกว่าฉันจึงคิดเหมือนกันว่าอาจเป็นจริงสำหรับคอลัมน์ทศนิยม ใช่ไหม?

ฉันควรใช้ความแม่นยำและสเกลใด ฉันคิดแม่นยำ 24/8 overkill นั้นไม่เพียงพอหรือตกลง?


นี่คือสิ่งที่ฉันตัดสินใจที่จะทำ:

  • จัดเก็บอัตราการแปลง (เมื่อมี) ในตารางธุรกรรมเองเป็นแบบลอย
  • เก็บสกุลเงินในตารางบัญชี
  • จำนวนการทำธุรกรรมจะเป็น DECIMAL(19,4)
  • การคำนวณทั้งหมดที่ใช้อัตราการแปลงจะได้รับการจัดการโดยแอปพลิเคชันของฉันดังนั้นฉันจึงควบคุมปัญหาการปัดเศษ

ฉันไม่คิดว่าการลอยตัวของอัตราการแปลงเป็นปัญหาเนื่องจากเป็นข้อมูลอ้างอิงเป็นส่วนใหญ่และฉันจะแปลงเป็นทศนิยมต่อไป

ขอบคุณทุกท่านสำหรับการป้อนข้อมูลที่มีค่าของคุณ


2
ถามตัวเองด้วยคำถาม: จำเป็นหรือไม่ที่จะต้องจัดเก็บข้อมูลในรูปแบบทศนิยม? ฉันไม่สามารถจัดเก็บข้อมูลเป็น Cents / Pennies -> จำนวนเต็มได้หรือไม่
Terence

5
DECIMAL(19, 4) เป็นตัวเลือกยอดนิยมตรวจสอบนี้ยังตรวจสอบที่นี่โลกรูปแบบสกุลเงินที่จะตัดสินใจว่าหลายสถานที่ที่จะใช้ทศนิยมหวังช่วย
shaijut

คำตอบ:


181

หากคุณกำลังมองหาหนึ่งขนาดที่เหมาะกับทุกคนฉันขอแนะนำให้DECIMAL(19, 4)เป็นตัวเลือกยอดนิยม (Google เป็นผู้ดำเนินการอย่างรวดเร็ว) ฉันคิดว่าสิ่งนี้มาจากประเภทข้อมูล VBA / การเข้าถึง / Jet สกุลเงินเก่าซึ่งเป็นทศนิยมชนิดคงที่จุดแรกในภาษาDecimalมาในสไตล์ 'รุ่น 1.0' เท่านั้น (เช่นไม่ได้ใช้งานอย่างสมบูรณ์) ใน VB6 / VBA6 / Jet 4.0

กฎง่ายๆสำหรับการจัดเก็บค่าทศนิยมคงที่คือการจัดเก็บอย่างน้อยหนึ่งตำแหน่งทศนิยมมากกว่าที่คุณต้องการจริงเพื่อให้การปัดเศษ หนึ่งในเหตุผลในการทำแผนที่Currencyประเภทเก่าในส่วนหน้าเพื่อDECIMAL(19, 4)พิมพ์ในส่วนหลังคือCurrencyจัดแสดงการปัดเศษของธนาคารตามธรรมชาติในขณะที่DECIMAL(p, s)โค้งมนโดยการตัด

ทศนิยมพิเศษในที่เก็บข้อมูลสำหรับ DECIMALอนุญาตให้ใช้อัลกอริทึมการปัดเศษแบบกำหนดเองแทนที่จะใช้ค่าเริ่มต้นของผู้ขาย (และการปัดเศษของนายธนาคารนั้นน่าตกใจพูดน้อยที่สุดสำหรับนักออกแบบที่คาดหวังว่าค่าทั้งหมดที่ลงท้ายด้วย. 5 จะห่างจากศูนย์) .

ใช่DECIMAL(24, 8)เสียงเหมือน overkill สำหรับฉัน สกุลเงินส่วนใหญ่อ้างถึงทศนิยมสี่หรือห้าตำแหน่ง ฉันรู้ว่าสถานการณ์ที่ระดับทศนิยม 8 (หรือมากกว่า) คือต้อง แต่นี่เป็นที่ที่ 'ปกติ' จำนวนการเงิน (พูดสี่ตำแหน่งทศนิยม) ได้รับ rata'd โปรหมายความแม่นยำทศนิยมควรจะลดลงตามไป (ยังพิจารณา ประเภทจุดลอยตัวในกรณีดังกล่าว) และไม่มีใครมีเงินจำนวนมากในปัจจุบันที่ต้องการความแม่นยำทศนิยม 24 :)

อย่างไรก็ตามแทนที่จะใช้วิธีการเดียวที่เหมาะกับทุกขนาดงานวิจัยบางอย่างอาจเป็นไปตามลำดับ ถามผู้เชี่ยวชาญด้านการออกแบบหรือโดเมนของคุณเกี่ยวกับกฎการบัญชีที่อาจมีผลบังคับใช้: GAAP, EU, ฯลฯ ฉันจำการถ่ายโอนภายในรัฐของสหภาพยุโรปที่มีกฎชัดเจนสำหรับการปัดเศษทศนิยมทศนิยมห้าตำแหน่งโดยใช้DECIMAL(p, 6)สำหรับการจัดเก็บ โดยทั่วไปนักบัญชีดูเหมือนจะชอบทศนิยมสี่ตำแหน่ง


PS หลีกเลี่ยงMONEYชนิดข้อมูลของ SQL Server เนื่องจากมีปัญหาร้ายแรงเกี่ยวกับความถูกต้องในการปัดเศษในหมู่ข้อควรพิจารณาอื่น ๆ เช่นความสะดวกในการพกพาเป็นต้นดูบล็อกของ Aaron Bertrandบล็อกของอาโรนเบอร์ทรานด์ของ


Microsoft และนักออกแบบภาษาเลือกการปัดเศษของนายธนาคารเนื่องจากนักออกแบบฮาร์ดแวร์เลือก [อ้างถึง] เป็นที่ประดิษฐานอยู่ในมาตรฐานของสถาบันวิศวกรไฟฟ้าและอิเล็กทรอนิกส์ (IEEE) และนักออกแบบฮาร์ดแวร์ก็เลือกเพราะนักคณิตศาสตร์ชอบมัน ดูWikipedia ; เพื่อถอดความ: ความน่าจะเป็นและทฤษฎีข้อผิดพลาดในปี 1906 เรียกสิ่งนี้ว่า 'กฎของคอมพิวเตอร์' ("คอมพิวเตอร์" หมายถึงมนุษย์ที่ทำการคำนวณ)


1
ดูคำตอบนี้และหน้านี้สำหรับข้อมูลเพิ่มเติมเกี่ยวกับสาเหตุที่นักออกแบบภาษาเลือกการปัดเศษของ Banker
Nick Chammas

1
onedaywhen: การปัดเศษของธนาคารไม่ได้เกิดจาก Microsoft @NickChammas: ไม่เป็นสิ่งประดิษฐ์ของนักออกแบบภาษา โดยทั่วไปแล้ว Microsoft และนักออกแบบภาษาเลือกใช้เพราะผู้ออกแบบฮาร์ดแวร์เลือก เป็นที่ประดิษฐานอยู่ในมาตรฐานของสถาบันวิศวกรไฟฟ้าและอิเล็กทรอนิกส์ (IEEE) และนักออกแบบฮาร์ดแวร์ก็เลือกเพราะนักคณิตศาสตร์ชอบมัน ดูen.wikipedia.org/wiki/Rounding#History ; เพื่อถอดความ: ความน่าจะเป็นและทฤษฎีข้อผิดพลาดในปี 1906 เรียกสิ่งนี้ว่า 'กฎของคอมพิวเตอร์' ("คอมพิวเตอร์" หมายถึงมนุษย์ที่ทำการคำนวณ)
phoog

9
ดังนั้นฉันควรใช้อะไรกับ Bitcoin
ชุดเครื่องมือ

1
@oneday เมื่อไหร่ทำไมถึงได้DECIMAL(19, 4)รับความนิยมมากกว่าDECIMAL(19, 2)? สกุลเงินโลกส่วนใหญ่เป็นเพียงทศนิยมสองตำแหน่ง
cokedude

3
@ zypA13510: ใช่การยืนยันของฉันเมื่อสิบปีที่แล้ว! แต่นี้เป็นของฉันที่สามมากที่สุดโหวตให้คำตอบและบัญชีสำหรับอันยุติธรรมของการเปลี่ยนแปลงเป็นเรื่องที่เกี่ยวกับตัวแทนดังนั้นฉันดังนั้นฉัน quids ใน :)
onedaywhen

105

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

ไม่ใช้จุดลอยตัวเป็นจำนวนเงิน

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

เมื่อคุณต้องการคำนวณหรือแปลง:

  1. แปลงค่าเป็นทศนิยม
  2. คำนวณค่าใหม่
  3. ปัดเศษตัวเลขแล้วแปลงกลับเป็นจำนวนเต็ม

เมื่อแปลงหมายเลขทศนิยมกลับไปเป็นจำนวนเต็มในขั้นตอนที่ 3 อย่าเพิ่งโยนมัน - ใช้ฟังก์ชันคณิตศาสตร์เพื่อปัดเศษก่อน ซึ่งโดยปกติจะมีroundแต่ในกรณีพิเศษมันอาจจะเป็นหรือfloor ceilรู้ความแตกต่างและเลือกอย่างระมัดระวัง

เก็บประเภทของตัวเลขไว้ข้างๆค่า

สิ่งนี้อาจไม่สำคัญสำหรับคุณหากคุณจัดการเพียงหนึ่งสกุลเงิน แต่เป็นสิ่งสำคัญสำหรับเราในการจัดการหลายสกุลเงิน เราใช้รหัส 3 ตัวสำหรับสกุลเงินเช่น USD, GBP, JPY, EUR และอื่น ๆ

มันอาจจะมีประโยชน์ในการจัดเก็บ:

  • ไม่ว่าจะเป็นจำนวนก่อนหรือหลังภาษี (และอัตราภาษีคืออะไร)
  • ไม่ว่าจะเป็นจำนวนที่เป็นผลมาจากการแปลง (และสิ่งที่มันถูกแปลงจาก)

รู้ขอบเขตความแม่นยำของตัวเลขที่คุณติดต่อด้วย

สำหรับค่าจริงคุณต้องการแม่นยำเป็นหน่วยที่เล็กที่สุดของสกุลเงิน ซึ่งหมายความว่าคุณไม่มีค่าน้อยกว่าหนึ่งเซ็นต์เงินเยนเยนและอื่น ๆ อย่าเก็บค่าที่มีความแม่นยำสูงกว่านั้นโดยไม่มีเหตุผล

ภายในคุณอาจเลือกที่จะจัดการกับค่าที่น้อยกว่าซึ่งในกรณีนี้เป็นค่าสกุลเงินชนิดอื่น ตรวจสอบให้แน่ใจว่ารหัสของคุณรู้ว่าเป็นแบบใดและไม่ทำให้สับสน หลีกเลี่ยงการใช้ค่าทศนิยมแม้ที่นี่


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

class Currency {
   String code;       //  eg "USD"
   int value;         //  eg 2500
   boolean converted;
}

class Price {
   Currency grossValue;
   Currency netValue;
   Tax taxRate;
}

ในฐานข้อมูลค่าจะถูกเก็บเป็นสตริงในรูปแบบต่อไปนี้:

USD:2500

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


และในกรณีที่ฉันไม่ได้บอกชัดเจนก่อนหน้านี้อย่าใช้ทุ่น!


1
ไม่เคยพูดไม่เคย: บางครั้งจำนวนเงินเป็นแบบมืออาชีพและจำเป็นต้องเพิ่มอีกครั้งในภายหลัง ตัวอย่าง: การหารเงินปันผลทั้งหมด (ค่อนข้างเล็ก) แบ่งตามจำนวนหุ้นที่ออก (ค่อนข้างเล็ก) เพื่อให้สุทธิต่อหุ้น บางครั้งลอยรอบดีกว่า :)
onedaywhen

23
ฉันไม่เคยยืน ข้อมูลจำเพาะจุดลอยตัวมีความไม่ถูกต้องที่จะเพิ่มการคำนวณเพิ่มเติมที่คุณทำ หากคุณต้องการเก็บค่าน้อยกว่าหนึ่งเซ็นต์หรือเพนนีให้กำหนดระดับความแม่นยำที่คุณต้องการและติดกับมัน อย่าใช้ทุ่น อย่างจริงจัง. มันเป็นความคิดที่ไม่ดี
Marcus Downing

4
คำตอบนี้สอดคล้องกับแนวทางปฏิบัติที่ดีที่สุดของ javascript ที่ดักลาสคร็อคฟอร์ดใน "Crockford ในชุดจาวาสคริปต์" ซึ่งเขาแนะนำให้ทำการคำนวณสกุลเงินทั้งหมดในเพนนิตี้เพื่อหลีกเลี่ยงข้อผิดพลาดของเครื่องจักรในการปัดเศษ ดังนั้นหากคุณกำลังทำงานกับสกุลเงินในจาวาสคริปต์มันสมเหตุสมผลอย่างมากในการเก็บค่าด้วยวิธีนี้
ลดกระดาษ

1
@oneday เมื่อกรณีสุทธิต่อหุ้นคุณสามารถรวมจำนวนเงิน pro-rata'd และเปรียบเทียบกับยอดรวมดั้งเดิมและกำหนดกลยุทธ์สำหรับการจัดการกับส่วนที่เหลือ (เมื่อใช้ประเภททศนิยม / จำนวนเต็ม)
ชีฟ

2
สำหรับ Mysql ฉันแนะนำให้เก็บจำนวนเต็ม (2500) เสมือนbigintว่าคุณตั้งใจจะเรียงตามจำนวน และไม่ต้องเสียเวลากับ PHP 32 บิตเมื่อต้องจัดการกับจำนวนเต็มขนาดใหญ่อัพเกรดเป็น 64 บิตหรือ Node.JS;)
Ricky Boyce

4

เมื่อจัดการเงินใน MySQL ให้ใช้ DECIMAL (13,2) ถ้าคุณทราบความแม่นยำของค่าเงินหรือใช้สองเท่าถ้าคุณต้องการค่าประมาณที่ดีพอ ดังนั้นหากแอปพลิเคชันของคุณต้องการจัดการค่าเงินสูงถึงหนึ่งล้านล้านดอลลาร์ (หรือยูโรหรือปอนด์) สิ่งนี้ควรใช้งานได้:

DECIMAL(13, 2)

หรือหากคุณต้องการปฏิบัติตามGAAP ให้ใช้:

DECIMAL(13, 4)

3
คุณสามารถลิงก์ไปยังส่วนเฉพาะของหลักเกณฑ์ GAAP แทนเนื้อหาของเอกสารหน้า 2500 ได้หรือไม่ ขอบคุณ
ReactingToAngularVues

@ReactingToAngularVues ดูเหมือนว่าหน้าเว็บมีการเปลี่ยนแปลง ขออภัย
pollux1er

2

ทศนิยม 4 ตำแหน่งจะให้ความแม่นยำในการจัดเก็บหน่วยย่อยของสกุลเงินที่เล็กที่สุดในโลก คุณสามารถนำมันลงไปอีกได้หากคุณต้องการความแม่นยำระดับนาโนเมตร (nanopayment ?!)

ฉันก็ชอบDECIMALประเภทเงินที่เฉพาะเจาะจงของ DBMS เช่นกันคุณปลอดภัยกว่าการรักษาตรรกะประเภทนั้นไว้ในแอปพลิเคชัน IMO อีกวิธีในแนวเดียวกันคือใช้จำนวนเต็ม [แบบยาว] โดยจัดรูปแบบเป็น¤unit.subunitเพื่อให้มนุษย์สามารถอ่านได้ (¤ = สัญลักษณ์สกุลเงิน) ที่ระดับแอปพลิเคชัน


1

ประเภทข้อมูลเงินบน SQL Server มีตัวเลขสี่หลักหลังจุดทศนิยม

จาก SQL Server 2000 Books Online:

ข้อมูลการเงินแสดงถึงจำนวนเงินที่เป็นบวกหรือลบ ในMicrosoft® SQL Server ™ 2000 ข้อมูลการเงินจะถูกจัดเก็บโดยใช้ชนิดข้อมูลเงินและเงินขนาดเล็ก ข้อมูลการเงินสามารถจัดเก็บได้อย่างแม่นยำในตำแหน่งทศนิยมสี่ตำแหน่ง ใช้ชนิดข้อมูลการเงินเพื่อเก็บค่าในช่วงตั้งแต่ -922,337,203,685,477.5808 ถึง +922,337,203,685,477.5807 (ต้องการ 8 ไบต์เพื่อเก็บค่า) ใช้ชนิดข้อมูลขนาดเล็กเงินเพื่อจัดเก็บค่าในช่วงตั้งแต่ -214,748.3648 ถึง 214,748.3647 (ต้องใช้ 4 ไบต์เพื่อเก็บค่า) หากต้องการตำแหน่งทศนิยมที่มากกว่าให้ใช้ประเภทข้อมูลทศนิยมแทน


1

บางครั้งคุณจะต้องไปน้อยกว่าหนึ่งร้อยและมีสกุลเงินต่างประเทศที่ใช้ปีศาจขนาดใหญ่มาก ตัวอย่างเช่นคุณอาจเรียกเก็บเงินจากลูกค้า 0.088 เซนต์ต่อธุรกรรม ในฐานข้อมูล Oracle ของฉันคอลัมน์ถูกกำหนดเป็น NUMBER (20,4)


1

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


นั่นคือสิ่งที่ฉันคิด แต่ในแง่ของอัตราสกุลเงิน (เช่นการแปลงดอลลาร์ซิมบับเวเป็น USD) ฉันจะทำการทดลองบางอย่างกับฐานข้อมูลที่ฉันใช้ (psql, sqlite) เพื่อดูว่าพวกเขาจัดการกับการปัดเศษทศนิยมอย่างไร
อีวาน

นอกจากนี้อย่าลอยมีปัญหาความแม่นยำในบาง dbms / ภาษา?
อีวาน

2
ลอยมีปัญหาความแม่นยำในทุกภาษา
Marcus Downing

คำแนะนำที่พบบ่อยที่สุดในวันนี้คือการใช้ความแม่นยำตามอำเภอใจ (คิด BigDecimal ) แต่เป็นเวลานานแล้วที่มีความแม่นยำสองเท่า (คิดdoubleแทนfloat) นอกจากนี้ความแม่นยำตามอำเภอใจมีบทลงโทษด้านประสิทธิภาพอย่างมากในบางกรณี การทดสอบเป็นแนวทางที่ถูกต้องอย่างแน่นอน
แฮงค์เกย์

0

หากคุณใช้ IBM Informix Dynamic Server คุณจะมีประเภท MONEY ซึ่งเป็นตัวแปรย่อยในประเภท DECIMAL หรือ NUMERIC เป็นประเภทจุดคงที่เสมอ (ในขณะที่ DECIMAL สามารถเป็นชนิดจุดลอยตัว) คุณสามารถระบุสเกลได้ตั้งแต่ 1 ถึง 32 และความแม่นยำตั้งแต่ 0 ถึง 32 (ค่าเริ่มต้นเป็นสเกลที่ 16 และความแม่นยำ 2) ดังนั้นขึ้นอยู่กับสิ่งที่คุณต้องการจัดเก็บคุณอาจใช้ DECIMAL (16,2) - ยังใหญ่พอที่จะระงับการขาดดุลของรัฐบาลกลางสหรัฐอเมริกาให้ใกล้ที่สุด - หรือคุณอาจใช้ช่วงที่เล็กลงหรือทศนิยมมากกว่า


0

ฉันคิดว่าส่วนใหญ่ของคุณหรือความต้องการของลูกค้าของคุณควรกำหนดความแม่นยำและขนาดที่จะใช้ ตัวอย่างเช่นสำหรับเว็บไซต์อีคอมเมิร์ซที่ฉันทำงานเกี่ยวกับข้อตกลงนั้นด้วยเงินในสกุลเงิน GBP เท่านั้นฉันจำเป็นต้องเก็บไว้ที่ทศนิยม (6, 2)


0

คำตอบที่ล่าช้าที่นี่ แต่ฉันเคยใช้

DECIMAL(13,2)

ที่ฉันคิดถูกควรจะอนุญาตให้มากถึง 99,999,999,999.99

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