การลบแบบ IEEE-754 มีความสมบูรณ์เชิงฟังก์ชัน
(orlp.net)- การลบเลขทศนิยมลอยตัวแบบ IEEE-754 สามารถใช้สร้างวงจรไบนารีใด ๆ ก็ได้ โดยอาศัย signed zero และกฎเครื่องหมายของผลลัพธ์
- หากมอง
-0เป็น false และ+0เป็น true แล้ว ในโหมดปัดเศษปกติx - yจะทำงานเหมือนA ∨ ¬Bหรือก็คือเกต IMPLY แบบสลับลำดับอาร์กิวเมนต์ - เมื่อมี ค่าคงที่ false เกตนี้สามารถสร้าง NOT ได้ และการรวมกันของ NOT + IMPLY จะกลายเป็นชุดเกตตรรกะที่มีความสมบูรณ์เชิงฟังก์ชัน
- ตัวอย่างใน Python แยกเครื่องหมายของ
-0.0และ0.0โดยตรง แล้วสร้างf_not,f_or,f_and,f_xorทั้งหมดบนพื้นฐานของการลบ - ตัวอย่างใน Rust ใช้อาร์เรย์
f32แทนจำนวนเต็ม 8 บิต และคำนวณ 23 + 19 = 42 โดยการบวกจำนวนเต็ม 8 บิตสองจำนวนต้องใช้คำสั่งเลขทศนิยมลอยตัวประมาณ 120 คำสั่ง
จุดตั้งต้นที่เกิดจากกฎเครื่องหมายของ IEEE-754
- การลบเลขทศนิยมลอยตัวแบบ IEEE-754 มี ความสมบูรณ์เชิงฟังก์ชัน
- ความสมบูรณ์เชิงฟังก์ชันหมายความว่า เราสามารถประกอบ วงจรไบนารี ใด ๆ ก็ได้โดยใช้งานปฏิบัติการนี้เพียงอย่างเดียว
- แก่นสำคัญอยู่ที่กฎบิตเครื่องหมายในมาตรา 6.3 ของมาตรฐาน IEEE 754-2019
- การลบ
x - yถูกพิจารณาเป็นผลบวกx + (-y) - ศูนย์สามารถมีเครื่องหมายได้ ดังนั้น
-0และ+0จึงถูกปฏิบัติเป็นค่าที่ต่างกัน - อย่างไรก็ตาม ในการเปรียบเทียบแบบ IEEE-754 นั้น
-0 == +0เป็นจริง - เมื่ออินพุตและผลลัพธ์ไม่ใช่ NaN เครื่องหมายของผลบวกหรือผลต่างจะเป็นไปตามกฎเครื่องหมายของโอเปอแรนด์
- หากผลต่างของค่าสองค่าที่มีเครื่องหมายเดียวกันออกมาเป็น 0 พอดี ผลลัพธ์จะเป็น
+0ในโหมดปัดเศษทุกแบบยกเว้นroundTowardNegative
- การลบ
- การประกอบต่อจากนี้สมมติให้ใช้
roundTiesToEvenซึ่งเป็นโหมดปัดเศษปริยาย- ใน
roundTowardNegativeก็ทำงานได้ในลักษณะคล้ายกัน
- ใน
ตารางค่าความจริงเมื่อเอาศูนย์มาลบกัน
- หากนำแค่
-0และ+0มาลบกัน จะได้ผลดังนี้-0 - -0 = +0-0 - +0 = -0+0 - -0 = +0+0 - +0 = +0
- ถ้ากำหนดให้
-0เป็น false และ+0เป็น true ตารางค่าความจริงของเอาต์พุตจะเป็นดังนี้0 0 -> 10 1 -> 01 0 -> 11 1 -> 1
- ตารางนี้เทียบเท่ากับ
A ∨ ¬Bและตรงกับเกต IMPLY ในรูปB → A- เมื่อเทียบกับเกต IMPLY ทั่วไป นี่คือ รูปแบบที่สลับอาร์กิวเมนต์
เมื่อมีค่าคงที่ false ก็จะสมบูรณ์เชิงฟังก์ชัน
- ตารางค่าความจริงนี้มีความสมบูรณ์เชิงฟังก์ชันเมื่อสามารถเข้าถึง ค่าคงที่ false ได้
- เมื่อมีค่าคงที่ false ก็สามารถสร้างเกต NOT ได้
- NOT + IMPLY เป็นชุดที่มีความสมบูรณ์เชิงฟังก์ชัน
- ส่วน NAND และ NOR นั้นมีความสมบูรณ์เชิงฟังก์ชันได้ด้วยตัวเองโดยไม่ต้องมีค่าคงที่เฉพาะ
- สำหรับการสร้างไมโครชิป ข้อดีคือผลิตชิ้นส่วนเพียงชนิดเดียวก็พอ
- และไม่จำเป็นต้องเดินสัญญาณ low แบบคงที่เพื่อสร้างเกต NOT
วงจรตรรกะจากการลบที่สร้างด้วย Python
- ตัวอย่างใน Python กำหนด
-0.0เป็น false และ0.0เป็น true- เนื่องจากใน IEEE-754 นั้น
+0และ-0เปรียบเทียบแล้วเท่ากัน จึงใช้math.copysignเพื่อ ดึงเครื่องหมาย ออกมาแยกความต่าง
- เนื่องจากใน IEEE-754 นั้น
- เกต NOT อาศัยคุณสมบัติที่
-0 - xสามารถกลับเครื่องหมายของศูนย์ได้f_not = lambda x: f_false - xf_not(-0.0)จะได้ truef_not(+0.0)จะได้ false
- เกต OR สร้างโดยกลับเครื่องหมายของอาร์กิวเมนต์ตัวที่สองก่อน แล้วจึงนำไปลบ
f_or = lambda a, b: a - f_not(b)- จะได้ false เฉพาะเมื่ออินพุตทั้งสองเป็น
-0เท่านั้น นอกนั้นเป็น true
- เกต AND และ XOR ก็สามารถสร้างได้จากการรวม OR กับ NOT
f_and = lambda a, b: f_not(f_or(f_not(a), f_not(b)))f_xor = lambda a, b: f_or(f_and(f_not(a), b), f_and(a, f_not(b)))
จำนวนเต็มแบบซอฟต์แวร์ที่สร้างด้วย Rust
- ตัวอย่างใน Rust กำหนด
Bit = f32และใช้ZERO = -0.0,ONE = 0.0เพื่อแทนบิต not,or,and,xorถูกสร้างทั้งหมดบนพื้นฐานของการลบเลขทศนิยมลอยตัว และนำไปใช้สร้างฟูลแอดเดอร์adder- ใช้
SoftU8 = [Bit; 8]เพื่อแทนจำนวนเต็ม 8 บิตto_softu8จะแปลงแต่ละบิตของu8ให้เป็นONEหรือZEROfrom_softu8จะตรวจสอบเครื่องหมายของแต่ละสมาชิกแล้วแปลงกลับเป็นu8
- โปรแกรมตัวอย่างแปลง 23 และ 19 เป็น
SoftU8แล้วบวกกัน จากนั้นพิมพ์ค่า 42 - การบวกจำนวนเต็ม 8 บิตสองจำนวนต้องใช้คำสั่งเลขทศนิยมลอยตัวประมาณ 120 คำสั่ง
- บน x86-64 ไม่มีคำสั่งกลับเครื่องหมายเลขทศนิยมลอยตัวโดยตรงจริง ๆ ดังนั้นคอมไพเลอร์จึงใช้มาสก์และ XOR เพื่อสลับ บิตเครื่องหมาย ซึ่งเป็นบิตบนสุดของเลขทศนิยมลอยตัวแบบ IEEE-754
2 ความคิดเห็น
ความคิดเห็นจาก Hacker News
การ นำไปใช้ในทางแปลกประหลาด ของคำสั่ง floating-point แบบนี้ นึกภาพได้เลยว่า DRM บางตัวน่าจะใช้เป็นวิธีทำให้ virtual machine อ่านยาก/สับสน
ขั้นต่อไปน่าจะเป็นการสร้างคอมไพเลอร์ที่ใช้คุณสมบัตินี้เพื่อรันซอร์สโค้ดทั่วไปในฐานะ จำนวนเต็มแบบ floating-point แล้วต่ออะไรทำนอง FFI เพื่อเรียก OS API ทั่วไป
เป็นการพิสูจน์เชิงสร้างสรรค์ว่ากลไกการจัดการ exception ของ Intel MMU มีความสมบูรณ์แบบทัวริง
มีการสร้าง assembler ที่แปลงคำสั่ง
Move, Branch if Zero, Decrementให้เป็นซอร์ส C ที่ตั้งค่าตารางควบคุมโปรเซสเซอร์หลายชุด และหลังจากโค้ดนั้นรันแล้ว CPU จะคำนวณด้วยการพยายามก่อให้เกิด exception โดยไม่รันคำสั่งแม้แต่คำสั่งเดียวเลือกได้ด้วยว่า assembler จะสร้างคำสั่ง X86 ที่แสดงตัวแปรบน VGA frame buffer และส่งต่อการควบคุมไปมาระหว่างคำสั่งแสดงผลแบบ native กับคำสั่ง trap ของ weird machine ได้
นึกถึงวิดีโอยอดเยี่ยมนี้ที่สร้าง การคำนวณด้วยเพียง IEEE-754 NaN และอนันต์: https://www.youtube.com/watch?v=5TFDG-y-EHs
เป็นคอนเทนต์ที่เนิร์ดสุด ๆ ชวนคิด และตลก แถมวิธีนำเสนอก็ดีมาก
แนะนำอย่างแรง โดยเฉพาะกับกลุ่มผู้อ่าน HN
ในเรื่องสั้น Coding Machines การใช้บิตเครื่องหมายในทางที่คล้ายกันเป็นเบาะแสใหญ่ที่บอกว่า AI ตัวจริงถูกปล่อยออกสู่โลกแล้ว
https://www.teamten.com/lawrence/writings/coding-machines/
แหล่งข้อมูลที่เกี่ยวข้องคือ https://dougallj.wordpress.com/2020/05/10/bitwise-conversion...
เป็น implementation ที่แปลง IEEE-754 double หนึ่งตัวให้เป็นคู่ของ double สองตัวที่เก็บค่าจำนวนเต็มของ 32 บิตล่างและ 32 บิตบนจากการแทนค่าระดับบิตของอาร์กิวเมนต์ โดยใช้เพียงการบวก/ลบ/คูณ double
ดูจากตารางค่าความจริงแล้ว การลบดูเป็นแบบ true-preserving ชัด ๆ ดังนั้นในความเป็นจริงไม่น่าจะ functionally complete ได้
ผมพลาดอะไรไปหรือเปล่า?
ถ้าไม่มีค่าคงที่นี้ก็ไม่ functionally complete และต่างจาก NAND ที่สามารถสร้าง false ได้จากค่าใด ๆ
ใจความของบทความคือการแสดงว่าสามารถเลียนแบบวงจรใด ๆ ได้ด้วย 0 ที่มีเครื่องหมายกับการลบ floating-point เท่านั้น และผมมองว่า functional completeness เป็นคำที่กระชับที่สุดในการสื่อเรื่องนั้น แต่ถ้าดูแค่ตารางค่าความจริงอย่างเคร่งครัด ก็ถือว่าบิดกฎไปเล็กน้อย ดังนั้นจะทำให้ชัดเจนในบทความ
ด้วยการลบและ 0 เราสร้าง false เป็น -0.0 ได้ และได้เซตที่ functionally complete
{->, _|_}ตาม Wikipedia [1][1] https://en.wikipedia.org/wiki/Functional_completeness
ผมไม่เห็นด้วยกับข้ออ้างว่าบิตของการลบเพียงอย่างเดียว functionally complete
การตัดสินว่าไม่ functionally complete เพราะเป็น true-preserving นั้นดูถูกต้องแล้ว
เนื้อหาคือ “เซตตัวเชื่อมสองสมาชิกทุกเซตที่มี NOT และมีหนึ่งใน {AND, OR, IMPLY} เป็นสับเซตขั้นต่ำที่ functionally complete ของ {NOT, AND, OR, IMPLY, IFF}”
[1] https://en.wikipedia.org/wiki/Functional_completeness
ก่อนอื่นเลย เราจะรู้ได้อย่างไรว่าตารางค่าความจริงเป็น true-preserving? ตารางค่าความจริงไม่ใช่การให้เหตุผลเชิงตรรกะ
ถ้าความสมบูรณ์เชิงฟังก์ชันหมายถึงสามารถสร้างวงจรตรรกะใด ๆ ก็ได้ แบบนี้การ ลบเลขทศนิยมลอยตัว ของ IEEE-754 ก็เท่ากับว่าเป็น Turing complete โดยพฤตินัยหรือเปล่า? หรือไม่ใช่?
ความสมบูรณ์เชิงฟังก์ชัน ขาดความสามารถในการทำซ้ำที่จำเป็นต่อการเป็น Turing complete
Turing completeness มักถูกใช้ผิดเวลาที่จริง ๆ ต้องการพูดถึง functional completeness และบางครั้งก็เป็นเพราะสับสนสองอย่างนี้ หรือเพราะใช้เป็นชื่อบทความ/พาดหัวบล็อกแล้วดูน่าเชื่อกว่า
movจริง ๆ แล้วไม่ใช่ Turing complete ต้องมีคำสั่งjmp: https://harrisonwl.github.io/assets/courses/malware/spring20...ระบบเข้ารหัสแบบโฮโมมอร์ฟิกมีความสมบูรณ์เชิงฟังก์ชัน แต่ไม่ใช่ Turing complete เพราะการวนซ้ำจะรั่วจำนวนครั้งของการดำเนินการที่ทำไป และทำให้การเข้ารหัสถูกทำลาย
เราสามารถสร้างเครื่องที่เป็น Turing complete ด้วยเกต NAND ได้ แต่การบอกว่าเกต NAND เป็น Turing complete ก็เหมือนบอกว่าสามารถอาศัยอยู่ในก้อนอิฐได้
คุณอาศัยอยู่ในก้อนอิฐไม่ได้ แต่เอาอิฐไปสร้างบ้านแล้วอาศัยอยู่ในนั้นได้
“ลบแล้วกระโดดถ้าน้อยกว่าหรือเท่ากับ 0” เป็นคำสั่งเดียวที่ Turing complete
https://en.wikipedia.org/wiki/One-instruction_set_computer
เคยโพสต์ไว้ในเธรด /r/programming มาก่อน แต่ขอเอามาโพสต์ที่นี่ด้วย
สามารถทำ adder ได้ด้วยการลบ “เพียง” 11 ครั้ง
fn adder(a: Bit, b: Bit, c: Bit) -> (Bit, Bit) {let r0 = c - b;let r1 = c - r0;let r2 = ZERO - r0;let r3 = b - r1;let r4 = r2 - r3;let r5 = a - r4;let r6 = r4 - a;let r7 = ZERO - r5;let r8 = r7 - r1;let r9 = r7 - r6;let r10 = ZERO - r8;(r9, r10)}ถ้าเป็น “จำนวนเต็มที่ implement ในซอฟต์แวร์โดยใช้เฉพาะการคำนวณแบบทศนิยมลอยตัว” โดยพื้นฐานแล้วก็เหมือนกับ ความพยายามทั้งหมดที่จะใช้ number ของ JavaScript เหมือน int
ประโยคที่ว่า “ถ้าเครื่องหมายของ significand ทั้งสองเหมือนกัน ผลลัพธ์ก็ต้องมีเครื่องหมายนั้นด้วย แต่ใน x−y ถ้าเครื่องหมายของ x และ y ต่างกัน ผลลัพธ์ต้องมีเครื่องหมายของ x” นั้นผิดเล็กน้อย หรือไม่ก็ใช้คำว่า เครื่องหมาย ปนกันในสองความหมาย
ถ้า x=5, y=10 คือทั้งคู่เป็นเครื่องหมายบวก x-y จะได้ -5 ซึ่งเป็นเครื่องหมายลบ
ต่อให้สมมติว่าเครื่องหมายของตัวแปร y ถูกกลับจริง ๆ ถ้าเลือก -3 กับ -6 ตัวหลังจะถูกกลับเป็น 6 และผลลัพธ์คือ +3 ซึ่งมีเครื่องหมายต่างจาก x
-3 กับ -6 ก็เช่นกัน เพราะ x และ y มีเครื่องหมายเหมือนกัน จึงไม่เข้าเงื่อนไขสำหรับการลบ
ตัวอย่างเป็นเรื่องของเครื่องหมายที่เหมือนกัน
ชื่อเรื่องมีข้อผิดพลาดนะครับ ไม่ใช่ว่าการลบถูกทำให้สมบูรณ์แล้ว แต่หมายความว่าสามารถแสดงฟังก์ชันทั้งหมดได้ด้วยการลบ จึงใช้คำว่า functionally complete