Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ jobs:
<a href="en/">English</a>
<a href="interactive/base-converter/">Basekonverterer</a>
<a href="interactive/bit-visualizer/">Bit-visualisering</a>
<a href="interactive/twos-complement/">Two’s complement</a>
</nav>
</main>
</body>
Expand Down
10 changes: 10 additions & 0 deletions interactive/twos-complement/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<!doctype html><html lang="no"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>EduNumbers – Two's complement</title><link rel="stylesheet" href="style.css"></head><body><main>
<p><a href="../../no/">← EduNumbers</a></p><h1>Two’s-complement explorer</h1>
<p>Samme bitmønster kan bety forskjellige tall. Utforsk signed og unsigned tolkning side om side.</p>
<section class="controls"><label>Signed verdi <input id="signed" value="-1" inputmode="numeric"></label>
<label>Bredde <select id="width"><option>8</option><option>16</option><option>32</option></select> bit</label><p id="error" role="alert" aria-live="polite"></p></section>
<section class="cards"><div><h2>Bitmønster</h2><code id="binary"></code></div><div><h2>Hex</h2><code id="hex"></code></div>
<div><h2>Unsigned</h2><code id="unsigned"></code></div><div><h2>Signed</h2><code id="signedOut"></code></div></section>
<section><h2>Hva skjer?</h2><p id="explain"></p><p><strong>Signed område:</strong> <code id="range"></code></p></section>
</main><script src="twos.js"></script></body></html>
4 changes: 4 additions & 0 deletions interactive/twos-complement/style.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 20 additions & 0 deletions interactive/twos-complement/twos.js
Original file line number Diff line number Diff line change
@@ -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<<BigInt(w-1);return {min:-half,max:half-1n,mod:1n<<BigInt(w)}}
function encodeSigned(n,w){const l=limits(w);if(n<l.min||n>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();
10 changes: 10 additions & 0 deletions tests/twos-complement.test.js
Original file line number Diff line number Diff line change
@@ -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");
Loading