เมื่อการแปลงจากจำนวนเต็มเป็นโสดอาจสูญเสียความแม่นยำ


27

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

การแปลงต่อไปนี้อาจสูญเสียความแม่นยำ:

  • จำนวนเต็มเป็นโสด
  • ยาวเป็นโสดหรือสองเท่า
  • ทศนิยมเป็นเดี่ยวหรือคู่

อย่างไรก็ตามการแปลงเหล่านี้จะไม่สูญเสียข้อมูลหรือขนาด

.. แต่ตามบทความอื่นเกี่ยวกับชนิดข้อมูล ,

  • ประเภทจำนวนเต็มสามารถจัดเก็บได้ตั้งแต่ -2.147.483.648 ถึง 2.147.483.647 และ

  • ประเภทเดียวสามารถเก็บได้จาก

    • 1,401298E-45 ถึง 3,4028235E + 38 สำหรับตัวเลขบวก
    • และ -3,4028235E + 38 ถึง - 1,401298E-45 สำหรับจำนวนลบ

.. ดังนั้น Single สามารถเก็บตัวเลขได้มากกว่าจำนวนเต็ม ฉันไม่เข้าใจว่าสถานการณ์เช่นนี้จากการแปลงเป็นจำนวนเต็มอาจไม่แม่นยำ มีคนอธิบายได้ไหม

คำตอบ:


87

ซิงเกิลสามารถเก็บตัวเลขได้มากกว่าจำนวนเต็ม

ไม่มันไม่สามารถ ทั้งสองSingleและIntegerเป็น 32 บิตซึ่งหมายความว่าทั้งสองสามารถเก็บจำนวนที่แน่นอนจำนวนเดียวกันนั่นคือ 2 32 = 4294967296 ตัวเลขที่แตกต่าง

เนื่องจากช่วงของSingleมีขนาดใหญ่กว่านั้นอย่างชัดเจนจึงเป็นที่ชัดเจนในทันที (เพราะหลักการของPigeonhole ) ที่ไม่สามารถแสดงตัวเลขทั้งหมดในช่วงนั้นได้

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

หากมีตัวเลขจำนวนหนึ่งIntegerที่ไม่สามารถแสดงได้SingleการแปลงจากIntegerเป็นSingle ต้องสามารถสูญเสียข้อมูลได้


3
+1 สำหรับคำอธิบายที่ดีว่าทำไมถึงเป็นเช่นนี้แม้ว่าคำถามจะเกิดขึ้นจริงเมื่อ ("ในสถานการณ์อะไร") มันเกิดขึ้น ...
doubleYou

21
@doubleYou: 4261412864 จาก 4294967296 Integers (99.2%) ไม่สามารถแสดงได้Singleดังนั้น "เมื่อ" คือ "สวยมากเสมอ"
Jörg W Mittag

2
หากคุณต้องการแม่นยำยิ่งขึ้นSingleสามารถแสดงตัวเลขที่แตกต่างกันได้เพียง 4,278,190,079 เท่านั้น Singleค่าหมายถึงจำนวนและถ้าหากตัวแทนเก็บไว้ไม่ได้ 255 ซึ่งหมายความว่ามี 255 * 2 ^ 24 Singles ซึ่งเป็นตัวแทนของตัวเลข ในจำนวนนี้มีสองตัวที่มีค่าเท่ากัน (กล่าวคือเป็นศูนย์) และส่วนที่เหลือเป็นจำนวนที่ต่างกัน
แทนเนอร์ Swett

10
en.wikipedia.org/wiki/Single-precision_floating-point_formatอธิบายถึงข้อ จำกัด ที่ดีสำหรับ IEEE754 binary32 จำนวนเต็มใน[-16777216,16777216](2 ^ 24 = ความกว้างซิกนิฟิแคนด์) สามารถแสดงได้อย่างแม่นยำ ตัวเลขที่ใหญ่ขึ้นจะถูกปัดเศษเป็นพหุคูณที่ใกล้เคียงที่สุดของ 2, 4, 8, ...
Peter Cordes

14
“ ซึ่งหมายความว่าทั้งสองสามารถเก็บจำนวนที่แน่นอนจำนวนเดียวกัน” - ไม่ได้หมายความว่า หมายความว่าหากทั้งสองประเภทมีวิธีการจัดเก็บแต่ละหมายเลขเท่ากัน และนี่ไม่ใช่กรณี; ตัวอย่างเช่นSingleมีสองวิธีในการจัดเก็บศูนย์ ดังนั้นSingleในความเป็นจริงสามารถเป็นตัวแทนน้อยกว่าIntegerตัวเลขที่แตกต่างกว่า
Konrad Rudolph

28

ชนิดจุดลอยตัว (เช่น Single และ Double) แสดงด้วยหน่วยความจำด้วยเครื่องหมาย mantissa และเลขชี้กำลัง คิดว่ามันเป็นสัญลักษณ์ทางวิทยาศาสตร์:

Sign*Mantissa*Base^Exponent

พวกเขา - อย่างที่คุณคาดไว้ - ใช้ฐาน 2 มี tweaks อื่น ๆ ที่อนุญาตให้ใช้แทนอินฟินิตี้และ NaN และเลขชี้กำลังเป็นออฟเซ็ต (จะกลับมาที่) และชวเลขสำหรับแมนทิสซา . มองหามาตรฐาน IEEE 754 ซึ่งครอบคลุมการแสดงและการดำเนินงานเพื่อดูรายละเอียดเพิ่มเติม

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


ในกรณีของ Single เรามี 1 บิตสำหรับเขาเซ็น 8 สำหรับ exponent และ 23 สำหรับ mantissa

ทีนี้ก็คือเราจะเก็บ mantissa จากตัวเลขที่สำคัญที่สุด จำไว้ว่าเลขศูนย์ทั้งหมดทางด้านซ้ายนั้นไม่เกี่ยวข้องกัน และเมื่อเราทำงานด้วยระบบเลขฐานสองเรารู้ว่าตัวเลขที่สำคัญที่สุดคือ 1 ※ เนื่องจากเรารู้ว่าเราไม่ต้องเก็บมัน ขอบคุณที่จดชวเลขช่วง mantissa ที่มีประสิทธิภาพคือ 24 บิต

※: หากจำนวนที่เราจัดเก็บนั้นเป็นศูนย์ เพื่อที่เราจะได้ตั้งค่าบิตทั้งหมดเป็นศูนย์ อย่างไรก็ตามหากเราพยายามตีความว่าภายใต้คำอธิบายที่ฉันให้คุณคุณจะมี 2 ^ 24 (นัย 1) คูณด้วย 1 (2 ถึงพลังของเลขชี้กำลัง 0) ดังนั้นในการแก้ไขศูนย์เลขชี้กำลังเป็นค่าพิเศษ นอกจากนี้ยังมีค่าพิเศษในการจัดเก็บอินฟินิตี้และ NaN ในเลขยกกำลัง

ตามการชดเชยเลขชี้กำลัง - นอกเหนือจากการหลีกเลี่ยงค่าพิเศษ - การมีออฟเซ็ตอนุญาตให้วางจุดทศนิยมก่อนเริ่มต้นของ mantissa หรือหลังสิ้นสุดโดยไม่จำเป็นต้องมีสัญลักษณ์สำหรับเลขชี้กำลัง


ซึ่งหมายความว่าสำหรับตัวเลขขนาดใหญ่ชนิดจุดลอยจะใส่จุดทศนิยมเกินกว่าจุดสิ้นสุดของ mantissa

จำไว้ว่าแมนทิสซาเป็นตัวเลข 24 บิต มันจะไม่แสดงหมายเลข 25 บิต ... มันไม่มีบิตพิเศษนั้น ดังนั้นเดี่ยวไม่สามารถแยกความแตกต่างระหว่าง 2 ^ 24 และ 2 ^ 24 + 1 (นี่คือตัวเลข 25 บิตแรกและพวกเขาแตกต่างกันในบิตสุดท้ายซึ่งไม่ได้เป็นตัวแทนในเดียว)

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


นี่ไม่ใช่คำอธิบายที่สมบูรณ์แบบของ1บิตนำโดยนัยในซิกนิแคนด์ มันส่อให้เห็นโดยลำเอียง-สัญลักษณ์ฟิลด์เป็นที่ไม่ใช่ศูนย์ Subnormals (aka denormals) รวมถึง+-0.0มีส่วน0สำคัญของซิกนิฟิแคนด์ ฉันเดาว่าคุณสามารถทำให้การพิจารณา0.0กรณีพิเศษง่ายขึ้น แต่0.0จริงๆแล้วก็ปฏิบัติตามกฎการเข้ารหัสเดียวกันกับ subnormals อื่น ๆ
Peter Cordes

25

นี่คือตัวอย่างจริงเมื่อแปลงจากIntegerเป็นSingleอาจเสียความแม่นยำ:

Singleชนิดสามารถเก็บจำนวนเต็มทั้งหมดจาก -16777216 เพื่อ 16777216 (รวม) แต่มันก็ไม่สามารถเก็บจำนวนเต็มทั้งหมดนอกช่วงนี้ ยกตัวอย่างเช่นมันไม่สามารถเก็บหมายเลข 16777217. สำหรับเรื่องที่ไม่สามารถจัดเก็บใด ๆมากขึ้นเลขคี่กว่า 16,777,216

เราสามารถใช้ Windows PowerShell เพื่อดูว่าเกิดอะไรขึ้นหากเราแปลงIntegerเป็นSingleและย้อนกลับ:

PS C:\Users\tanne> [int][float]16777213
16777213
PS C:\Users\tanne> [int][float]16777214
16777214
PS C:\Users\tanne> [int][float]16777215
16777215
PS C:\Users\tanne> [int][float]16777216
16777216
PS C:\Users\tanne> [int][float]16777217
16777216
PS C:\Users\tanne> [int][float]16777218
16777218
PS C:\Users\tanne> [int][float]16777219
16777220

สังเกตว่า 16777217 มีการปัดเศษลงเป็น 16777216 และ 16777219 มีการปัดเศษเป็น 16777220


4
และด้วยขนาดที่เพิ่มขึ้นระยะห่างระหว่างfloats ที่เป็นตัวแทนที่ใกล้ที่สุดยังคงเพิ่มขึ้นอย่างต่อเนื่องในฐานะพลัง en.wikipedia.org/wiki/…
Peter Cordes

12

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

จุดลอยตัวที่มีความแม่นยำเดี่ยวมี 23 mantissa บิต ​​แต่มี "นัย 1" ดังนั้น mantissa จึงมีประสิทธิภาพ 24 บิต ดังนั้นจำนวนเต็มทั้งหมดที่มีขนาดสูงถึง 2 24จึงสามารถแสดงได้อย่างแม่นยำในจุดลอยตัวที่มีความแม่นยำเดียว

เหนือกว่าตัวเลขที่สามารถแสดงได้อย่างต่อเนื่อง

  • จาก 2 24ถึง 2 25เพียงสามารถแสดงตัวเลขได้
  • จาก 2 25ถึง 2 26สามารถแสดงได้ทวีคูณของ 4 เท่านั้น
  • จาก 2 26ถึง 2 27สามารถแสดงได้ทวีคูณของ 8 เท่านั้น
  • จาก 2 27ถึง 2 28จะสามารถแสดงได้ทวีคูณของ 16 เท่านั้น
  • จาก 2 28ถึง 2 29สามารถแสดงผลได้หลายเท่าเท่านั้น
  • จาก 2 29ถึง 2 30สามารถคูณได้ 64 เท่านั้น
  • จาก 2 30ถึง 2 31สามารถคูณได้เพียง 128 รายการเท่านั้น

ดังนั้นในจำนวนเต็มที่เป็นไปได้2 32 32 บิตที่เป็นไปได้ 32 บิตเพียง 2 * (2 24 + 7 * 2 23 ) = 9 * 2 24สามารถแสดงในทศนิยมความแม่นยำเดียว นั่นคือ 3.515625% จากทั้งหมด


8

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

สมมติว่าคุณมีหน่วยความจำ 5 หลัก คุณสามารถเลือกที่จะใช้สิ่งเหล่านี้เช่น int ที่ไม่ได้ลงนามแบบปกติอนุญาตให้คุณมีตัวเลขใด ๆ ระหว่าง 0 ถึง 99999 ถ้าคุณต้องการที่จะเป็นตัวแทนของตัวเลขที่มากขึ้นคุณสามารถใช้สัญกรณ์ทางวิทยาศาสตร์และจัดสรรเลขสองหลักเป็นเลขชี้กำลัง ตอนนี้คุณสามารถเป็นตัวแทนของอะไรระหว่าง 0 และ 9.99 x 10 99

อย่างไรก็ตามจำนวนที่มากที่สุดที่คุณสามารถเป็นตัวแทนได้ตอนนี้คือเพียง 999 ถ้าคุณพยายามที่จะเป็นตัวแทน 12345 คุณจะได้รับ 1.23 x 10 4หรือ 1.24 x 10 4แต่คุณไม่สามารถแสดงตัวเลขใด ๆ ในระหว่างเพราะคุณ มีตัวเลขไม่เพียงพอ


3
การใช้ตัวเลขทศนิยมเป็นความคิดที่ดีที่ทำให้เข้าใจง่ายขึ้น แต่ย่อหน้าสุดท้ายนั้นทำให้เข้าใจผิดเล็กน้อย: จริง ๆ แล้วคุณสามารถแสดงตัวเลขที่สูงกว่า 999 และตัวอย่างของคุณแสดง: 12300 จะเป็น 1.23 x 10 <sup> 4 <sup > สิ่งที่คุณหมายถึงคือการเริ่มต้นจากหมายเลขนั้นมีช่องว่าง คุณจะคิดว่ามันจะเรียบเรียงใหม่ไหม?
Fabio พูดว่า Reinstate Monica
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.