ฉันควรใช้ประเภทข้อมูลใดสำหรับสกุลเงินในเกม


96

ในเกมจำลองธุรกิจอย่างง่าย (สร้างขึ้นใน Java + Slick2D) จำนวนเงินของผู้เล่นควรถูกจัดเก็บเป็นfloatหรือintหรืออย่างอื่นหรือไม่?

ในกรณีใช้งานของฉันธุรกรรมส่วนใหญ่จะใช้เซ็นต์ ($ 0.50, $ 1.20, ฯลฯ ) และการคำนวณอัตราดอกเบี้ยแบบง่ายจะมีส่วนเกี่ยวข้อง

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


2
คำถามนี้ถูกพูดถึงใน StackOverflow ดูเหมือน BigDecimal น่าจะเป็นวิธีที่จะไป stackoverflow.com/questions/285680/…
Ade Miller

2
Java ไม่มีCurrencyประเภทเหมือน Delphi หรือไม่ซึ่งใช้คณิตศาสตร์จุดคงที่ที่ปรับขนาดเพื่อให้คุณได้เลขทศนิยมโดยไม่มีปัญหาความแม่นยำที่เป็นทศนิยม
Mason Wheeler

1
มันเป็นเกม การบัญชีจะไม่กระทบกระเทือนเงินเพนนีและทำให้เป็นเรื่องปรกติกับการใช้ทศนิยมสำหรับสกุลเงินไม่สำคัญ
Loren Pechtel

@MasonWheeler: Java มีBigDecimalปัญหาประเภทนี้
Martin Schröder

คำตอบ:


92

คุณสามารถใช้intและพิจารณาทุกอย่างเป็นเซ็นต์ $ 1.20 เพียง 120 เซ็นต์ ที่หน้าจอคุณใส่ทศนิยมในตำแหน่งที่มันเป็น

การคำนวณดอกเบี้ยจะถูกปัดเศษหรือปัดเศษขึ้น ดังนั้น

newAmt = round( 120 cents * 1.04 ) = round( 124.8 ) = 125 cents

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


3
หากคุณใช้roundไม่มีเหตุผลจริงที่จะทิ้งลงไปintมี?
sam hocevar

19
+1 ตัวเลขจุดลอยตัวจะทำให้การเปรียบเทียบความเท่าเทียมกันยากขึ้นพวกเขาจะจัดรูปแบบอย่างถูกต้องได้ยากขึ้นและทำให้เกิดข้อผิดพลาดในการปัดเศษเมื่อเวลาผ่านไป ดีกว่าที่จะทำทุกอย่างด้วยตัวเองเพื่อที่คุณจะไม่ได้รับการร้องเรียนในภายหลัง มันไม่ได้ทำงานมากจริงๆ
Dobes Vandermeer

+1 ฟังดูเป็นวิธีที่ดีสำหรับเกมของฉัน ฉันจะจัดการกับการปัดเศษ ints ตัวเองและอยู่ห่างจากปัญหาลอยทั้งหมด
Lucas Tulio

เป็นเพียงข้อจำกัดความรับผิดชอบ: ฉันเปลี่ยนคำตอบที่ถูกต้องสำหรับคำตอบนี้เพราะมันเป็นสิ่งที่ฉันใช้ มันง่ายกว่ามากและในบริบทของเกมอย่างง่ายทศนิยมที่ไม่ได้นับไม่สำคัญเลย
Lucas Tulio

3
อย่างไรก็ตามใช้คะแนนพื้นฐานไม่ใช่เซ็นต์ (ที่ 100 คะแนนพื้นฐาน = 1 เซ็นต์) ด้วยปัจจัย 10,000 แทนที่จะเป็น 100 ซึ่ง GAAP จำเป็นสำหรับแอปพลิเคชันทางการเงินการธนาคารหรือการบัญชีทั้งหมด
Pieter Geerkens

66

ตกลงฉันจะกระโดดเข้า

คำแนะนำของฉัน: มันเป็นเกม doubleใช้ง่ายและการใช้งาน

นี่คือเหตุผลของฉัน:

  • floatมีปัญหาความแม่นยำที่ปรากฏเมื่อเพิ่มหน่วยเป็นล้านดังนั้นแม้ว่ามันอาจไม่เป็นพิษเป็นภัย แต่ฉันจะหลีกเลี่ยงประเภทนั้น doubleเริ่มต้นรับปัญหารอบ quintillons เท่านั้น (พันล้านพันล้าน)
  • เนื่องจากคุณกำลังจะมีอัตราดอกเบี้ยที่คุณจะต้องมีความแม่นยำอนันต์ทฤษฎีอยู่แล้ว: มีอัตราดอกเบี้ย 4%, $ 100 จะกลายเป็น $ 104 แล้ว $ 108.16 แล้ว $ 112.4864 ฯลฯ ซึ่งทำให้intและlongไร้ประโยชน์เพราะคุณไม่ทราบว่าจะหยุดกับ ทศนิยม
  • BigDecimalจะทำให้คุณมีความแม่นยำโดยพลการ แต่จะช้าลงอย่างเจ็บปวดเว้นแต่คุณจะยึดความแม่นยำในบางครั้ง กฎการปัดเศษคืออะไร คุณจะเลือกที่จะหยุดได้อย่างไร มันมีค่าบิตความแม่นยำมากกว่าdoubleหรือไม่ ฉันไม่เชื่อ

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

ตัวอย่างการปฏิบัติ

ฉันเห็นความคิดเห็นเล็กน้อยที่อ้างถึงสิ่งที่เกี่ยวกับการปัดเศษหรือความแม่นยำที่ฉันไม่เห็นด้วย นี่คือตัวอย่างเพิ่มเติมบางส่วนเพื่อแสดงให้เห็นถึงสิ่งที่ฉันหมายถึง

การจัดเก็บ : หากหน่วยฐานของคุณเป็นเซ็นต์คุณอาจต้องการปัดเศษเป็นเซ็นต์ที่ใกล้ที่สุดเมื่อจัดเก็บค่า:

void SetValue(double v) { m_value = round(v * 100.0) / 100.0; }

คุณจะไม่มีปัญหาในการปัดเศษเมื่อใช้วิธีนี้ซึ่งคุณจะไม่ได้ใช้กับประเภทจำนวนเต็ม

การดึงข้อมูล : การคำนวณทั้งหมดสามารถทำได้โดยตรงกับค่าสองเท่าโดยไม่มีการแปลง:

double value = data.GetValue();
value = value / 3.0 * 12.0;
[...]
data.SetValue(value);

โปรดทราบว่ารหัสข้างต้นไม่สามารถใช้งานได้หากคุณแทนที่doubleด้วยint64_t: จะมีการแปลงโดยนัยเป็นdoubleแล้วจึงตัดทอนเป็นint64_tโดยให้สูญเสีย data.data.GetValue () ที่เป็นไปได้

การเปรียบเทียบ : การเปรียบเทียบเป็นสิ่งเดียวที่จะทำให้ถูกต้องกับประเภทจุดลอย ฉันขอแนะนำให้ใช้วิธีการเปรียบเทียบเช่นวิธีนี้:

/* Are values equal to a tenth of a cent? */
bool AreCurrencyValuesEqual(double a, double b) { return abs(a - b) < 0.001; }

การปัดเศษความยุติธรรม

สมมติว่าคุณมี $ 9.99 ในบัญชีที่มีความสนใจ 4% ผู้เล่นควรมีรายได้เท่าไรด้วยการปัดเศษจำนวนเต็มคุณจะได้ $ 0.03 ด้วยการปัดเศษทศนิยมคุณจะได้รับ $ 0.04 ฉันเชื่อว่าหลังมีความยุติธรรมมากกว่า


4
+1 คำอธิบายที่ฉันต้องการเพิ่มเพียงอย่างเดียวคือแอปพลิเคชันทางการเงินสอดคล้องกับข้อกำหนดเฉพาะที่กำหนดไว้ล่วงหน้าดังนั้นอัลกอริทึมการปัดเศษ หากเกมดังกล่าวเป็นเกมคาสิโนมันจะสอดคล้องกับมาตรฐานที่คล้ายกันและไม่เป็นตัวเลือก
Ivaylo Slavov

2
คุณยังต้องคิดถึงกฎการปัดเศษเพื่อจัดรูปแบบ UI เนื่องจากมีผู้เล่นส่วนหนึ่งที่พยายามรักษาเกมเป็นสเปรดชีตอยู่เสมอหากคุณไม่ต้องการจัดการกับคนที่กระโดดขึ้นและลงกรีดร้องเมื่อพวกเขาพบว่าแบ็กเอนด์ของคุณไม่ได้ใช้ความแม่นยำเหมือนกับ UI ของคุณ ผลลัพธ์ที่ 'ผิด' จะถูกแสดงตัวเลือกที่ดีที่สุดของคุณเพื่อให้พวกเขาเงียบคือการใช้การเป็นตัวแทนภายในและภายนอกเดียวกันซึ่งหมายถึงการใช้รูปแบบจุดคงที่ นอกจากประสิทธิภาพจะกลายเป็นปัญหา BigDecimal เป็นตัวเลือกที่ดีที่สุดสำหรับการทำเช่นนี้
Dan Neely

10
ฉันไม่เห็นด้วยกับคำตอบนี้ ปัญหาการปัดเศษปรากฏขึ้นและหากเกิดปัญหาขึ้นและหากคุณไม่ได้ใช้การปัดเศษเพิ่มเติม $ 1.00 อาจกลายเป็น $ 0.99 ทันที แต่ที่สำคัญที่สุดทศนิยมความแม่นยำตามอำเภอใจนั้นช้า (เกือบ) ไม่เกี่ยวข้องอย่างสมบูรณ์ เราเกือบจะในปี 2013 และหากคุณไม่ได้ทำสิ่งที่มีขนาดใหญ่ทริกเกอร์หรือลอการิทึมมากกว่าตัวเลขหลายล้านที่มีตัวเลขหลายพันหลักคุณจะไม่สังเกตเห็นถึงประสิทธิภาพ อย่างไรก็ตามความเรียบง่ายดีที่สุดเสมอดังนั้นคำแนะนำของฉันคือเก็บตัวเลขทั้งหมดเป็นเซนต์ วิธีการที่พวกเขากำลังทั้งหมดint_64ts
ชุดนอนแพนด้า

8
@Sam ครั้งล่าสุดที่ฉันตรวจสอบบัญชีธนาคารของฉันยอดคงเหลือของฉันไม่ได้สิ้นสุดใน .0000000000000001 ระบบสกุลเงินจริงต้องทำงานกับหน่วยที่ไม่สามารถหารได้และระบบจะแสดงจำนวนเต็มอย่างถูกต้อง หากคุณต้องแบ่งสกุลเงินอย่างถูกต้อง (ซึ่งไม่ธรรมดาในโลกการเงินจริง) คุณต้องทำเพื่อไม่ให้เงินสูญหาย ยกตัวอย่างเช่นหรือ10/3 = 3+3+4 10/3 = 3+3+3 (+1)สำหรับการดำเนินการอื่น ๆ ทั้งหมดจำนวนเต็มทำงานได้อย่างสมบูรณ์เหมือนจุดลอยซึ่งสามารถรับปัญหาการปัดเศษในการดำเนินการใด ๆ
ชุดนอนแพนด้า

2
@ SamHocevar 999 * 4/100 ในกรณีที่ไม่ได้กำหนดไว้โดยกฎระเบียบถือว่าการปัดเศษทั้งหมดจะกระทำในธนาคาร
Dan Neely

21

ประเภทจุดลอยตัวใน Java ( float, double) ไม่ได้เป็นตัวแทนที่ดีสำหรับสกุลเงินเนื่องจากเหตุผลหลักประการหนึ่ง - มีข้อผิดพลาดของเครื่องในการปัดเศษ แม้มีการคำนวณง่ายส่งกลับจำนวนทั้งหมดที่ชอบ - 12.0/2(6.0) ที่จุดลอยอาจจะผิดพลาดรอบ (เนื่องจากสรรพสินค้าแทนเฉพาะประเภทนี้ในหน่วยความจำ) เป็น6.0000000000001หรือ5.999999999999998หรือคล้ายกัน นี่คือผลลัพธ์ของการปัดเศษเครื่องเฉพาะที่เกิดขึ้นในตัวประมวลผลและจะไม่ซ้ำกันกับคอมพิวเตอร์ที่คำนวณ โดยปกติแล้วจะไม่ค่อยมีปัญหาในการทำงานกับค่าเหล่านี้เนื่องจากข้อผิดพลาดค่อนข้างประมาท แต่เป็นความเจ็บปวดที่แสดงให้ผู้ใช้เห็น

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

หากคุณต้องการผลผลิตที่สูงคุณควรยึดติดกับประเภทที่เรียบง่าย ในกรณีที่คุณทำงานกับข้อมูลทางการเงินที่สำคัญและแต่ละร้อยเป็นสิ่งสำคัญ (เช่นแอปพลิเค Forex หรือบางเกมคาสิโน) แล้วผมแนะนำให้คุณใช้หรือLong จะช่วยให้คุณจัดการกับปริมาณมากและความแม่นยำที่ดี เพียงสมมติว่าคุณต้องการสมมติว่าตัวเลข 4 หลักหลังจุดทศนิยมทุกอย่างที่คุณต้องการก็คือการคูณจำนวนด้วย 10,000 มีประสบการณ์ในการพัฒนาเกมคาสิโนออนไลน์ฉันเคยเห็นมักจะใช้แทนเงินในเซ็นต์ . ในแอปพลิเคชั่น Forex ความแม่นยำมีความสำคัญมากขึ้นดังนั้นคุณจะต้องมีตัวคูณที่มากขึ้น - ทั้งจำนวนนั้นไม่มีปัญหาการปัดเศษของเครื่องlongLongLong

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


4
+1 รายละเอียด: "สมมติว่าคุณต้องการสมมติว่า 4 หลักหลังจุดทศนิยมจุดทศนิยมทั้งหมดที่คุณต้องการคือการคูณจำนวน 1,000" -> สิ่งนี้เรียกว่าจุดคงที่
Laurent Couvidou

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

2
@Ivaylo Slavov ฉันเห็นด้วยกับคุณ ในคำตอบของฉันฉันเพิ่งทำความคิดเห็นของฉัน ฉันชอบที่จะพูดคุยและมีความเต็มใจที่จะทำสิ่งที่ถูกต้อง ขอบคุณสำหรับความคิดเห็นของคุณ :)
Md Mahbubur Rahman

1
@ SamHocevar: การใช้เหตุผลนั้นไม่สามารถใช้ได้ในทุกกรณี ดู: ideone.com/nI2ZOK
Samaursa

1
@ SamHocevar: นั่นไม่รับประกันเช่นกัน ตัวเลขที่อยู่ในช่วงที่ยังคงเป็นจำนวนเต็มเริ่มสะสมข้อผิดพลาดในส่วนแรก: ideone.com/AKbR7i
Samaursa

17

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

แต่สำหรับเกมขนาดใหญ่ (เช่นเกมโซเชียล) และที่ความเร็วกระบวนการหน่วยความจำไม่ จำกัดBigDecimalนั้นดีกว่า เพราะที่นี่

  • int หรือ long สำหรับการคำนวณทางการเงิน
  • ลอยและคู่ไม่สามารถแสดงฐานจริงจำนวน 10 ได้อย่างถูกต้อง

แหล่งข้อมูล:

จากhttps://stackoverflow.com/questions/3730019/why-not-use-double-or-float-to-represent-currency

เนื่องจากการลอยและการทวีคูณไม่สามารถแสดงตัวเลขจริงจำนวนฐาน 10 ได้อย่างแม่นยำ

นี่คือการทำงานของเลขทศนิยม IEEE-754: มันอุทิศบิตสำหรับสัญญาณสองสามบิตเพื่อเก็บเลขชี้กำลังและส่วนที่เหลือสำหรับเศษส่วนจริง สิ่งนี้นำไปสู่การแสดงตัวเลขในรูปแบบที่คล้ายกับ 1.45 * 10 ^ 4; ยกเว้นว่าแทนที่จะเป็น 10 ฐานมันเป็นสอง

ในความเป็นจริงแล้วเลขทศนิยมจริงทั้งหมดสามารถดูได้ว่าเป็นเศษส่วนที่แน่นอนของกำลังสิบ เช่น 10.45 คือ 1045/10 ^ 2 และในขณะที่เศษส่วนบางส่วนไม่สามารถแสดงได้อย่างแม่นยำเหมือนกับเศษส่วนของกำลังสิบ (1/3 มาถึงใจ) บางส่วนก็ไม่สามารถแทนได้อย่างแม่นยำเหมือนเศษส่วนของกำลังสองเช่นกัน ตัวอย่างง่ายๆคุณไม่สามารถเก็บ 0.1 ไว้ในตัวแปรเลขทศนิยมได้ คุณจะได้ค่าที่ใกล้เคียงที่สุดซึ่งเป็นเหมือน 0.0999999999999999996 และซอฟต์แวร์จะปัดเศษเป็น 0.1 เมื่อแสดงผล

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

จาก Bloch, J. , Java ที่มีประสิทธิภาพ, 2nd ed, รายการ 48:

The float and double types are particularly ill-suited for 

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

For example, suppose you have $1.03 and you spend 42c. How much money do you have left?

System.out.println(1.03 - .42);

prints out 0.6100000000000001.

The right way to solve this problem is to use BigDecimal, 

int หรือ long สำหรับการคำนวณทางการเงิน

ยังได้ดู


ฉันไม่เห็นด้วยกับส่วนที่ยาวเหมาะสำหรับการคำนวณขนาดเล็ก จากประสบการณ์ในชีวิตจริงของฉันมันพิสูจน์แล้วว่าเพียงพอที่จะรองรับความแม่นยำและเงินจำนวนมาก ใช่อาจเป็นเรื่องยุ่งยากเล็กน้อยในการใช้ แต่มันเต้น BigDecimal ในสองจุดสำคัญ - 1) มันเร็วกว่า (BigDecimal ใช้การปัดเศษซอฟต์แวร์ซึ่งช้ากว่าที่การปัดเศษ CPU ในตัวที่ใช้ในการลอย / สองเท่า) และ 2) BigDecimal ยากที่จะคงอยู่ในฐานข้อมูลโดยไม่สูญเสียความแม่นยำ - ฐานข้อมูลทั้งหมดไม่สนับสนุนประเภท 'กำหนดเอง' ดังกล่าว ยาวเป็นที่รู้จักกันดีและได้รับการสนับสนุนอย่างกว้างขวางในฐานข้อมูลที่สำคัญทั้งหมด
Ivaylo Slavov

@Ivaylo Slavov ขออภัยในความผิดพลาดของฉัน ฉันได้อัพเดตคำตอบแล้ว
Md Mahbubur Rahman

ไม่มีความจำเป็นที่จะให้วิธีการที่แตกต่างกันสำหรับปัญหาเดียวกัน :) ขอโทษ
โอโบ Slavov

2
@Ivaylo Slavov ฉันเห็นด้วยกับคุณ ในคำตอบของฉันฉันเพิ่งทำความคิดเห็นของฉัน ฉันชอบที่จะพูดคุยและมีความเต็มใจที่จะทำสิ่งที่ถูกต้อง ขอบคุณสำหรับความคิดเห็นของคุณ :)
Md Mahbubur Rahman

เป็นวัตถุประสงค์ของเว็บไซต์นี้เพื่อชี้แจงปัญหาและช่วยให้ OP ตัดสินใจอย่างถูกต้องขึ้นอยู่กับสถานการณ์เฉพาะ สิ่งที่เราทำได้คือช่วยเร่งกระบวนการ :)
Ivaylo Slavov

13

คุณต้องการเก็บสกุลเงินของคุณlongและคำนวณสกุลเงินของคุณdoubleอย่างน้อยก็เป็นข้อมูลสำรอง คุณต้องการให้ทุกการทำธุรกรรมlongที่จะใช้สถานที่ที่เป็น

เหตุผลที่คุณต้องการเก็บสกุลเงินไว้longคือคุณไม่ต้องการเสียสกุลเงินใด ๆ

สมมติว่าคุณใช้doubleและคุณไม่มีเงิน มีคนให้คุณสามสลึงแล้วนำพวกเขากลับมา

You:       0.1+0.1+0.1-0.1-0.1-0.1 = 2.7755575615628914E-17

มันไม่เจ๋งขนาดนั้น บางทีคนที่มีเงิน 10 ดอลล่าร์ต้องการที่จะให้โชคลาภของพวกเขาด้วยการให้สามเซนต์ครั้งแรกของคุณและจากนั้นให้ $ 9.70 กับคนอื่น

Them: 10.0-0.1-0.1-0.1-9.7 = 1.7763568394002505E-15

จากนั้นคุณให้สลึงกลับคืนมาให้พวกเขา:

Them: ...+0.1+0.1+0.1 = 0.3000000000000018

นี่มันหัก

ทีนี้ลองใช้เวลานานแล้วเราจะติดตามสิบเซ็นต์ (ดังนั้น 1 = $ 0.001) ให้ทุกคนบนโลกนี้หนึ่งพันหนึ่งร้อยสิบสองล้านเจ็ดหมื่นห้าพันหนึ่งร้อยสี่สิบสามดอลลาร์:

Us: 7000000000L*1112075143000L = 1 894 569 218 048

อืมรอเราให้ทุกคนได้เงินหนึ่งพันล้านเหรียญ ล้นเป็นภัยพิบัติที่นี่

ดังนั้นเมื่อใดก็ตามที่คุณกำลังคำนวณจำนวนเงินที่จะโอนการใช้doubleและมันจะได้รับMath.round longแล้วแก้ไขขึ้นยอดคงเหลือ (เพิ่มและลบทั้งสองบัญชี) longโดยใช้

เศรษฐกิจของคุณจะไม่รั่วไหลและจะเพิ่มขึ้นเป็นสี่พันล้านดอลลาร์

มีปัญหาที่ยุ่งยากมากขึ้น - ตัวอย่างเช่นคุณจะทำอย่างไรถ้าคุณจ่ายเงินยี่สิบครั้ง? * - แต่สิ่งนี้ควรเริ่มต้น

* คุณคำนวณการชำระเงินหนึ่งรอบไปlong; จากนั้นคูณด้วย20.0และตรวจสอบว่าอยู่ในช่วงหรือไม่ ถ้าเป็นเช่นนั้นคุณคูณการชำระเงินด้วย20Lเพื่อรับจำนวนเงินที่หักจากยอดคงเหลือของคุณ โดยทั่วไปการทำธุรกรรมทั้งหมดจะต้องได้รับการจัดการตามlongดังนั้นคุณจำเป็นต้องสรุปการทำธุรกรรมของแต่ละบุคคลจริงๆ; คุณสามารถคูณเป็นทางลัด แต่คุณต้องให้แน่ใจว่าคุณไม่ได้เพิ่มข้อผิดพลาดในการปัดเศษและที่คุณไม่ล้นซึ่งหมายความว่าคุณต้องตรวจสอบกับก่อนที่จะทำการคำนวณจริงกับdoublelong


8

ฉันจะไปบอกว่าค่าใด ๆ ที่อาจปรากฏต่อผู้ใช้ควรเป็นจำนวนเต็ม เงินเป็นเพียงตัวอย่างที่โดดเด่นที่สุดของเรื่องนี้ จัดการ 225 ดาเมจเป็นสี่เท่าให้กับสัตว์ประหลาดที่มี 900 HP และพบว่ามันมีเหลือ 1 HP จะลบออกจากประสบการณ์เช่นเดียวกับการค้นพบว่าคุณเป็นเศษเสี้ยวที่มองไม่เห็นของเงินสั้น ๆ

ในด้านเทคนิคฉันคิดว่ามันคุ้มค่าที่จะสังเกตว่าไม่ต้องเปลี่ยนกลับไปทำสิ่งที่ก้าวหน้าอย่างที่สนใจ ตราบใดที่คุณมีจำนวน headroom เพียงพอในประเภทจำนวนเต็มที่คุณเลือกการคูณและการหารจะยืนสำหรับการคูณด้วยจำนวนทศนิยมเช่นการเพิ่ม 4% ปัดเศษลง:

number=(number*104)/100

หากต้องการเพิ่ม 4% ปัดเศษตามแบบแผนมาตรฐาน:

number=(number*104+50)/100

ไม่มีจุดลอยตัวที่ไม่ถูกต้องที่นี่การปัดเศษจะแยก.5เครื่องหมายนั้นเสมอ

แก้ไขคำถามจริง:

เห็นว่าการอภิปรายได้หายไปผมเริ่มคิดว่าการสรุปสิ่งที่คำถามคือทั้งหมดที่เกี่ยวกับอาจจะมีประโยชน์มากกว่าที่เรียบง่ายint/ floatคำตอบ ที่สำคัญของคำถามที่ไม่เกี่ยวกับประเภทของข้อมูลมันเป็นเรื่องของการควบคุมรายละเอียดของโปรแกรม

การใช้จำนวนเต็มเพื่อแทนค่าที่ไม่ใช่จำนวนเต็มบังคับให้โปรแกรมเมอร์จัดการกับรายละเอียดการนำไปใช้งาน "ความแม่นยำในการใช้คืออะไร" และ "จะปัดเศษอย่างไรดี" เป็นคำถามที่ต้องตอบอย่างชัดเจน

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

เกิดอะไรขึ้นในการใช้งานแบบลอยตัวและต้องการควบคุมการปัดเศษ? ดูเหมือนจะเป็นไปไม่ได้เกือบ วิธีเดียวที่จะทำให้ลอยคาดเดาได้อย่างแท้จริงคือการใช้ค่าเดียวที่สามารถแสดงในทั้ง2^ns แต่การก่อสร้างนั้นทำให้ใช้งานได้ยาก

ดังนั้นคำตอบสำหรับคำถามง่าย ๆ คือ: ถ้าคุณต้องการควบคุมให้ใช้จำนวนเต็มถ้าไม่ใช่ให้ใช้การลอย

แต่คำถามที่กำลังถกเถียงกันนั้นเป็นเพียงคำถามอีกรูปแบบหนึ่ง: คุณต้องการควบคุมหรือไม่?


5

แม้ว่าจะเป็น "เกมเพียงอย่างเดียว" ฉันจะใช้Money Patternจาก Martin Fowler ซึ่งได้รับการสนับสนุนเป็นเวลานาน

ทำไม?

รองรับหลายภาษา (L10n): การใช้รูปแบบนั้นคุณสามารถแปลสกุลเงินเกมของคุณได้อย่างง่ายดาย ลองคิดถึงเกมกุนเก่าเช่น "Transport Tycoon" พวกเขาอนุญาตให้ผู้เล่นเปลี่ยนสกุลเงินในเกม (เช่นจากปอนด์อังกฤษเป็นดอลลาร์สหรัฐ) เพื่อให้ตรงกับสกุลเงินโลกแห่งความจริง

และ

ชนิดข้อมูลแบบยาวคือเลขจำนวนเต็มเสริมสองตัวที่เซ็นชื่อแบบ 64 บิต มีค่าต่ำสุด -9,223,372,036,854,775,808 และค่าสูงสุด 9,223,372,036,854,775,807 (รวม) ( แบบฝึกหัด Java )

นั่นหมายความว่าคุณสามารถจัดเก็บM2 US Money Supply ได้ 9,000 เท่า(ประมาณ 10,000 ล้านดอลลาร์) ให้คุณมีพื้นที่มากพอที่จะใช้สกุลเงินโลกอื่น ๆ แม้กระทั่งผู้ที่มี / มีภาวะเงินเฟ้อรุนแรง (หากอยากรู้อยากเห็นให้ดูเงินเฟ้อเยอรมันหลังสงครามโลกครั้งที่ 1 ซึ่งมีขนมปัง 1 ปอนด์อยู่ที่ 3,000,000,000 คะแนน)

Long นั้นง่ายต่อการคงอยู่เร็วมากและควรให้คุณมีเวลาเพียงพอในการคำนวณดอกเบี้ยทั้งหมดโดยใช้เลขจำนวนเต็มเท่านั้นคำตอบจาก eBusiness ให้คำอธิบายเกี่ยวกับวิธีการทำเช่นนั้น


2

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

ในที่สุดฉันก็มักจะใช้เวลาอีกเล็กน้อยในการเขียนโค้ดและใช้การทดสอบหน่วยสำหรับ "มุมคดี" และกรณีปัญหาที่เป็นที่รู้จัก - ดังนั้นฉันจะคิดในแง่ของการแก้ไข BigInteger ซึ่งครอบคลุมจำนวนมากโดยพลการและรักษาบิตเศษส่วนด้วย BigDecimal หรือ ส่วน BigRational (สอง BigIntegers หนึ่งสำหรับส่วนและอื่น ๆ สำหรับเศษ) - และรวมรหัสเพื่อให้ส่วนที่เศษส่วนเป็นเศษส่วนที่แท้จริงโดย (อาจเป็นระยะ ๆ ) เพิ่มส่วนที่ไม่ใช่เศษส่วนไปยัง BigInteger หลัก จากนั้นฉันก็จะติดตามทุกอย่างภายในเป็นเซ็นต์เพื่อป้องกันเศษส่วนจากการคำนวณ GUI

อาจเป็นวิธีที่ซับซ้อนสำหรับเกม (ง่าย) - แต่ดีสำหรับห้องสมุดที่เผยแพร่เป็นโอเพ่นซอร์ส! เพียงแค่ต้องคิดหาวิธีในการรักษาประสิทธิภาพให้ดีเมื่อจัดการกับเศษส่วน ...


1

ไม่ลอยแน่นอน ด้วยตัวเลขเพียง 7 หลักคุณจะมีความถูกต้องเช่น:

12,345.67 123,456.7x

คุณเห็นแล้วว่าด้วยเงิน 10 ^ 5 ดอลลาร์คุณกำลังสูญเสียเงินเพนนี double ใช้งานได้สำหรับวัตถุประสงค์ของเกมไม่ใช่ในชีวิตจริงเนื่องจากความถูกต้อง

แต่ความยาว (และนี่หมายถึง 64 บิตหรือยาวใน C ++) ไม่เพียงพอหากคุณกำลังติดตามธุรกรรมและรวมเข้าด้วยกัน ก็เพียงพอที่จะรักษาสัดส่วนการถือครองสุทธิของ บริษัท ใด ๆ แต่การทำธุรกรรมทั้งหมดสำหรับหนึ่งปีอาจล้น ขึ้นอยู่กับว่า "โลก" ทางการเงินของคุณใหญ่แค่ไหนในเกม


0

วิธีแก้ปัญหาอื่นที่ฉันจะเพิ่มที่นี่คือการสร้างระดับเงิน ชั้นจะเป็นสิ่งที่ต้องการ (อาจเป็นโครงสร้างได้)

class Money
{
     string currencyType; //the type of currency example USD could be an enum as well
     bool isNegativeValue;
     unsigned long int wholeUnits;
     unsigned short int partialUnits;
}

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

สิ่งนี้สามารถขยายเพิ่มเติมเพื่อรวมสิ่งต่าง ๆ เช่นจำนวนหน่วยบางส่วนต่อหน่วยทั้งหมดดังนั้นคุณอาจมีสกุลเงินที่แบ่งย่อยอย่างอื่นที่ไม่ใช่ 100 หน่วยย่อยเซ็นต์ในกรณีนี้ต่อดอลลาร์ คุณสามารถมี floopies 125 ชิ้นทำหนึ่ง flooper ตัวอย่างเช่น

แก้ไข:

ฉันจะขยายความคิดในการที่คุณอาจมีตารางการเรียงลำดับที่ชั้นเงินสามารถเข้าถึงที่จะให้อัตราแลกเปลี่ยนสำหรับสกุลเงินเทียบกับสกุลเงินอื่น ๆ ทั้งหมด คุณสามารถสร้างสิ่งนี้ในฟังก์ชั่นการใช้งานมากเกินไป ดังนั้นหากคุณพยายามเพิ่ม 4 USD เป็น 4 British ปอนด์โดยอัตโนมัติจะให้คุณ 6.6 ปอนด์ (ตามการเขียน)


0

ดังที่ได้กล่าวไว้ในหนึ่งในความคิดเห็นเกี่ยวกับคำถามเดิมของคุณปัญหานี้ได้รับการกล่าวถึงใน StackExchange: https://stackoverflow.com/questions/8148684/what-is-the-best-data-type-to-use-for-use เงินในจาวาแอป

โดยพื้นฐานแล้วไม่ควรใช้ประเภทจุดลอยตัวแทนค่าเงินและเช่นเดียวกับภาษาส่วนใหญ่ Java มีประเภทที่เหมาะสมกว่า เอกสารคู่มือของออราเคิลเกี่ยวกับการตรวจสอบเบื้องต้นแนะนำให้ใช้BigDecimal


-1

ใช้คลาสเป็นวิธีที่ดีที่สุด คุณสามารถซ่อนการใช้เงินของคุณคุณสามารถสลับเป็นสองเท่า / นานเท่าที่คุณต้องการได้ตลอดเวลา

ตัวอย่าง

class Money
{
    private long count;//Store here what ever you want in what type you want i suggest long or int
    //methods constructors etc.
    public String print()
    {
        return count/100.F; //convert this to string
    }
}

ในรหัสของคุณใช้เพียงแค่ทะเยอทะยานมันเป็นวิธีที่ดีกว่าที่ฉันคิดและคุณสามารถเก็บวิธีการที่มีประโยชน์มากขึ้น

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