มีภาษาการเขียนโปรแกรมที่ 1/6 ทำงานเหมือนกับ 1.0 / 6.0 หรือไม่


11

ในขณะที่ฉันกำลังเขียนโปรแกรมใน C ++ เมื่อหลายวันก่อนฉันทำผิดพลาด (ที่ฉันมีประวัติของการทำมัน!) ในส่วนหนึ่งของรหัสของฉันฉันมี 1/6 และฉันคาดหวังว่าจะเป็น 0.16666666666 ซึ่งไม่ใช่กรณี ในขณะที่คุณทุกคนรู้ว่าผลลัพธ์คือ 0 - C, C ++, Java, Python ทั้งหมดทำงานเหมือนกัน

ฉันโพสต์บนหน้า Facebook ของฉันและตอนนี้มีการอภิปรายเกี่ยวกับกรณีที่มีการเขียนโปรแกรมภาษาที่มีลักษณะการทำงานเช่นเดียวกับ1/61.0/6.0


5
Haskell 1/6 = 0.166666666666666666
t123

PowerShell สร้าง 0.16666666666666667 ซึ่งทำให้ฉันประหลาดใจเนื่องจาก 1 เป็นจำนวนเต็ม ฉันพนันได้ว่ามีภาษา. NET อื่น ๆ ที่สร้างมูลค่าที่คุณคาดหวัง
JohnL

ในทางทฤษฎีมีจำนวนไม่ จำกัด .. นี่คืออีก: Rebolเช่นเดียวกับอนุพันธ์เช่น Orca, Red และอื่น ๆ >> 1 / 6->== 0.166666666666667
Izkata

ลัวะทำสิ่งนี้ มันมีเพียงประเภทตัวเลขเดียวซึ่งโดยปกติจะเหมือนกับคู่ของ C
Machado

ใน Clojure 1/6เป็นจริง 1/6 (ประเภทเศษส่วน) ซึ่งบังคับให้Doubleเป็น 1.66666 ...
kaoD

คำตอบ:


17

ทุกคนลืมปาสกาลหรือไม่

1/6อัตราผลตอบแทน0.1666666...(เพื่อความแม่นยำสิ่งที่ได้รับการสนับสนุน)

1 div 6 อัตราผลตอบแทน 0

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

นอกจากนี้เนื่องจาก C มีการกำหนดเป้าหมายไว้ที่รหัสระดับระบบเป็นหลักโปรแกรม C ส่วนใหญ่จึงไม่ใช้ floating-point เลย ในครั้งเดียวการเพิ่มรหัสจุดลอยตัวไปยังโปรแกรมที่ไม่ต้องการอย่างอื่นอาจเป็นปัญหาร้ายแรง อาจเป็นกรณีนี้สำหรับระบบฝังตัวขนาดเล็ก - ซึ่งอีกครั้งเป็นเป้าหมายสำคัญสำหรับ C.

ในโปรแกรม C ส่วนใหญ่การตัดทอนจำนวนเต็มอาจเป็นสิ่งที่คุณต้องการ

หากได้1 / 6ผลลัพธ์เป็นจำนวนจุดลอยตัวเป็น C ดังนั้น:

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

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

ในคู่มือ C รุ่นปี 1974 (นั่นคือ 4 ปีก่อนการตีพิมพ์ฉบับพิมพ์ครั้งแรกของ K&R) Ritchie ไม่ได้พูดถึงความสับสนที่อาจเกิดขึ้น:

ไบนารี / โอเปอเรเตอร์บ่งชี้ถึงการหาร ข้อควรพิจารณาประเภทเดียวกันกับการคูณใช้

ที่บอกว่าถ้าทั้งสองตัวถูกดำเนินการเป็นประเภทintหรือผลที่ได้คือประเภทcharint

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


5
ปาสคาล รับรายละเอียดก่อนที่ C ผิดพลาด™
Mason Wheeler

และจากนั้นอัลกอลก็พัฒนาผู้สืบทอดต่อไป (ในจำนวนที่ทั้ง C และ Pascal ยืน)
AProgrammer

Pascal - รับรายละเอียดถูกต้อง (ให้หรือรับปัจจัย 10)
Martin Beckett

1
ฉันเขียนตอนแรก1.666666...ซึ่งผิดอย่างชัดเจน ข้อแก้ตัวอ่อนแอของฉันคือโปรแกรมทดสอบ Pascal ที่ฉันเขียนพิมพ์1.6666666666666667E-0001
Keith Thompson

16

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


ขอบคุณ คุณมีภาษาโปรแกรมอื่นใดในใจ ครอบครัวขั้นพื้นฐานอาจจะ?
Pouya

1
@Pouya: พฤติกรรมนี้เป็นมาตรฐานสำหรับครอบครัว Pascal /สร้างค่าทศนิยมเสมอและตัวดำเนินการแยก ( div) ถูกใช้สำหรับการหารจำนวนเต็ม
Mason Wheeler

13

ภาษาที่โดดเด่นจาวาสคริปต์ 1.0 / 6.0 = 1/6 = 0.16666666666666666

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

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


6
นี่ไม่ใช่ "กฎง่ายๆ" มันเป็นกฎของ C และภาษาหนึ่งภาษาที่คัดลอกความผิดพลาดของ C อย่างสุ่ม ๆ
Mason Wheeler

4
@MasonWheeler: FORTRAN "คัดลอกความผิดพลาดของ C" หรือไม่? ฉันเองเชื่อว่าภาษาที่ออกแบบมาอย่างดีควรใช้โอเปอเรเตอร์แยกต่างหากสำหรับการหารที่ถูกตัดทอนเทียบกับการหารจำนวนเต็มจำนวนโดยประมาณ (และ btw สำหรับค่าและความเท่าเทียมอ้างอิง) แต่การตัดสินใจออกแบบในวันที่ นั่นไม่ได้หมายความว่าทุกภาษาควรทำในสิ่งที่ฟอร์แทนทำในปี 1950
supercat

3
ภาษาระดับต่ำกว่าจำเป็นต้องสร้างความแตกต่างเหล่านี้ ภาษาระดับสูงขึ้น / แบบไดนามิกมักจะดีกว่าโดยไม่ต้องพวกเขา มันเป็นการแลกเปลี่ยนการออกแบบ ฉันซาบซึ้งใจที่มีตัวสร้างตัวเลข / ประเภทใบ้จำนวนมากใน JS แต่ฉันคิดว่าฉันรู้สึกว่า JS ไม่เพียงพอเมื่อพยายามเขียนเอนจิ้น 3 มิติที่มีประสิทธิภาพสูงโดยไม่มีการควบคุมประเภทที่เข้มงวด JS อาจมียูทิลิตี้ซ้อนทับกับภาษาอื่น ๆ แต่ฉันไม่คิดว่าจะมีใครคิดว่ามันเป็น goto สำหรับการเขียนสิ่งที่มีประสิทธิภาพสูงใกล้เคียงกับโครเมี่ยม
Erik Reppen

2
คณิตศาสตร์นอกการเขียนโปรแกรมพิจารณากฎจำนวนเต็มเท่านั้นสำหรับการหาร
Erik Reppen

5
@MasonWheeler: การเขียนโปรแกรมไม่ใช่คณิตศาสตร์ที่บริสุทธิ์ ในทางคณิตศาสตร์ 1/6 เป็นจำนวนตรรกยะและไม่สามารถแสดงได้อย่างชัดเจนด้วยเลขทศนิยมแบบเลขฐานสอง การแสดงที่แม่นยำเพียงอย่างเดียวคืออัตราส่วนกับตัวส่วนหกเท่าของตัวเศษ
วินไคลน์

7

มีหลายภาษาที่ให้((1/6)*6)ผลลัพธ์เป็น 1 ไม่ใช่ใน 0 ตัวอย่างเช่น PL / SQL, ภาษาเบสิกหลายภาษา, Lua

โดยบังเอิญในทุก ๆ langauges 1/6 ให้ผลลัพธ์เป็น. 166666667 หรือ 0.16666666666667 หรืออะไรทำนองนั้นที่คล้ายกัน ฉันเลือก ((1/6) * 6) == 1 ตัวแปรเพื่อกำจัดความแตกต่างเล็กน้อยเหล่านั้น


7
นั่นไม่ใช่คำถาม
Roc Martí

20
โดยบังเอิญในทุก ๆ langauges 1/6 ให้ผลลัพธ์เป็น. 166666667 หรือ 0.16666666666667 หรืออะไรทำนองนั้นที่คล้ายกัน ฉันเลือก((1/6)*6)==1ตัวแปรเพื่อกำจัดความแตกต่างเล็กน้อยเหล่านั้น แต่ดูเหมือนว่าฉันประเมินทักษะทางคณิตศาสตร์ของบางคน
281377

1
@ RocMartíใช่มันคือ ...
MattDavey

1
ฉันจะแปลกใจที่เห็น (1.0 / 6.0) * 6 มีค่าเท่ากับ 1! การปัดเศษผลลัพธ์ (1.0 / 6.0) จะทำให้เกิดความแตกต่างเล็กน้อย (แม้ว่าจะมีไม่กี่ภาษาที่เป็นค่าเริ่มต้นที่แน่นอนไม่มีที่สิ้นสุด)
Sjoerd

1
@Sererd: มันไม่ได้ประหลาดใจเกินไปที่จริง พิจารณาเป็นทศนิยมในสถานการณ์สมมติที่ 1/11 * 11 ด้วยค่าทั้งหมดที่ถูกต้องถึงห้าตัวเลขที่สำคัญ ค่า 1/11 คือ 9.0909 * 10 ^ -2 คูณด้วย 11 และหนึ่งจะได้รับ 99.9999 * 10 / -2 ก่อนที่จะปัดเศษ ตัวเลขสำคัญรอบที่ห้าและผลลัพธ์จะเท่ากับ 1.0000 * 10 ^ 0 โปรดทราบว่ากุญแจสำคัญคือ mantissa ของ 1/6 คือ "... 0101010101 ... " หากบิตสุดท้ายของการเป็นตัวแทนคือ "1" การคูณด้วยหกและการปัดเศษจะให้ผล 1 หากบิตสุดท้ายเป็นศูนย์จะไม่เกิดขึ้น
supercat

3

Haskell ปฏิบัติต่อ 1/6 และ 1.0 / 6.0 เหมือนกับ 0.166666666666666666 เหมือนกัน นอกจากนี้ยังแสดงผล 1 / 6.0 และ 1.0 / 6 ว่าเป็นค่าเดียวกันเช่นกัน

นี่เป็นเพราะประเภทตัวเลขพื้นฐานใน Haskell ไม่เหมือนกับภาษาอื่น การแบ่งจำนวนเต็มจริงค่อนข้างซับซ้อน


2

ใช่ Perl ทำ ซับเดียว

perl -e '$x=1/6;print "$x\n";'

ผลลัพธ์ในผลลัพธ์ของ:

0.166666666666667

ฉันเชื่อว่า PHP ทำงานในลักษณะเดียวกัน

แก้ไขเพื่อเพิ่ม:ฉันยังเชื่อว่าเงื่อนไขที่จำเป็น (แต่ไม่เพียงพอ) 1/6 == 1.0/6.0สำหรับภาษาที่มีปัญหาในการพิมพ์อย่างอ่อน


ทำไมการพิมพ์ห่าจึงไม่จำเป็น (จำเป็นต้องมีความหมาย) เพียงกำหนด / ถึง (ยัง) หมายถึงส่วนที่ลอยในกรณีของการขัดแย้งทั้งสองเป็นจำนวนเต็ม

1
@delnan - en.wikipedia.org/wiki/Weak_typingฉันคิดว่ามันอาจจะเป็นไปได้ที่จะมีภาษาพิมพ์มั่นที่/มากเกินไปโดยอัตโนมัติขึ้นอยู่กับประเภทของการขัดแย้ง แต่ดูเหมือนว่าการละเมิดหลักการน้อยมหัศจรรย์ที่จะ ฉัน ...

2
การพิมพ์ที่อ่อนแอ / แข็งแรงนั้นไม่ชัดเจน (เช่นเดียวกับ wiki ยังหมายถึง) โปรดหลีกเลี่ยงและเจาะจง ฉันคิดว่าคำจำกัดความของคุณห้ามการแปลงโดยนัย แต่ไม่ใช่ความแตกต่างแบบเฉพาะกิจ? หากเป็นเช่นนั้นให้พิจารณา Haskell ซึ่งไม่มีการแปลงโดยนัย แต่ใช้งานได้ดี (เช่นในการทำงาน 99% ของเวลาและสามารถเข้าใจได้โดยมนุษย์ทั่วไป) ตัวเลขที่หลากหลาย และทำไมสิ่งนี้ถึงน่าประหลาดใจ? มันจะน่าประหลาดใจกว่านี้ (ไม่ต้องพูดน่ารำคาญ) กับฉันถ้าฉันต้องเพิ่มจำนวนจุดลงในทุก ๆ อินสแตนซ์ของผู้ปฏิบัติงานใด ๆ ขึ้นอยู่กับความแม่นยำที่แน่นอนที่ฉันต้องการ

2
@ Jackmaney ฉันคิดว่าผู้มาใหม่ส่วนใหญ่เป็นวิธีที่น่าแปลกใจมากที่ 1/2 ควรเท่ากับ 0 มากกว่าถ้าหากการหารจำนวนเต็มสองจำนวนให้ผลลัพธ์เป็นสองเท่า หลังจากจำนวนเต็มทั้งหมดจะไม่ถูกปิดภายใต้การหารในคณิตศาสตร์อย่างใดอย่างหนึ่ง เช่นเดียวกับที่ delnan ชี้ให้เห็น Haskell เป็นตัวอย่างของภาษาที่มีการพิมพ์อย่างมากซึ่งในจำนวนเต็มสองตัว / ไม่ได้ผลิตจำนวนเต็ม และ Python 3 ก็เป็นอีกอันหนึ่ง
sepp2k

1
พิมพ์ภาษา Haxe เป็นอย่างยิ่ง (แม้ว่าจะอนุมานได้) พิมพ์และยังไม่มีการแบ่งจำนวนเต็มเพียงลอย ดังนั้นคุณไป
K.Steff

2

ใน Squeak Smalltalk /บนจำนวนเต็มสร้างวัตถุเศษส่วน ดังนั้นแม้ว่านี่จะไม่เหมือนกับการหารลอย แต่ก็ยัง(1/6)*6ส่งคืน 1


ใน Smalltalk-80 ทุกครั้ง (นั่นคือเกือบทุก Smalltalks) แอมเบอร์เป็นหนึ่งในข้อยกเว้นร่วมสมัย (ซึ่งเป็นที่เข้าใจและถูกรวบรวมเป็น JavaScript)
herby

2

ใช่ฉันเพียงแค่การตรวจสอบของฉันTI-99 / 4A 's สร้างขึ้นในTI ขั้นพื้นฐาน เนื่องจากถือว่านิพจน์ตัวเลขทั้งหมดเป็นจุดลอยตัวการดำเนินการหารจะเป็นทศนิยมเช่นกัน

 TI BASIC READY
>PRINT 1/6
  .1666666667

>


2

MATLAB ตัวอักษรตัวเลขเป็นสองเท่าโดยค่าเริ่มต้น

>> 1/6
ans =
    0.1667

2

Clojure ใช้เศษส่วนเป็นค่าเริ่มต้น มันไม่เหมือนกับ 1.0 / 6.0 แต่คุณสามารถแปลงเป็นfloatหรือdoubleเมื่อคุณต้องการ

user=> (/ 1 6)
1/6
user=> (* (/ 1 6) 2)
1/3
user=> (pos? (/ 1 6)) ; Is 1/6 > 0?
true
user=> (float (/ 1 6))
0.16666667

1

น่าแปลกที่ดูเหมือนว่าจะทำงานอย่างถูกต้องในWindows PowerShell (รุ่น 3)

PS C:\> 1.0 / 6.0
0.166666666666667

PS C:\> 1/6
0.166666666666667

ดูเหมือนว่าจะทำงานใน Python 3 ตามที่ sepp2k พูดถึง อีกสองภาษาที่ฉันมีอยู่ใน REPL, Scala และ Ruby ทั้งสองมีการแบ่งจำนวนเต็มและให้ผลเป็น 0


0

ภาษา Rexx สร้างคำตอบที่ถูกต้องเสมอ ตัวอย่างเช่น: 5/2 = 2.5 Rexx เป็นภาษาที่ยอดเยี่ยมที่ไม่ได้ใช้อย่างเพียงพอ ในทางทฤษฎีเมื่อผู้รวบรวมไม่สามารถระบุสิ่งที่คุณต้องการได้ดีกว่าที่จะทำคณิตศาสตร์ที่ถูกต้อง แต่สิ่งนี้อาจไม่มีประสิทธิภาพ Rexx ยังมีตัวดำเนินการ //

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