เหตุใด C จึงไม่มีลอยที่ไม่ได้ลงชื่อ


131

ฉันรู้ว่าคำถามดูเหมือนจะแปลก โปรแกรมเมอร์บางครั้งก็คิดมากเกินไป โปรดอ่านต่อ ...

ใน CI ใช้signedและunsignedจำนวนเต็มมาก ฉันชอบข้อเท็จจริงที่ว่าคอมไพลเลอร์เตือนฉันหากฉันทำสิ่งต่างๆเช่นการกำหนดจำนวนเต็มที่เซ็นชื่อให้กับตัวแปรที่ไม่ได้ลงชื่อ ฉันได้รับคำเตือนหากเปรียบเทียบเซ็นกับจำนวนเต็มที่ไม่ได้ลงชื่อและอื่น ๆ อีกมากมาย

ฉันชอบคำเตือนเหล่านี้ พวกเขาช่วยฉันรักษารหัสของฉันให้ถูกต้อง

ทำไมเราไม่มีความหรูหราเหมือนกันสำหรับลอย? รากที่สองจะไม่ส่งกลับจำนวนลบอย่างแน่นอน มีสถานที่อื่น ๆ เช่นกันที่ค่าการลอยตัวเชิงลบไม่มีความหมาย ตัวเลือกที่สมบูรณ์แบบสำหรับการลอยตัวที่ไม่ได้ลงนาม

Btw - ฉันไม่ค่อยสนใจเกี่ยวกับความแม่นยำพิเศษเพียงเล็กน้อยที่ฉันจะได้รับโดยการลบบิตเครื่องหมายออกจากโฟลต ฉันมีความสุขมากกับfloatพวกเขาในขณะนี้ ฉันแค่อยากจะทำเครื่องหมายลอยว่าไม่ได้ลงนามในบางครั้งและรับคำเตือนแบบเดียวกับที่ฉันได้รับจากจำนวนเต็ม

ฉันไม่ทราบภาษาโปรแกรมใด ๆ ที่รองรับตัวเลขทศนิยมที่ไม่ได้ลงชื่อ

มีความคิดว่าทำไมพวกเขาถึงไม่มี?


แก้ไข:

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


4
น่าสนใจคุณสามารถโพสต์ตัวอย่างกรณีที่การตรวจสอบการพิมพ์ลายเซ็นมีประโยชน์หรือไม่

litb ความคิดเห็นของคุณพุ่งตรงมาที่ฉันหรือเปล่า ถ้าเป็นเช่นนั้นฉันไม่เข้าใจ

Iraimbilanja ใช่ :) fabs ไม่สามารถคืนค่าจำนวนลบได้เพราะจะส่งคืนค่าสัมบูรณ์ของอาร์กิวเมนต์
Johannes Schaub - litb

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

1
มีการเพิ่มประสิทธิภาพไมโครที่ไม่ได้ลงชื่อสำหรับการตรวจสอบจุดในช่วง: ((ไม่ได้ลงนาม) (p-min)) <(max-min) ซึ่งมีเพียงสาขาเดียว แต่เช่นเคยควรตั้งค่าโปรไฟล์เพื่อดูว่า มันช่วยได้มาก (ส่วนใหญ่ฉันใช้กับ 386 คอร์ดังนั้นฉันไม่รู้ว่าซีพียูสมัยใหม่รับมือได้อย่างไร)
Skizz

คำตอบ:


114

เหตุใด C ++ จึงไม่รองรับการลอยตัวที่ไม่ได้ลงชื่อเนื่องจากไม่มีการทำงานของรหัสเครื่องที่เทียบเท่าสำหรับ CPU ในการดำเนินการ ดังนั้นมันจะไม่มีประสิทธิภาพมากที่จะสนับสนุนมัน

หาก C ++ รองรับบางครั้งคุณอาจใช้โฟลตที่ไม่ได้ลงนามและไม่ทราบว่าประสิทธิภาพของคุณเพิ่งถูกฆ่า หาก C ++ รองรับการดำเนินการทศนิยมทุกครั้งจะต้องได้รับการตรวจสอบเพื่อดูว่ามีการลงนามหรือไม่ และสำหรับโปรแกรมที่ดำเนินการกับทศนิยมหลายล้านรายการสิ่งนี้ไม่สามารถยอมรับได้

ดังนั้นคำถามก็คือทำไมผู้ใช้ฮาร์ดแวร์ไม่สนับสนุน และฉันคิดว่าคำตอบก็คือไม่มีมาตรฐานลอยตัวที่ไม่ได้ลงนามที่กำหนดไว้ในตอนแรก เนื่องจากภาษาต่างๆชอบที่จะใช้งานร่วมกันได้แม้ว่าจะมีการเพิ่มภาษาก็ไม่สามารถใช้ประโยชน์ได้ หากต้องการดูจุดลอย spec ที่คุณควรจะดูที่มาตรฐาน IEEE 754 Floating-Point

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

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


18
C / C ++ ไม่ต้องการการใช้งานรหัสเครื่องเฉพาะเพื่อใช้ภาษา คอมไพเลอร์ C / C ++ ในยุคแรกสามารถสร้างรหัสทศนิยมสำหรับ 386 ซึ่งเป็น CPU ที่ไม่มี FPU! คอมไพเลอร์จะสร้างการเรียกใช้ไลบรารีเพื่อเลียนแบบคำสั่ง FPU ดังนั้น ufloat สามารถทำได้โดยไม่ต้องรองรับ CPU
Skizz

10
Skizz ในขณะที่ถูกต้อง Brian ได้พูดถึงเรื่องนี้แล้ว - เนื่องจากไม่มีรหัสเครื่องที่เทียบเท่าประสิทธิภาพจะแย่เมื่อเปรียบเทียบ
Anthony

2
@Brian R.Bondy: ฉันเสียคุณที่นี่: "เพราะไม่มีการทำงานของรหัสเครื่องที่เทียบเท่าสำหรับ CPU ในการดำเนินการ ... " คุณช่วยอธิบายในแง่ที่ง่ายกว่านี้ได้ไหม
Lazer

2
เหตุผลที่ OP ต้องการการสนับสนุนสำหรับการลอยตัวที่ไม่ได้ลงชื่อนั้นมีไว้สำหรับข้อความเตือนดังนั้นจริงๆแล้วมันไม่มีส่วนเกี่ยวข้องกับขั้นตอนการสร้างรหัสของคอมไพเลอร์ - เกี่ยวข้องกับการตรวจสอบประเภทล่วงหน้าเท่านั้นดังนั้นการสนับสนุนในรหัสเครื่องจึงไม่เกี่ยวข้อง และ (ตามที่ได้เพิ่มไว้ที่ด้านล่างของคำถาม) คำแนะนำจุดลอยตัวปกติสามารถใช้สำหรับการดำเนินการจริง
Joe F

2
ฉันไม่แน่ใจว่าทำไมสิ่งนี้จึงส่งผลต่อประสิทธิภาพ เช่นเดียวกับintของการตรวจสอบประเภทที่เกี่ยวข้องกับการลงชื่อทั้งหมดอาจเกิดขึ้นในเวลาคอมไพล์ OP แนะนำว่าunsigned floatจะดำเนินการตามปกติfloatโดยมีการตรวจสอบเวลาคอมไพล์เพื่อให้แน่ใจว่าจะไม่มีการดำเนินการบางอย่างที่ไม่ได้มีความหมาย รหัสเครื่องที่ได้และประสิทธิภาพอาจเหมือนกันไม่ว่าคุณจะเซ็นชื่อลอยหรือไม่ก็ตาม
xanderflood

14

มีความแตกต่างอย่างมีนัยสำคัญระหว่างจำนวนเต็มที่ลงชื่อและไม่ได้ลงชื่อใน C / C ++:

value >> shift

ค่าที่ลงชื่อจะปล่อยให้บิตบนสุดไม่เปลี่ยนแปลง (เข้าสู่ระบบขยาย) ค่าที่ไม่ได้ลงชื่อจะล้างบิตบนสุด

เหตุผลที่ไม่มีการลอยตัวที่ไม่ได้ลงนามคือคุณพบปัญหาทุกประเภทอย่างรวดเร็วหากไม่มีค่าติดลบ พิจารณาสิ่งนี้:

float a = 2.0f, b = 10.0f, c;
c = a - b;

c มีค่าอะไร? -8 แต่นั่นหมายความว่าอย่างไรในระบบที่ไม่มีจำนวนลบ FLOAT_MAX - 8 บางที? อันที่จริงมันใช้ไม่ได้เนื่องจาก FLOAT_MAX - 8 คือ FLOAT_MAX เนื่องจากเอฟเฟกต์ความแม่นยำดังนั้นสิ่งต่างๆจึงน่าขันยิ่งขึ้น จะเป็นอย่างไรหากเป็นส่วนหนึ่งของนิพจน์ที่ซับซ้อนมากขึ้น:

float a = 2.0f, b = 10.0f, c = 20.0f, d = 3.14159f, e;
e = (a - b) / d + c;

นี่ไม่ใช่ปัญหาสำหรับจำนวนเต็มเนื่องจากลักษณะของระบบเสริมของ 2

นอกจากนี้ให้พิจารณาฟังก์ชันทางคณิตศาสตร์มาตรฐาน: sin, cos และ tan จะใช้ได้เพียงครึ่งเดียวของค่าอินพุตคุณไม่พบบันทึกของค่า <1 คุณไม่สามารถแก้สมการกำลังสองได้: x = (-b +/- root ( bb - 4.ac)) / 2.a และอื่น ๆ ในความเป็นจริงมันอาจจะใช้ไม่ได้กับฟังก์ชันที่ซับซ้อนใด ๆ เนื่องจากสิ่งเหล่านี้มีแนวโน้มที่จะถูกนำไปใช้เป็นการประมาณพหุนามซึ่งจะใช้ค่าลบที่ใดที่หนึ่ง

ดังนั้นการลอยตัวที่ไม่ได้ลงนามจึงค่อนข้างไร้ประโยชน์

แต่ไม่ได้หมายความว่าคลาสที่ช่วงตรวจสอบค่าลอยไม่มีประโยชน์คุณอาจต้องการยึดค่ากับช่วงที่กำหนดเช่นการคำนวณ RGB


@Skizz: ถ้าการเป็นตัวแทนเป็นปัญหาคุณหมายความว่าถ้าใครสักคนสามารถคิดค้นวิธีการจัดเก็บลอยตัวที่มีประสิทธิภาพเท่า2's complementๆ กันจะไม่มีปัญหากับการลอยตัวที่ไม่ได้ลงชื่อ?
Lazer

3
value >> shift for signed values leave the top bit unchanged (sign extend) คุณแน่ใจหรือไม่? ฉันคิดว่านั่นเป็นพฤติกรรมที่กำหนดไว้สำหรับการนำไปใช้งานอย่างน้อยก็สำหรับค่าที่มีการลงนามเชิงลบ
แดน

@ แดน: เพิ่งดูมาตรฐานล่าสุดและระบุว่ามีการกำหนดการใช้งานจริง ๆ - ฉันเดาว่าในกรณีที่มีซีพียูที่ไม่มีการเลื่อนอย่างถูกต้องพร้อมคำแนะนำในการขยายเครื่องหมาย
Skizz


1
จุดลอยตัวจะอิ่มตัว (ถึง - / + Inf) แทนการห่อ คุณอาจคาดหวังว่าการลบที่ไม่ได้ลงชื่อจะล้นจนอิ่มตัว0.0หรืออาจเป็น Inf หรือ NaN หรือเป็นเพียงพฤติกรรมที่ไม่ได้กำหนดเช่น OP ที่แนะนำในการแก้ไขคำถาม ฟังก์ชัน Re: Trig: ดังนั้นอย่ากำหนดเวอร์ชันอินพุตที่ไม่ได้ลงชื่อsinและอื่น ๆ และตรวจสอบให้แน่ใจว่าได้ปฏิบัติตามค่าที่ส่งคืนตามที่ลงชื่อแล้ว คำถามไม่ได้เสนอให้แทนที่ float ด้วย float ที่ไม่ได้ลงชื่อเพียงแค่เพิ่มunsigned floatเป็นประเภทใหม่
Peter Cordes

9

(นอกจากนี้ Perl 6 ยังให้คุณเขียน

subset Nonnegative::Float of Float where { $_ >= 0 };

จากนั้นคุณสามารถใช้Nonnegative::Floatเช่นเดียวกับที่คุณทำประเภทอื่น ๆ )

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

[แก้ไข]

C ก็เหมือนกับการประกอบ: สิ่งที่คุณเห็นคือสิ่งที่คุณได้รับ โดยนัย "ฉันจะตรวจสอบว่าลอยนี้ไม่เป็นค่าลบสำหรับคุณ" ขัดต่อปรัชญาการออกแบบ ถ้าคุณต้องการจริงๆคุณสามารถเพิ่มassert(x >= 0)หรือคล้ายกันได้ แต่คุณต้องทำอย่างชัดเจน


svn.perl.org/parrot/trunk/languages/perl6/docs/STATUSบอกว่าใช่ แต่of ...ไม่แยกวิเคราะห์
ephemient

8

ฉันเชื่อว่า int ที่ไม่ได้ลงนามถูกสร้างขึ้นเนื่องจากความต้องการส่วนต่างมูลค่าที่มากกว่าที่ int ที่ลงนามสามารถเสนอได้

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

แก้ไข: หลังจากอ่านคำตอบของ Brian R.Bondyแล้วฉันต้องแก้ไขคำตอบของฉัน: เขาพูดถูกอย่างแน่นอนว่าซีพียูพื้นฐานไม่มีการดำเนินการลอยที่ไม่ได้ลงนาม อย่างไรก็ตามฉันยังคงเชื่อว่านี่เป็นการตัดสินใจออกแบบตามเหตุผลที่ระบุไว้ข้างต้น ;-)


2
นอกจากนี้การบวกและการลบจำนวนเต็มยังมีเครื่องหมายเดียวกันหรือไม่ได้ลงนาม - จุดลอยตัวไม่มากนัก ใครจะทำงานพิเศษเพื่อรองรับการลอยตัวทั้งแบบลงนามและไม่ได้ลงนามเนื่องจากยูทิลิตีส่วนเพิ่มที่ค่อนข้างต่ำของคุณลักษณะดังกล่าว
ephemient

7

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

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


5

รากที่สองจะไม่ส่งกลับจำนวนลบอย่างแน่นอน มีสถานที่อื่น ๆ เช่นกันที่ค่าการลอยตัวเชิงลบไม่มีความหมาย ตัวเลือกที่สมบูรณ์แบบสำหรับการลอยตัวที่ไม่ได้ลงนาม

C99 รองรับจำนวนเชิงซ้อนและรูปแบบทั่วไปของ sqrt ดังนั้นsqrt( 1.0 * I)จะเป็นค่าลบ


ผู้แสดงความคิดเห็นเน้นความเงาเล็กน้อยด้านบนโดยที่ฉันอ้างถึงsqrtมาโครประเภททั่วไปแทนที่จะเป็นฟังก์ชันและจะส่งคืนค่าจุดลอยตัวแบบสเกลาร์โดยการตัดทอนคอมเพล็กซ์ไปเป็นองค์ประกอบจริง:

#include <complex.h>
#include <tgmath.h>

int main () 
{
    complex double a = 1.0 + 1.0 * I;

    double f = sqrt(a);

    return 0;
}

นอกจากนี้ยังมีสมอง - ผายลมเนื่องจากส่วนจริงของ sqrt ของจำนวนเชิงซ้อนใด ๆ เป็นบวกหรือศูนย์และ sqrt (1.0 * I) คือ sqrt (0.5) + sqrt (0.5) * ฉันไม่ใช่ -1.0


ใช่ แต่คุณเรียกใช้ฟังก์ชันด้วยชื่ออื่นถ้าคุณทำงานกับจำนวนเชิงซ้อน ประเภทผลตอบแทนยังแตกต่างกัน จุดดี!
Nils Pipenbrinck

4
ผลลัพธ์ของ sqrt (i) คือจำนวนเชิงซ้อน และเนื่องจากไม่ได้เรียงลำดับจำนวนเชิงซ้อนคุณจึงไม่สามารถพูดได้ว่าจำนวนเชิงซ้อนเป็นเชิงลบ (เช่น <0)
วินมาร์

1
quinmars แน่ใจว่าไม่ใช่ csqrt? หรือคุณพูดถึงคณิตศาสตร์แทน C? ฉันยอมรับว่ามันเป็นจุดที่ดี :)
Johannes Schaub - litb

อันที่จริงฉันกำลังพูดถึงคณิตศาสตร์ ฉันไม่เคยจัดการกับจำนวนเชิงซ้อนใน c
วินมาร์

1
"รากที่สองจะไม่ส่งกลับจำนวนลบอย่างแน่นอน" -> มักจะก่อให้เกิดsqrt(-0.0) -0.0แน่นอน -0.0 ไม่ได้เป็นเชิงลบค่า
chux - คืนสถานะ Monica

4

ฉันเดาว่ามันขึ้นอยู่กับว่ามีการลงนามข้อกำหนดจุดลอยตัวของ IEEE เท่านั้นและภาษาโปรแกรมส่วนใหญ่ใช้

บทความ Wikipedia เกี่ยวกับเลขทศนิยมของ IEEE-754

แก้ไข: ตามที่ผู้อื่นระบุไว้ฮาร์ดแวร์ส่วนใหญ่ไม่รองรับการลอยตัวแบบไม่ติดลบดังนั้นการลอยแบบปกติจึงมีประสิทธิภาพมากกว่าเนื่องจากมีการรองรับฮาร์ดแวร์


C ได้รับการแนะนำมานานก่อนที่มาตรฐาน IEEE-754 จะปรากฏขึ้น
phuclv

@phuclv ไม่ได้เป็นฮาร์ดแวร์จุดลอยตัวทั่วไป มันถูกนำมาใช้ในมาตรฐาน C "ไม่กี่ปี" ต่อมา อาจมีเอกสารบางอย่างลอยอยู่ในอินเทอร์เน็ตเกี่ยวกับเรื่องนี้ (นอกจากนี้บทความวิกิพีเดียยังกล่าวถึง C99)
Tobias Wärre

ฉันไม่เข้าใจว่าคุณหมายถึงอะไร คำตอบของคุณไม่มี "ฮาร์ดแวร์" และ IEEE-754 เกิดหลัง C ดังนั้นประเภททศนิยมใน C จึงไม่สามารถขึ้นอยู่กับมาตรฐาน IEEE-754 ได้เว้นแต่จะมีการนำประเภทเหล่านั้นมาใช้ใน C ในภายหลัง
phuclv

@phuclv C เป็น / รู้จักกันในชื่อแอสเซมบลีแบบพกพาดังนั้นจึงค่อนข้างใกล้เคียงกับฮาร์ดแวร์ ภาษาได้รับคุณสมบัติในช่วงหลายปีที่ผ่านมาแม้ว่าการลอยตัว (ก่อนเวลาของฉัน) จะถูกนำมาใช้ใน C แต่ก็น่าจะเป็นการใช้งานซอฟต์แวร์และมีราคาค่อนข้างแพง ในขณะที่ตอบคำถามนี้ฉันเห็นได้ชัดว่าฉันเข้าใจสิ่งที่ฉันพยายามอธิบายได้ดีกว่าตอนนี้ และถ้าคุณดูคำตอบที่ยอมรับคุณอาจเข้าใจว่าทำไมฉันถึงพูดถึงมาตรฐาน IEE754 สิ่งที่ฉันไม่เข้าใจคือคุณเลือกคำตอบ 10 ปีซึ่งไม่ใช่คำตอบที่ยอมรับ?
Tobias Wärre

3

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


4
PDP-7 ซึ่งเป็นแพลตฟอร์มแรกของ C มีหน่วยจุดลอยตัวของฮาร์ดแวร์ที่เป็นอุปกรณ์เสริม PDP-11 ซึ่งเป็นแพลตฟอร์มถัดไปของ C มีการลอยตัวแบบ 32 บิตในฮาร์ดแวร์ 80x86 มาในรุ่นต่อมาพร้อมกับเทคโนโลยีบางอย่างที่ล้าหลัง
ephemient

3

ประเภทจำนวนเต็มที่ไม่ได้ลงนามใน C ถูกกำหนดให้เป็นไปตามกฎของวงแหวนพีชคณิตนามธรรม ตัวอย่างเช่นสำหรับค่า X และ Y ใด ๆ การเพิ่ม XY ใน Y จะให้ผล X ประเภทจำนวนเต็มที่ไม่ได้ลงชื่อจะได้รับการรับรองว่าจะปฏิบัติตามกฎเหล่านี้ในทุกกรณีซึ่งไม่เกี่ยวข้องกับการแปลงเป็นหรือจากประเภทตัวเลขอื่นใด [หรือประเภทที่ไม่ได้ลงนามในขนาดต่างๆ] และการรับประกันนั้นเป็นหนึ่งในคุณสมบัติที่สำคัญที่สุดของประเภทดังกล่าว ในบางกรณีคุณควรละทิ้งความสามารถในการแสดงจำนวนลบเพื่อแลกกับการรับประกันพิเศษเฉพาะประเภทที่ไม่ได้ลงนามเท่านั้นที่สามารถให้ได้ ประเภทจุดลอยตัวไม่ว่าจะเซ็นชื่อหรือไม่ก็ตามไม่สามารถปฏิบัติตามกฎทั้งหมดของวงแหวนพีชคณิตได้ [เช่นไม่สามารถรับประกันได้ว่า X + YY จะเท่ากับ X] และ IEEE ทำไม่ได้ t ยังอนุญาตให้พวกเขาปฏิบัติตามกฎของคลาสความเท่าเทียมกัน [โดยกำหนดให้ค่าบางค่าเปรียบเทียบไม่เท่ากันกับตัวมันเอง] ฉันไม่คิดว่าประเภทจุดลอยตัวที่ "ไม่ได้ลงนาม" สามารถเป็นไปตามสัจพจน์ใด ๆ ที่ประเภทจุดลอยตัวธรรมดาไม่สามารถทำได้ดังนั้นฉันไม่แน่ใจว่าจะมีข้อดีอะไรบ้าง


1

IHMO เป็นเพราะการรองรับประเภทจุดลอยตัวทั้งแบบลงนามและไม่ได้ลงนามในฮาร์ดแวร์หรือซอฟต์แวร์จะเป็นปัญหาเกินไป

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

  • การเปลี่ยนเลขคณิตและตรรกะต้องการการเปลี่ยนแปลงเล็กน้อยในฟิลเลอร์สำหรับบิตด้านบน
  • ขยับขยายคูณสามารถใช้ฮาร์ดแวร์เดียวกันสำหรับส่วนหลักแล้วบางส่วนตรรกะที่แยกต่างหากเพื่อปรับผลในการเปลี่ยนแปลง signness ไม่ใช่ว่าใช้ในตัวคูณจริง แต่สามารถทำได้
  • เปรียบเทียบลงนามสามารถแปลงเปรียบเทียบไม่ได้ลงนามและในทางกลับกันได้อย่างง่ายดายด้วยการสลับบิตด้านบนหรือเพิ่ม INT_MINยังเป็นไปได้ในทางทฤษฎีอาจไม่ได้ใช้กับฮาร์ดแวร์ แต่มีประโยชน์กับระบบที่รองรับการเปรียบเทียบเพียงประเภทเดียว (เช่น 8080 หรือ 8051)

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

น่าเสียดายที่เราไม่หรูหราสำหรับประเภทจุดลอยตัว เพียงแค่ปล่อยเครื่องหมายบิตเราจะมีเวอร์ชันที่ไม่ได้ลงชื่อ แต่แล้วเราควรใช้บิตนั้นเพื่ออะไร?

  • เพิ่มช่วงโดยเพิ่มลงในเลขชี้กำลัง
  • เพิ่มความแม่นยำโดยเพิ่มเข้าไปในแมนทิสซา สิ่งนี้มักมีประโยชน์มากกว่าเนื่องจากโดยทั่วไปเราต้องการความแม่นยำมากกว่าช่วง

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

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

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

ที่กล่าวว่ามีรูปแบบจุดลอยตัวพิเศษที่ไม่ได้ลงชื่อหลายแบบโดยส่วนใหญ่มีวัตถุประสงค์เพื่อการประมวลผลภาพเช่นประเภทจุดลอยตัว 10 และ 11 บิตของกลุ่ม Khronos


0

ฉันสงสัยว่าเป็นเพราะโปรเซสเซอร์พื้นฐานที่กำหนดโดยคอมไพเลอร์ C ไม่มีวิธีจัดการกับตัวเลขทศนิยมที่ไม่ได้ลงชื่อ


โปรเซสเซอร์ที่สำคัญมีวิธีที่ดีในการจัดการกับตัวเลขทศนิยมที่มีการลงนามหรือไม่? C ได้รับความนิยมเมื่อหน่วยประมวลผลเสริมจุดลอยตัวมีลักษณะเฉพาะและแทบจะไม่เป็นสากล
David Thornley

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

0

คำถามที่ดี.

ถ้าอย่างที่คุณพูดมันมีไว้สำหรับคำเตือนเวลาคอมไพล์เท่านั้นและไม่มีการเปลี่ยนแปลงพฤติกรรมของพวกเขาเป็นอย่างอื่นฮาร์ดแวร์พื้นฐานจะไม่ได้รับผลกระทบและจะเป็นการเปลี่ยนแปลง C ++ / คอมไพเลอร์เท่านั้น

ฉันเคยชนะมาก่อนหน้านี้ แต่สิ่งที่ได้คือมันคงช่วยอะไรไม่ได้มาก ที่ดีที่สุดคอมไพเลอร์สามารถค้นหาการกำหนดแบบคงที่

unsigned float uf { 0 };
uf = -1f;

หรือยาวกว่าเล็กน้อย

unsigned float uf { 0 };
float f { 2 };
uf -= f;

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

unsigned char uc { 0 };
uc -= 1;

หลังจากนี้ 'uc' จะมีค่า 255

ตอนนี้คอมไพลเลอร์จะทำอย่างไรกับสถานการณ์เดียวกันกับประเภทลอยที่ไม่ได้ลงชื่อ หากไม่ทราบค่าในเวลาคอมไพล์ก็จำเป็นต้องสร้างรหัสที่เรียกใช้การคำนวณก่อนจากนั้นจึงทำการตรวจสอบเครื่องหมาย แต่จะเป็นอย่างไรเมื่อผลลัพธ์ของการคำนวณดังกล่าวมีข้อความว่า "-5.5" - ค่าใดที่ควรเก็บไว้ใน float ที่ประกาศว่าไม่ได้ลงนาม เราอาจลองใช้เลขคณิตแบบแยกส่วนเช่นอินทิกรัล แต่นั่นก็มาพร้อมกับปัญหาในตัวของมันเอง: ค่าที่ใหญ่ที่สุดคืออินฟินิตี้ที่ไม่มีที่สิ้นสุด .... ซึ่งใช้ไม่ได้ผลคุณไม่สามารถมี "อินฟินิตี้ - 1" ได้ การหาค่าที่แตกต่างกันมากที่สุดเท่าที่จะสามารถถือได้ก็จะไม่ได้ผลจริง ๆ เมื่อคุณพบว่ามีการป้องกัน "ณ " คงเป็นตัวเต็ง

สุดท้ายนี้จะไม่มีปัญหากับตัวเลขจุดคงที่เนื่องจากมีการกำหนดโมดูโลไว้อย่างดี

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