From 998e96bdfa5b68d19e2ed32013b5a2a52b0c8119 Mon Sep 17 00:00:00 2001 From: pos Date: Mon, 28 Sep 2026 15:53:35 +0200 Subject: [PATCH 1/6] M19: add two's-complement explorer --- interactive/twos-complement/index.html | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 interactive/twos-complement/index.html diff --git a/interactive/twos-complement/index.html b/interactive/twos-complement/index.html new file mode 100644 index 0000000..6269994 --- /dev/null +++ b/interactive/twos-complement/index.html @@ -0,0 +1,10 @@ + +EduNumbers – Two's complement
+

← EduNumbers

Two’s-complement explorer

+

Samme bitmønster kan bety forskjellige tall. Utforsk signed og unsigned tolkning side om side.

+
+
+

Bitmønster

Hex

+

Unsigned

Signed

+

Hva skjer?

Signed område:

+
\ No newline at end of file From eb465c3f2674db0e5ea8a5f064bddedcdeb2c2d8 Mon Sep 17 00:00:00 2001 From: pos Date: Mon, 28 Sep 2026 15:53:37 +0200 Subject: [PATCH 2/6] M19: implement two's-complement model --- interactive/twos-complement/twos.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 interactive/twos-complement/twos.js diff --git a/interactive/twos-complement/twos.js b/interactive/twos-complement/twos.js new file mode 100644 index 0000000..2bda7d5 --- /dev/null +++ b/interactive/twos-complement/twos.js @@ -0,0 +1,20 @@ +"use strict"; +const signedEl=document.querySelector("#signed"),widthEl=document.querySelector("#width"),errorEl=document.querySelector("#error"); +function limits(w){const half=1n<l.max)throw new Error(`Verdien må være mellom ${l.min} og ${l.max}.`);return n<0n?l.mod+n:n} +function decodeSigned(bits,w){const l=limits(w);return bits>l.max?bits-l.mod:bits} +function groupedBinary(bits,w){return bits.toString(2).padStart(w,"0").replace(/(.{8})(?=.)/g,"$1 ")} +function render(){ + const w=Number(widthEl.value),l=limits(w);let n,bits; + try{if(!/^[+-]?\d+$/.test(signedEl.value.trim()))throw new Error("Skriv inn et signed heltall.");n=BigInt(signedEl.value.trim());bits=encodeSigned(n,w);errorEl.textContent=""} + catch(e){errorEl.textContent=e.message;return} + document.querySelector("#binary").textContent=groupedBinary(bits,w); + document.querySelector("#hex").textContent="0x"+bits.toString(16).toUpperCase().padStart(w/4,"0"); + document.querySelector("#unsigned").textContent=bits.toString(); + document.querySelector("#signedOut").textContent=decodeSigned(bits,w).toString(); + document.querySelector("#range").textContent=`${l.min} … ${l.max}`; + document.querySelector("#explain").textContent=n<0n + ? `Negativ verdi: 2^${w} + (${n}) = ${bits}. Det er det lagrede unsigned bitmønsteret.` + : `Positiv verdi: signed og unsigned har samme numeriske verdi så lenge signbiten er 0.`; +} +signedEl.addEventListener("input",render);widthEl.addEventListener("change",render);render(); From caa4b5636fbc3121d0856a060e545230e83a28be Mon Sep 17 00:00:00 2001 From: pos Date: Mon, 28 Sep 2026 15:53:39 +0200 Subject: [PATCH 3/6] M19: style two's-complement explorer --- interactive/twos-complement/style.css | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 interactive/twos-complement/style.css diff --git a/interactive/twos-complement/style.css b/interactive/twos-complement/style.css new file mode 100644 index 0000000..a257b44 --- /dev/null +++ b/interactive/twos-complement/style.css @@ -0,0 +1,4 @@ +:root{font-family:system-ui,sans-serif;line-height:1.55;color-scheme:light dark}body{margin:0}main{max-width:60rem;margin:3rem auto;padding:0 1.25rem} +.controls{display:flex;gap:1.5rem;flex-wrap:wrap}.controls label{display:grid;gap:.3rem}input,select{font:inherit;padding:.5rem}#error{flex-basis:100%;min-height:1.5em;font-weight:600} +.cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(12rem,1fr));gap:.75rem}.cards div{border:1px solid #8888;border-radius:.6rem;padding:1rem}.cards h2{font-size:1rem;margin-top:0} +code{font-family:ui-monospace,monospace;overflow-wrap:anywhere;font-size:1.05rem}a{color:inherit} \ No newline at end of file From 2a04370caeb1fa48ecfef2a0563aa3322c9458ca Mon Sep 17 00:00:00 2001 From: pos Date: Mon, 28 Sep 2026 15:53:48 +0200 Subject: [PATCH 4/6] M19: test two's-complement encoding --- tests/twos-complement.test.js | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 tests/twos-complement.test.js diff --git a/tests/twos-complement.test.js b/tests/twos-complement.test.js new file mode 100644 index 0000000..7312d0c --- /dev/null +++ b/tests/twos-complement.test.js @@ -0,0 +1,10 @@ +const fs=require("fs"),vm=require("vm");const s=fs.readFileSync("interactive/twos-complement/twos.js","utf8"); +const a=s.indexOf("function limits"),b=s.indexOf("function render"),box={};vm.createContext(box);vm.runInContext(s.slice(a,b),box); +const eq=(a,b,m)=>{if(a!==b)throw new Error(m+": "+a+" != "+b)}; +eq(box.encodeSigned(-1n,8),255n,"-1/8");eq(box.decodeSigned(255n,8),-1n,"FF/8"); +eq(box.encodeSigned(-128n,8),128n,"-128/8");eq(box.encodeSigned(127n,8),127n,"127/8"); +eq(box.encodeSigned(-32768n,16),32768n,"-32768/16");eq(box.decodeSigned(65535n,16),-1n,"FFFF/16"); +eq(box.encodeSigned(-2147483648n,32),2147483648n,"INT32_MIN"); +eq(box.groupedBinary(0xFF00n,16),"11111111 00000000","grouping"); +for(const [n,w] of [[-129n,8],[128n,8],[32768n,16]]){let ok=false;try{box.encodeSigned(n,w)}catch{ok=true}if(!ok)throw new Error("Expected range rejection "+n)} +console.log("Two's-complement tests passed"); \ No newline at end of file From 82faa927875ef56b645508174609b9e307b606bd Mon Sep 17 00:00:00 2001 From: pos Date: Mon, 28 Sep 2026 15:53:57 +0200 Subject: [PATCH 5/6] M19: qualify two's-complement explorer in CI --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8485160..21f6fbf 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,6 +24,7 @@ jobs: run: | node tests/base-converter.test.js node tests/bit-visualizer.test.js + node tests/twos-complement.test.js - name: Build HTML, EPUB, PDF and Kindle run: make all - name: Upload course artifacts From bf64010df94c7745a42f5e840b6290c9378fdd94 Mon Sep 17 00:00:00 2001 From: pos Date: Mon, 28 Sep 2026 15:53:59 +0200 Subject: [PATCH 6/6] M19: link two's-complement explorer --- .github/workflows/pages.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 3257d18..f3a8a1a 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -58,6 +58,7 @@ jobs: English Basekonverterer Bit-visualisering + Two’s complement