ความแตกต่างระหว่างชนิดข้อมูลทศนิยมและทศนิยม


175

มันแตกต่างกันอย่างไรเมื่อฉันใช้ชนิดข้อมูลทศนิยมและทศนิยมใน MySQL?

เมื่อใดที่ฉันควรใช้


อย่าใช้FLOAT(m,n)มันนำไปสู่การปัดเศษสองรอบ มันไม่ได้ให้ประโยชน์อะไรเลย
Rick James

คำตอบ:


185

นี่คือสิ่งที่ฉันพบเมื่อฉันมีข้อสงสัยนี้

mysql> create table numbers (a decimal(10,2), b float);
mysql> insert into numbers values (100, 100);
mysql> select @a := (a/3), @b := (b/3), @a * 3, @b * 3 from numbers \G
*************************** 1. row ***************************
  @a := (a/3): 33.333333333
  @b := (b/3): 33.333333333333
@a + @a + @a: 99.999999999000000000000000000000
@b + @b + @b: 100

ทศนิยมทำในสิ่งที่ควรทำในกรณีนี้มันตัดส่วนที่เหลือออกไปดังนั้นจึงสูญเสียส่วนที่ 1/3

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

หวังว่านี่จะช่วยได้


4
การทดสอบที่ยอดเยี่ยม หลายปีที่ผ่านมาฟังก์ชั่นการแปลงข้อมูลของ C lib มักจะสร้างความแตกต่างของค่านาทีที่แปลงจาก ASCII เป็นจำนวนมากเมื่อเทียบกับที่กล่าวไว้ใน SQLServer เรื่องนี้ไม่ค่อยเป็นจริงอีกต่อไป การทดสอบเป็นนโยบายที่ดีที่สุดเนื่องจากเป็นการดีที่สุดที่จะทราบได้อย่างชัดเจนว่าอะไรคือข้อดี

15
ที่จริงแล้วการเพิ่ม DECIMAL มีข้อผิดพลาด หากคุณเพิ่ม 33.333333333 สามครั้งคุณจะไม่ได้ 100 ถ้าคุณหาร 100 ด้วย 3 คุณจะไม่ได้ตัวเลขที่มีเหตุผลหากไม่มีชุดตัวเลขต่อท้ายซ้ำคุณจึงไม่สามารถคูณด้วย 3 และได้ 100 ออกไป เครื่องคิดเลขและลอง เรารู้ว่ามีเหตุมีผล 1/3 + 1/3 + 1/3 ควรเท่ากับ 3 / 3rds IE: 1 แต่จำนวนตรรกยะคลาสนี้ไม่อนุญาตให้เราทำสิ่งนี้คำตอบแบบลอยถูกต้อง แต่นักบัญชีของคุณจะเกลียด !
user2548100

5
ไม่@aให้ 99.999999999000000000000000000000000 ทศนิยมหรือไม่ ซึ่งเป็นเทคนิคที่ถูกต้อง
Vincent Poirier

78

"ลอย" ในสภาพแวดล้อมส่วนใหญ่เป็นประเภททศนิยมจุดไบนารี สามารถจัดเก็บค่าฐาน -2 ได้อย่างแม่นยำ (จนถึงบางจุด) แต่ไม่สามารถจัดเก็บค่าฐาน 10 (ฐานสิบ) ได้อย่างแม่นยำ ทุ่นลอยที่เหมาะสมที่สุดสำหรับการคำนวณทางวิทยาศาสตร์ พวกมันไม่เหมาะกับคณิตศาสตร์เชิงธุรกิจส่วนใหญ่และการใช้ทุ่นอย่างไม่เหมาะสมจะกัดคุณ ค่าทศนิยมจำนวนมากไม่สามารถแสดงได้อย่างแน่นอนในฐาน -2 0.1ไม่สามารถเช่นและเพื่อให้คุณเห็นผลแปลก ๆ 1.0 - 0.1 = 0.8999999เช่น

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


@Michael Petrotta - ผู้ใช้เพียงป้อนตัวเลขทศนิยมของเขาในฟิลด์ที่กำหนดในรูปแบบ .. ฉันต้องการเพียงแค่เก็บไว้ใน DB ซึ่งจะเหมาะสมกว่า ?
Hacker

12
@Pradeep: ฉันรู้สึกว่าคุณไม่ได้ตอบคำถามของฉัน อาจเป็นเพราะคุณไม่รู้คำตอบด้วยตัวเองบางทีคุณอาจรู้สึกไม่สะดวกใจที่จะขอรายละเอียดเพิ่มเติมจากผู้จัดการหรือลูกค้าของคุณ หากเป็นเช่นนั้นฉันขอแนะนำให้กัดกระสุนปืนนั่งกับพวกเขาสองสามชั่วโมงแล้วเดินผ่านแอปพลิเคชันของคุณจริงๆ อะไรกันแน่และในรายละเอียดมากเป็นข้อมูลของคุณถูกใช้สำหรับ?
ไมเคิล Petrotta

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

1
ฉันคิดว่าคำตอบของคุณอาจถูกต้องในทางเทคนิค แต่การเน้นไปที่การลอยตัวเป็นประเภทไบนารีปิดบังจุดที่พวกเขาทั้งสองเก็บข้อมูลในรูปแบบเดียวกัน จำนวนจุดลอยตัวที่ยกกำลังแรกคือจำนวนเต็มและถูกเก็บไว้อย่างนั้น ในความเป็นจริงสำหรับความแม่นยำ 80 บิตฐานคือ int64 ในทางกลับกันถ้าคุณเขียนไลบรารีสำหรับจำนวนเต็มที่ยกกำลังพวกคุณจะพบปัญหาเดียวกันกับจำนวนเต็มหรือ DECIMALS หรือตัวเลขโรมันหรืออมยิ้ม มันไม่ใช่ที่จัดเก็บข้อมูลที่สร้าง "ข้อผิดพลาดในการปัดเศษ" แต่เป็นการจัดการทางคณิตศาสตร์ของห้องสมุด
user2548100

1
ด้วยคำถามที่มีคุณภาพต่ำมากซึ่งแทบไม่มีพารามิเตอร์ใดที่ระบุถึงสิ่งที่กลุ่ม OPs กังวลเป็นเรื่องยากที่จะรู้ว่าอะไรคือคำตอบที่เหมาะสม โดยทั่วไป DECIMAL จะจัดเก็บตัวเลขที่ใหญ่กว่าและ libs คณิตศาสตร์เป็นไปตามความคาดหวังของนักบัญชีในขณะที่ double float เป็นสื่อบันทึกข้อมูลที่มีประสิทธิภาพน้อยกว่าซึ่งมี libs คณิตศาสตร์ที่เหมาะสมที่สุด
user2548100

21

MySQL เพิ่งเปลี่ยนวิธีที่พวกเขาพวกเขาเก็บประเภททศนิยม ในอดีตพวกเขาเก็บอักขระ (หรือ nybbles) สำหรับแต่ละหลักประกอบด้วยการแทน ASCII (หรือ nybble) ของตัวเลข - vs - จำนวนเต็มประกอบของสองหรือบางส่วนของอนุพันธ์

รูปแบบการจัดเก็บปัจจุบันสำหรับ DECIMAL คือชุดของจำนวนเต็ม 1,2,3 หรือ 4 ไบต์ซึ่งบิตถูกต่อกันเพื่อสร้างหมายเลขส่วนเติมเต็มของสองด้วยจุดทศนิยมโดยนัยที่คุณกำหนดและเก็บไว้ใน DB schema เมื่อคุณประกาศ คอลัมน์และระบุว่าเป็นขนาด DECIMAL และตำแหน่งจุดทศนิยม

ตัวอย่างเช่นถ้าคุณใช้ int แบบ 32 บิตคุณสามารถเก็บหมายเลขใด ๆ ได้ตั้งแต่ 0 - 4,294,967,295 นั่นจะครอบคลุม 999,999,999 อย่างน่าเชื่อถือดังนั้นถ้าคุณขว้าง 2 บิตและใช้ (1 << 30 -1) คุณจะไม่ยอมแพ้ การครอบคลุมตัวเลข 9 หลักทั้งหมดที่มีเพียง 4 ไบต์จะมีประสิทธิภาพมากกว่าการครอบคลุม 4 หลักใน 32 บิตโดยใช้อักขระ ASCII 4 ตัวหรือตัวเลข 8 nybble (nybble คือ 4 บิตอนุญาตให้มีค่า 0-15 มากกว่าที่จำเป็นสำหรับ 0-9 แต่คุณไม่สามารถกำจัดของเสียนั้นได้โดยไปที่ 3 บิตเนื่องจากครอบคลุมเฉพาะค่า 0-7)

ตัวอย่างที่ใช้ในเอกสารออนไลน์ MySQL ใช้ DECIMAL (18,9) เป็นตัวอย่าง นี่คือตัวเลข 9 หลักก่อนหน้าและ 9 หลักหลังจุดทศนิยมโดยนัยซึ่งตามที่อธิบายไว้ข้างต้นจำเป็นต้องใช้ที่เก็บข้อมูลต่อไปนี้

ในฐานะที่เป็น 18 ตัวอักษร 8 บิต: 144 บิต

ในฐานะที่เป็น 18 4 บิต nybbles: 72 บิต

ในฐานะที่เป็น 2 จำนวนเต็ม 32 บิต: 64 บิต

ปัจจุบัน DECIMAL รองรับได้สูงสุด 65 หลักเนื่องจาก DECIMAL (M, D) ซึ่งค่าที่ใหญ่ที่สุดสำหรับ M ที่อนุญาตคือ 65 และค่า D ที่อนุญาตสูงสุดคือ 30

เพื่อไม่ให้ต้องใช้จำนวน 9 หลักในแต่ละครั้งจำนวนเต็มที่น้อยกว่า 32 บิตจะถูกใช้เพื่อเพิ่มตัวเลขโดยใช้จำนวนเต็ม 1,2 และ 3 ไบต์ ด้วยเหตุผลบางอย่างที่ขัดแย้งกับลอจิกลงชื่อแทนที่จะใช้ ints ที่ไม่ได้ลงชื่อและใช้วิธีนี้ 1 บิตถูกโยนออกไปทำให้เกิดความสามารถในการจัดเก็บต่อไปนี้ สำหรับ 1,2 และ 4 ไบต์ ints บิตที่หายไปไม่สำคัญ แต่สำหรับ 3 ไบต์ของไบต์นั้นเป็นหายนะเพราะตัวเลขทั้งหมดหายไปเนื่องจากการสูญเสียบิตเดียวนั้น

ด้วย int 7 บิต: 0 - 99

ด้วย int 15 บิต: 0 - 9,999

ด้วย 23- บิต int: 0 - 999,999 (0 - 9,999,999 กับ 24- บิต int)

จำนวนเต็ม 1,2,3 และ 4 ไบต์ถูกรวมเข้าด้วยกันเพื่อสร้าง "bit pool" DECIMAL ใช้เพื่อแสดงตัวเลขอย่างแม่นยำว่าเป็นจำนวนเต็มส่วนเติมเต็มของทั้งสอง จุดทศนิยมจะไม่ถูกเก็บไว้โดยนัย

ซึ่งหมายความว่าไม่จำเป็นต้องใช้ ASCII ไปยังการแปลง int เพื่อให้เอ็นจิ้น DB แปลง "number" เป็นสิ่งที่ CPU จำได้ว่าเป็นตัวเลข ไม่มีการปัดเศษไม่มีข้อผิดพลาดในการแปลงเป็นจำนวนจริงที่ CPU สามารถจัดการได้

การคำนวณจำนวนเต็มขนาดใหญ่โดยพลการนี้จะต้องทำในซอฟต์แวร์เนื่องจากไม่มีการสนับสนุนฮาร์ดแวร์สำหรับหมายเลขชนิดนี้ แต่ไลบรารี่เหล่านี้เก่าและเหมาะสมที่สุดถูกเขียนขึ้นเมื่อ 50 ปีที่แล้วเพื่อรองรับข้อมูลจุดลอยตัวที่แม่นยำของ IBM 370 Fortran . พวกมันยังช้ากว่าพีชคณิตจำนวนเต็มคงที่ที่ทำกับฮาร์ดแวร์จำนวนเต็มของ CPU หรือการคำนวณจุดลอยตัวที่ทำใน FPU

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

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

ตามความคิดสุดท้ายหากคุณต้องการการคำนวณจุดลอยตัวที่มีความแม่นยำสูงมีความก้าวหน้าอย่างมากในรหัสจุดลอยตัวในรอบ 20 ปีที่ผ่านมาและการสนับสนุนฮาร์ดแวร์สำหรับโฟลต 96 บิตและQuadruple Precisionนั้นอยู่ใกล้ ๆ แต่จะมีไลบรารี่ที่มีความแม่นยำตามอำเภอใจดีหากการจัดการค่าที่เก็บไว้นั้นมีความสำคัญ


ฉันเชื่อว่าสถาปัตยกรรม Hazwell ของ Intel มีการทำงาน AVX-2 ในจำนวนเต็ม 256 บิตที่ครอบคลุมทุกค่า 77 หลักที่เป็นไปได้ซึ่งสามารถนำไปใช้โดยตรงกับจำนวนเต็มที่แม่นยำของ DECIMAL อาจพิสูจน์ให้รอบคอบว่า Oracle สนับสนุนรูปแบบใหม่ของ DECIMAL ในอนาคตซึ่งครอบคลุมตัวเลข 77 หลักเทียบกับ 65 ฉันคาดว่าจะมีการปรับปรุงประสิทธิภาพ 5-10 เท่าโดยใช้ฮาร์ดแวร์แทนซอฟต์แวร์ 2 ^ 256 = 115.792.089.237.316.195.423.570.985.008.687.907.853.269.984.665.640.564.039.457.584.007.913.129, 639,936 (78 หลัก)

โปรเซสเซอร์เวกเตอร์ของ Intel รองรับการทำงานทางคณิตศาสตร์ 512 บิต ซึ่งจะครอบคลุม 154 หลัก 2 ^ 512 = 13.407.807.929.942.597.099.574.024.998.205.846.127.479.365.820.592.393.377.723.561.443.721.764.030.073.546.976.801.874.298.166.903.427.690.031.858.186.486.050.853.753.882.811.946.569.946.433.649.006.084.096 (155 หลัก)

13

ไม่เฉพาะเจาะจงกับ MySQL เท่านั้นความแตกต่างระหว่างประเภททศนิยมและทศนิยมเป็นวิธีที่พวกเขาแสดงค่าเศษส่วน {m*2^n | m, n Integers}ลอยชนิดจุดแทนเศษส่วนในไบนารีซึ่งสามารถแสดงค่าเป็น ค่าเช่น 1/5 ไม่สามารถแสดงได้อย่างแม่นยำ (ไม่มีข้อผิดพลาดในการปัดเศษ) ตัวเลขทศนิยมจะถูก จำกัด ในทำนองเดียวกัน {m*10^n | m, n Integers}แต่เป็นตัวแทนของตัวเลขเช่น ทศนิยมยังไม่สามารถแสดงตัวเลขได้เช่น 1/3 แต่บ่อยครั้งในหลาย ๆ เขตข้อมูลทั่วไปเช่นการเงินความคาดหวังก็คือเศษส่วนทศนิยมบางอย่างสามารถแสดงได้เสมอโดยไม่สูญเสียความน่าเชื่อถือ เนื่องจากตัวเลขทศนิยมสามารถแทนค่าเช่น$0.20(หนึ่งในห้าของดอลลาร์) จึงเป็นที่ต้องการในสถานการณ์เหล่านั้น


เนื่องจากโปรเซสเซอร์ของ Intel ดำเนินการแบบ Double Float ระดับกลางทั้งหมดด้วยความแม่นยำ 80 บิตจึงแทบไม่มีข้อยกเว้นในการปัดเศษเมื่อผลลัพธ์สุดท้ายถูกตัดกลับจาก 80 บิตเป็น 64 บิต แม้แต่ไลบรารีซอฟต์แวร์แบบ floating point จำนวนมากก็สามารถจัดการสิ่งเหล่านี้และความผิดปกติทางคณิตศาสตร์อื่น ๆ อีกนับร้อย ทฤษฎีและการปฏิบัติจึงแตกต่างกันอย่างดุเดือดในพื้นที่นี้

9

ทศนิยมสำหรับปริมาณคงที่เช่นเงินที่คุณต้องการจำนวนทศนิยมเฉพาะ ทุ่นลอยสำหรับการจัดเก็บ ... ตัวเลขความแม่นยำจุดลอยตัว


6

ฉันพบว่ามีประโยชน์นี้:

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

ที่มา: http://code.rohitink.com/2013/06/12/mysql-integer-float-decimal-data-types-differences/


5
mysql> CREATE TABLE num(id int ,fl float,dc dec(5,2));
Query OK, 0 rows affected (0.00 sec)


mysql> INSERT INTO num VALUES(1,13.75,13.75);
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO num VALUES(2,13.15,13.15);
Query OK, 1 row affected (0.00 sec)

mysql> SELECT * FROM num WHERE fl = 13.15;
Empty set (0.00 sec)

mysql> SELECT * FROM num WHERE dc = 13.15;
+------+-------+-------+
| id   | fl    | dc    |
+------+-------+-------+
|    2 | 13.15 | 13.15 |
+------+-------+-------+
1 row in set (0.00 sec)

mysql> SELECT SUM(fl) ,SUM(dc)  FROM num;
+--------------------+---------+
| SUM(fl)            | SUM(dc) |
+--------------------+---------+
| 26.899999618530273 |   26.90 |
+--------------------+---------+
1 row in set (0.00 sec)


mysql> SELECT * FROM num WHERE ABS(fl -  13.15)<0.01;
+------+-------+-------+
| id   | fl    | dc    |
+------+-------+-------+
|    2 | 13.15 | 13.15 |
+------+-------+-------+
1 row in set (0.00 sec)

2

หากคุณอยู่หลังการทำงานและไม่แม่นยำคุณควรทราบว่าการคำนวณด้วยการลอยนั้นเร็วกว่าทศนิยม


2

ประเภทจุดลอยตัว (ค่าโดยประมาณ) - ลอยสองเท่า

ชนิด FLOAT และ DOUBLE แสดงค่าข้อมูลตัวเลขโดยประมาณ MySQL ใช้สี่ไบต์สำหรับค่าความแม่นยำเดียวและแปดไบต์สำหรับค่าความแม่นยำสองเท่า

สำหรับ FLOAT มาตรฐาน SQL จะอนุญาตให้ใช้ข้อกำหนดทางเลือกของความแม่นยำ (แต่ไม่ใช่ช่วงของเลขชี้กำลัง) เป็นบิตตามด้วยคำสำคัญ FLOAT ในวงเล็บ MySQL ยังรองรับข้อกำหนดความแม่นยำที่เป็นตัวเลือกนี้ แต่ค่าความแม่นยำนั้นใช้เพื่อกำหนดขนาดการจัดเก็บเท่านั้น ความแม่นยำตั้งแต่ 0 ถึง 23 ผลลัพธ์ในคอลัมน์ FLOAT ความแม่นยำเดี่ยว 4 ไบต์ ความแม่นยำตั้งแต่ 24 ถึง 53 ส่งผลให้มีคอลัมน์ DOUBLE ที่มีความแม่นยำสองเท่า 8 ไบต์

MySQL อนุญาตให้ใช้ไวยากรณ์ที่ไม่เป็นมาตรฐาน: FLOAT (M, D) หรือ REAL (M, D) หรือความแม่นยำสองเท่า (M, D) ในที่นี้“ (M, D)” หมายถึงค่าที่สามารถจัดเก็บได้ด้วยจำนวน M ถึงทั้งหมดซึ่ง D หลักอาจอยู่หลังจุดทศนิยม ตัวอย่างเช่นคอลัมน์ที่กำหนดเป็น FLOAT (7,4) จะมีลักษณะเช่น -999.9999 เมื่อแสดง MySQL ทำการปัดเศษเมื่อเก็บค่าดังนั้นหากคุณแทรก 999.00009 ลงในคอลัมน์ FLOAT (7,4) ผลลัพธ์จะอยู่ที่ประมาณ 999.0001

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

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

https://dev.mysql.com/doc/refman/5.5/en/floating-point-types.html

ปัญหาเกี่ยวกับค่า Floating-Point

จำนวนจุดลอยตัวบางครั้งทำให้เกิดความสับสนเพราะพวกเขาเป็นตัวอย่างและไม่ได้เก็บไว้เป็นค่าที่แน่นอน ค่า floating-point ตามที่เขียนในคำสั่ง SQL อาจไม่เหมือนกันกับค่าที่แสดงภายใน ความพยายามในการปฏิบัติต่อค่าจุดลอยตัวที่แน่นอนในการเปรียบเทียบอาจนำไปสู่ปัญหา พวกเขายังขึ้นอยู่กับการพึ่งพาแพลตฟอร์มหรือการใช้งาน ชนิดข้อมูล FLOAT และ DOUBLE อาจมีปัญหาเหล่านี้ สำหรับคอลัมน์ DECIMAL, MySQL ทำการดำเนินการด้วยความแม่นยำของตัวเลข 65 หลักซึ่งควรแก้ปัญหาความไม่ถูกต้องที่พบบ่อยที่สุด

ตัวอย่างต่อไปนี้ใช้ DOUBLE เพื่อแสดงให้เห็นว่าการคำนวณที่ดำเนินการโดยใช้การดำเนินการจุดลอยตัวนั้นอยู่ภายใต้ข้อผิดพลาดจุดลอย

mysql> CREATE TABLE t1 (i INT, d1 DOUBLE, d2 DOUBLE);
mysql> INSERT INTO t1 VALUES (1, 101.40, 21.40), (1, -80.00, 0.00),
    -> (2, 0.00, 0.00), (2, -13.20, 0.00), (2, 59.60, 46.40),
    -> (2, 30.40, 30.40), (3, 37.00, 7.40), (3, -29.60, 0.00),
    -> (4, 60.00, 15.40), (4, -10.60, 0.00), (4, -34.00, 0.00),
    -> (5, 33.00, 0.00), (5, -25.80, 0.00), (5, 0.00, 7.20),
    -> (6, 0.00, 0.00), (6, -51.40, 0.00);

mysql> SELECT i, SUM(d1) AS a, SUM(d2) AS b
    -> FROM t1 GROUP BY i HAVING a <> b;

+------+-------+------+
| i    | a     | b    |
+------+-------+------+
|    1 |  21.4 | 21.4 |
|    2 |  76.8 | 76.8 |
|    3 |   7.4 |  7.4 |
|    4 |  15.4 | 15.4 |
|    5 |   7.2 |  7.2 |
|    6 | -51.4 |    0 |
+------+-------+------+

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

หากคอลัมน์ d1 และ d2 ถูกกำหนดเป็น DECIMAL แทนที่จะเป็น DOUBLE ผลลัพธ์ของแบบสอบถาม SELECT จะมีเพียงหนึ่งแถวเท่านั้นซึ่งเป็นแถวสุดท้ายที่แสดงด้านบน

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

mysql> SELECT i, SUM(d1) AS a, SUM(d2) AS b FROM t1
    -> GROUP BY i HAVING ABS(a - b) > 0.0001;
+------+-------+------+
| i    | a     | b    |
+------+-------+------+
|    6 | -51.4 |    0 |
+------+-------+------+
1 row in set (0.00 sec)

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

mysql> SELECT i, SUM(d1) AS a, SUM(d2) AS b FROM t1
    -> GROUP BY i HAVING ABS(a - b) <= 0.0001;
+------+------+------+
| i    | a    | b    |
+------+------+------+
|    1 | 21.4 | 21.4 |
|    2 | 76.8 | 76.8 |
|    3 |  7.4 |  7.4 |
|    4 | 15.4 | 15.4 |
|    5 |  7.2 |  7.2 |
+------+------+------+
5 rows in set (0.03 sec)

ค่าจุดลอยตัวอาจขึ้นอยู่กับการพึ่งพาแพลตฟอร์มหรือการนำไปปฏิบัติ สมมติว่าคุณดำเนินการคำสั่งต่อไปนี้:

CREATE TABLE t1(c1 FLOAT(53,0), c2 FLOAT(53,0));
INSERT INTO t1 VALUES('1e+52','-1e+52');
SELECT * FROM t1;

ในบางแพลตฟอร์มคำสั่ง SELECT จะส่งกลับค่า inf และ -inf สำหรับคนอื่น ๆ มันจะส่งคืน 0 และ -0

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

https://dev.mysql.com/doc/refman/5.5/en/problems-with-float.html


0

กฎฮาร์ด & เร็ว

หากสิ่งที่คุณต้องทำคือการเพิ่มลบหรือทวีคูณตัวเลขที่คุณเก็บไว้ DECIMAL นั้นดีที่สุด

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

สำหรับความถูกต้องฉันเคยเขียนระบบงบประมาณที่คำนวณ% การมีส่วนร่วมของบัญชี 3,000+ บัญชีสำหรับ 3,600 หน่วยต่อเดือนไปยังโหนดการรวมหน่วยของเดือนนั้นจากนั้นขึ้นอยู่กับเมทริกซ์ของเปอร์เซ็นต์ (3,000 + x 12 x 3,600) ฉันคูณจำนวนเงินที่งบประมาณโดยโหนดองค์กรสูงสุดลงไปอีก 3 ระดับถัดไปของโหนดองค์กรและคำนวณค่าทั้งหมด (3,000 +12) สำหรับหน่วยรายละเอียดทั้งหมด 3,200 หน่วยจากนั้น การคำนวณจุดลอยตัวที่มีความแม่นยำสองล้านล้านและล้านรายการใดรายการหนึ่งจะทำให้การประมาณการทั้งหมดเหล่านี้หมดไปในการรวมกลุ่มแบบกลุ่มย่อยกลับไปสู่ระดับสูงสุดในองค์กร

ข้อผิดพลาดที่จุดรวมลอยหลังจากทั้งหมดของการคำนวณนั้นคือZERO นั่นคือในปี 1986 และห้องสมุดจุดลอยตัวในวันนี้นั้นดีกว่าเมื่อก่อนมาก Intel ทำการคำนวณในระดับกลางทั้งหมดเป็นสองเท่าด้วยความแม่นยำ 80 บิตซึ่งทั้งหมดยกเว้นกำจัดข้อผิดพลาดในการปัดเศษ เมื่อมีคนบอกคุณว่า "มันเป็นข้อผิดพลาดของจุดลอยตัว" ก็เกือบจะไม่แน่นอน



-2
declare @float as float(10)
declare @Decimal as decimal(10)
declare @Inetger as int

set @float =10.7
set @Decimal =10.7
set @Inetger=@Decimal

print @Inetger

ใน float เมื่อตั้งค่าเป็นจำนวนเต็มพิมพ์ 10 แต่ในทศนิยม 11

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