diff options
| author | eunhhu <[email protected]> | 2026-10-19 01:13:07 +0100 |
|---|---|---|
| committer | eunhhu <[email protected]> | 2026-10-19 01:13:07 +0100 |
| commit | b79f627af433a63f8f42869b124f5ef344a49352 (patch) | |
| tree | 02aebc9cb63ace5040e5467949f3bec91edabf36 | |
| parent | 7e1b3dc160906abaecfd4fe49ab2a51a3e2bfda4 (diff) | |
| download | pure-b79f627af433a63f8f42869b124f5ef344a49352.tar.gz pure-b79f627af433a63f8f42869b124f5ef344a49352.zip | |
027's stage 5 designed and 5a made: two numbers (number, binary64, the default of literals and of counts, positions, lengths and u32 fields, whole to 2^53 exactly; i64 the exact integers past it, bits and money, never mixed, moved by number() and i64() when exact), / a number's division, // the floored quotient, i64 folds exact (only the result in range, so in any order), number folds in order unless the batch executor's 2^53 test says any order is exact, money as i64 minor units shown by .Nd, a replace carrying number, bool and i64 when exact; 5a: opcode FLOORDIV (95, 96 now): a number's floored quotient as Python's float //, exactly (a less its floored remainder, divided, to the nearest whole; a zero signed as a / b; by zero divide), a new opcode so the JIT, the tier and the batch executor leave it to the executor; unit test floor_division (7 // 2, -7 // 2, 7 // -2, 1 // 0.1 = 9, 0 // -3 = -0, 1e300 // 7, by zero); counts: b43 against b42 within 0.001% but BFS without batches -0.09%; the bank's queries (s42 and s43 in turns, six each) -0.20% to 0.00%; 008 and 027
| -rw-r--r-- | core/module.c | 16 | ||||
| -rw-r--r-- | core/module.h | 1 | ||||
| -rw-r--r-- | core/test-module.c | 28 | ||||
| -rw-r--r-- | docs/design/008-module-format.md | 3 | ||||
| -rw-r--r-- | docs/design/027-computation-core.md | 26 | ||||
| -rw-r--r-- | research/experiments/040-against-c/2026-10-19-counts-s43.jsonl | 12 | ||||
| -rw-r--r-- | research/experiments/041-batches/2026-10-19-counts.jsonl | 10 |
7 files changed, 94 insertions, 2 deletions
diff --git a/core/module.c b/core/module.c index 71a0a25..052528c 100644 --- a/core/module.c +++ b/core/module.c @@ -130,7 +130,7 @@ static const struct { unsigned char pops, in[3], out; unsigned short where; unsi /* LOOP takes its low and high, two numbers or two i64s (the operands are its argument's). */ [PURE_OP_LOOP] = {0, {0}, 0, IN_HANDLER | IN_COMPUTE, A_LOOP, O_ANY}, [PURE_OP_BREAK] = {0, {0}, 0, IN_HANDLER | IN_COMPUTE, A_NONE, O_ANY}, - [PURE_OP_FLOOR] = NUMS(1), [PURE_OP_MOD] = NUMS(2), + [PURE_OP_FLOOR] = NUMS(1), [PURE_OP_MOD] = NUMS(2), [PURE_OP_FLOORDIV] = NUMS(2), [PURE_OP_I64_ADD] = I64S(2), [PURE_OP_I64_SUB] = I64S(2), [PURE_OP_I64_MUL] = I64S(2), [PURE_OP_I64_DIV] = I64S(2), [PURE_OP_I64_MOD] = I64S(2), [PURE_OP_I64_NEG] = I64S(1), [PURE_OP_I64_MIN] = I64S(2), [PURE_OP_I64_MAX] = I64S(2), [PURE_OP_I64_COMPARE] = {2, {T_I64, T_I64}, T_BOOL, READ, A_RELATION, O_ANY}, @@ -1526,6 +1526,16 @@ static uint32_t nesting_of(const PureModule *m) { return most; } +/* a // b for numbers as Python's float floor division: the quotient of a less its floored remainder, rounded to the + * nearest whole (it is one but for rounding), a zero with the sign of a / b (027's stage 5). b is not 0. */ +static double floor_divide(double a, double b) { + double mod = fmod(a, b), quotient = (a - mod) / b; + if (mod != 0 && (b < 0) != (mod < 0)) quotient -= 1.0; + if (quotient == 0) return copysign(0.0, a / b); + double whole = floor(quotient); + return quotient - whole > 0.5 ? whole + 1.0 : whole; +} + /* A name and its place among those of one namespace, in order of name and then of place. */ typedef struct { const char *name; size_t index; } Named; static int by_name(const void *a, const void *b) { @@ -4510,6 +4520,10 @@ static inline __attribute__((always_inline)) int step(Frame *frame, const PureOp if (value != 0 && (value < 0) != (b < 0)) value += b; else if (value == 0) value = copysign(0.0, b); break; + case PURE_OP_FLOORDIV: /* MOD's quotient, exactly (027's stage 5): a less its remainder, divided, as Python's // */ + if (b == 0) { kind = PURE_FAULT_DIVIDE; FAILED; } + value = floor_divide(a, b); + break; default: value = maximum(a, b); break; } if (!isfinite(value)) { kind = PURE_FAULT_NON_FINITE; FAILED; } diff --git a/core/module.h b/core/module.h index 8a84ddb..ce8cebf 100644 --- a/core/module.h +++ b/core/module.h @@ -47,6 +47,7 @@ typedef enum { PURE_OP_BYTES_SLICE, PURE_OP_BYTE, PURE_OP_BYTES_LOAD, PURE_OP_BYTES_STORE, PURE_OP_LIST_VALUE, PURE_OP_LIST_BIND, PURE_OP_APPEND_IN, PURE_OP_NUM_PARSE, PURE_OP_CALL, PURE_OP_RETURN, PURE_OP_CHAR_AT, PURE_OP_CHAR, + PURE_OP_FLOORDIV, PURE_OP_COUNT } PureOpcode; /* 027's 4c: an anchor is a place in a text or a list that follows its edits. A text's (a cursor) is a gap between diff --git a/core/test-module.c b/core/test-module.c index cb8e228..36722d2 100644 --- a/core/test-module.c +++ b/core/test-module.c @@ -6962,6 +6962,33 @@ static void timers(void) { CHECK(load(bad, &module_b) == PURE_INVALID && strstr(error, "view with an effect")); } +/* Floor division of numbers (027's stage 5): as Python's //, the quotient MOD's remainder leaves, exactly (1 // 0.1 is 9: + * 0.1 is a little more than a tenth), its zero signed as a / b; by zero it fails as divide. */ +static void floor_division(void) { + enum { FDIV = PURE_OP_FLOORDIV }; + static const S nums[] = {{"a", 0}, {"b", 0}, {"c", 0}, {"d", 0}, {"e", 0}, {"f", 0}}; + static const I inputs[] = {KEY("go", "KeyG"), KEY("zero", "KeyZ")}; + static const O code[] = { + {C, 0, 7}, {C, 0, 2}, {FDIV, 0, 0}, {ST, 0, 0}, {C, 0, -7}, {C, 0, 2}, {FDIV, 0, 0}, {ST, 1, 0}, + {C, 0, 7}, {C, 0, -2}, {FDIV, 0, 0}, {ST, 2, 0}, {C, 0, 1}, {C, 0, 0.1}, {FDIV, 0, 0}, {ST, 3, 0}, + {C, 0, 0}, {C, 0, -3}, {FDIV, 0, 0}, {ST, 4, 0}, {C, 0, 1e300}, {C, 0, 7}, {FDIV, 0, 0}, {ST, 5, 0}, + {C, 0, 1}, {C, 0, 0}, {FDIV, 0, 0}, {ST, 0, 0}, + }; + static const H handlers[] = {{1, 0, 24}, {3, 24, 4}}; + static const V none[] = {{"v", 0, 1}}; + Spec spec = SPEC(nums, inputs, none, handlers, code); + spec.nv = 0; + if (load(spec, &module_a) != PURE_OK) { fprintf(stderr, "floor division: %s\n", error); abort(); } + PureApp app; + CHECK(pure_app_start(&app, &module_a, (PureData){0}) == PURE_OK && press(&app, 0) == PURE_OK); + double huge = 1e300, quotient = (huge - fmod(huge, 7)) / 7; + CHECK(app.values[0].num == 3 && app.values[1].num == -4 && app.values[2].num == -4 && app.values[3].num == 9 && + app.values[4].num == 0 && signbit(app.values[4].num) && app.values[5].num == floor(quotient)); + CHECK(press(&app, 1) == PURE_INVALID && app.fault == PURE_FAULT_DIVIDE && app.values[0].num == 3); + pure_app_stop(&app); + printf("{\"floor_division\":6}\n"); +} + /* A number is written with the fewest digits that read back as it, the * digits %g gives (with its exponent's leading zeros dropped): the same * text as that search over precisions, for values of every kind. */ @@ -7312,6 +7339,7 @@ int main(void) { bytes_fields(); anchors(); list_views(); + floor_division(); memory_budget(); work_budget(); fold_mutations(); diff --git a/docs/design/008-module-format.md b/docs/design/008-module-format.md index dde292e..201baf9 100644 --- a/docs/design/008-module-format.md +++ b/docs/design/008-module-format.md @@ -36,7 +36,7 @@ v7은 027의 4단계(패턴 모으기)의 판이다. 4b로 **키별 문서**를 인자 셋이고, 키의 누름과 뗌, text 입력, 타이머, frame, 가리킴은 메시지 입력의 묶음(무엇이 그 메시지를 만드는가)이다. 핸들러의 이벤트는 그 입력의 번호이고, DT와 TEXT_INPUT은 필드 읽기(FIELD)가 대신한다(아래 표의 번호가 당겨졌다). 4e로 **보기가 아무 값**이 되었다: 보기와 열의 타입이 레코드 필드의 것(바이트와 리스트가 더해졌다)이고, 표는 리스트 보기다. 리스트 보기는 리스트 상태 그대로나 그 지도, 또는 블록이 -낸 리스트를 행으로 가지며, 보기 항목은 20바이트다. 4단계가 끝날 때까지 v7은 걸음마다 바뀐다(1.0 전, 생산자는 aup 하나다). +낸 리스트를 행으로 가지며, 보기 항목은 20바이트다. 5단계(수의 모델)로 내림한 몫(FLOORDIV, 95)이 더해졌다. 4단계가 끝날 때까지 v7은 걸음마다 바뀐다(1.0 전, 생산자는 aup 하나다). **레코드 안의 리스트**(027의 2b). - 레코드 필드의 타입 4(LIST)는 항목들의 레코드를 기본값 자리(i64)에 둔다. @@ -181,6 +181,7 @@ v7은 027의 4단계(패턴 모으기)의 판이다. 4b로 **키별 문서**를 | 92 | RETURN | … r… → | 함수 `인자`(자신)를 끝낸다. 맨 위의 결과가 부른 쪽으로 간다 | | 93 | CHAR_AT | t q → q | text의 바이트 자리 q(i64)에서 시작하는 글자의 코드 포인트(i64). 글자가 시작하지 않는 자리나 밖이면 `limit` | | 94 | CHAR | q → t | 코드 포인트 하나의 text. 0–0x10FFFF 밖이면 `limit`, 서로게이트면 `encoding` | +| 95 | FLOORDIV | n n → n | 내림한 몫(027의 5단계): MOD의 나머지를 뺀 a를 b로 나눈 것을 가까운 정수로(파이썬의 float `//`와 같다. `1 // 0.1`은 9). 0의 부호는 a / b의 것. 0으로 나누면 `divide` | bool은 실행 중에 수의 칸에 든 0이나 1이고, i64는 같은 8바이트의 정수다. CONST의 인자 2는 i64 상수(값의 8바이트), NUM_TEXT의 인자 2는 i64의 10진 글자다. LOAD·STORE·지역 칸·항목·FIELD·SEND는 i64를 수처럼 옮긴다. FOLD의 종류에 64(PURE_FOLD_INTEGER)를 diff --git a/docs/design/027-computation-core.md b/docs/design/027-computation-core.md index 28c03b2..4c7fb60 100644 --- a/docs/design/027-computation-core.md +++ b/docs/design/027-computation-core.md @@ -513,6 +513,32 @@ - 정수 리터럴의 기본, `/`와 `//`, 돈의 십진수, 묶음 실행기의 2^53 검사를 정한다. - **막대.** TPC-C가 SQLite와 행마다 같은 상태를 남기는 것이 그대로다. +- **설계(2026-10-19).** + - **두 수.** `number`는 binary64이고 리터럴과 셈(개수, 자리, 길이, u32 필드)의 기본이다. 정수는 정수인 number이고 2^53까지 정확하다. + `i64`는 정확한 정수의 타입이다: 2^53을 넘는 정수, 비트, 돈. 둘은 섞이지 않는다. 옮기기는 `number(i)`(정확할 때만, 넘으면 + `limit`)와 `i64(x)`(정수이고 범위 안일 때만)다. 리터럴은 맥락을 따른다(i64가 오는 자리의 `1`은 i64). + - 왜 i64가 기본이 아닌가: 개수·자리·길이·u32 필드가 모두 number라서, 리터럴만 i64로 하면 `n = xs.count` 같은 줄마다 옮기기가 + 든다. 그것들을 모두 i64로 하면 묶음 실행기와 JIT의 수의 길(binary64 레인)을 i64로 다시 지어야 한다. 앱의 셈은 거의 2^53 안이고, + 그 밖은 i64가 맡는다. 한 기본 타입이 압축이다. + - **나눗셈.** `/`는 number의 나눗셈이다(i64끼리는 거절: `//`를 쓰거나 옮긴다). `//`는 내림한 몫이다: number는 파이썬의 그것과 + 같은 정확한 내림(나머지를 빼고 나눈다), i64는 I64_DIV. `%`는 그 짝(내림한 나머지, 나누는 수의 부호)으로 이미 있다. + - **접기.** i64의 합은 정확하게 셈하고 결과만 범위를 본다(중간이 넘쳐도 끝이 i64 안이면 답이다): 차례에 매이지 않는다. number의 + 합은 차례대로의 왼쪽 접기이고, 구현은 답이 같을 때만 차례를 바꾼다. 묶음 실행기의 2^53 검사가 그 조건이다: 모든 항이 정수이고 + 그 크기의 합이 2^53 아래면 어느 차례든 정확하다. 아니면 차례대로다. + - **돈.** 새 타입 없이 i64의 최소 단위(센트)다. 셈은 정확하고, 반올림은 앱이 쓴다(`price * rate // 10000`). 보이는 꼴은 + `{cents:.2d}`: i64를 소수점 아래 두 자리로 나눠 쓴다(1234 → `12.34`, −5 → `-0.05`). JSON의 경계는 i64를 정수로 쓴다. + - **교체.** number, bool, i64는 정확할 때만 서로 옮긴다(`pure_cell_as`: 정수인 number ↔ i64, 0·1 ↔ bool). 아니면 기본값이다. + - 걸음: 5a(`//`와 `/`의 규칙), 5b(i64 접기의 정확한 합), 5c(`.Nd`와 TPC-C의 돈을 i64 센트로, SQLite도), 5d(교체의 규칙과 008). + - **5a 만듦(2026-10-19).** + - Pure: 명령 FLOORDIV(95, 명령 96개). number의 내림한 몫을 파이썬의 float `//`처럼 정확하게 낸다(나머지를 뺀 a를 b로 나누고 + 가까운 정수로, 0의 부호는 a / b의 것, 0으로 나누면 `divide`). 새 명령이라 JIT, 계층, 묶음 실행기의 빠른 길은 그것을 모르고 + 실행기로 넘긴다(DIV의 인자로 했다면 그 길마다 고쳐야 했다). + - aup: `//`를 `/`와 같은 우선순위로 더했다(number는 FLOORDIV, i64는 I64_DIV). i64끼리의 `/`는 거절한다("an i64 divides by //"). + i64의 `/`를 쓰던 시험 셋(받아들인 꼴 하나, i64의 시험 앱, 무작위 프로그램의 i64 식)을 `//`로 고쳤다. + - 시험: 단위 시험 `floor_division`(7 // 2, −7 // 2, 7 // −2, 1 // 0.1 = 9, 0 // −3 = −0, 1e300 // 7, 0으로 나누기). aup의 시험이 무작위 + 200쌍의 `a // b`와 `a % b`를 파이썬과 비트까지(−0의 부호까지) 견준다. 진단 하나(305). + - 명령 수(b43 대 b42): 실행기만의 BFS −0.09%, 나머지 ±0.001% 안이다. 은행 질의(s42와 s43을 한 번에 번갈아 여섯 번)는 −0.20%에서 + 0.00% 사이다. **순서의 까닭.** - 1과 2가 묶음의 명세 대부분을 연다. 가장 값이 크다. diff --git a/research/experiments/040-against-c/2026-10-19-counts-s43.jsonl b/research/experiments/040-against-c/2026-10-19-counts-s43.jsonl new file mode 100644 index 0000000..3c376cb --- /dev/null +++ b/research/experiments/040-against-c/2026-10-19-counts-s43.jsonl @@ -0,0 +1,12 @@ +{"serve": "pure-serve", "per_branch": {"instructions": 27047243, "cycles": 11689798}, "in_credit": {"instructions": 3496991, "cycles": 1607154}, "large": {"instructions": 1412830, "cycles": 600237}, "ranks": {"instructions": 24415275, "cycles": 14986084}, "none": {"instructions": 617346, "cycles": 188795}, "field": {"instructions": 8917677, "cycles": 3131398}, "found": {"instructions": 14070799, "cycles": 10410020}} +{"serve": "pure-serve", "per_branch": {"instructions": 27096179, "cycles": 11779409}, "in_credit": {"instructions": 3508003, "cycles": 1643044}, "large": {"instructions": 1412862, "cycles": 589402}, "ranks": {"instructions": 24443281, "cycles": 14729351}, "none": {"instructions": 617346, "cycles": 197858}, "field": {"instructions": 8917574, "cycles": 3100270}, "found": {"instructions": 14069967, "cycles": 10336650}} +{"serve": "pure-serve", "per_branch": {"instructions": 27046648, "cycles": 11407491}, "in_credit": {"instructions": 3496871, "cycles": 1607966}, "large": {"instructions": 1412854, "cycles": 595377}, "ranks": {"instructions": 24415831, "cycles": 15099521}, "none": {"instructions": 617346, "cycles": 195509}, "field": {"instructions": 8917574, "cycles": 3112623}, "found": {"instructions": 14070670, "cycles": 10552319}} +{"serve": "pure-serve", "per_branch": {"instructions": 27172179, "cycles": 11797248}, "in_credit": {"instructions": 3525116, "cycles": 1604609}, "large": {"instructions": 1412766, "cycles": 596630}, "ranks": {"instructions": 24485315, "cycles": 14888250}, "none": {"instructions": 617346, "cycles": 190584}, "field": {"instructions": 8917574, "cycles": 3103366}, "found": {"instructions": 14067734, "cycles": 10384052}} +{"serve": "pure-serve", "per_branch": {"instructions": 27243839, "cycles": 11832991}, "in_credit": {"instructions": 3541033, "cycles": 1752046}, "large": {"instructions": 1412902, "cycles": 588650}, "ranks": {"instructions": 24523700, "cycles": 15063501}, "none": {"instructions": 617350, "cycles": 189045}, "field": {"instructions": 8917574, "cycles": 3096652}, "found": {"instructions": 14071735, "cycles": 10551854}} +{"serve": "pure-serve", "per_branch": {"instructions": 27166328, "cycles": 11864741}, "in_credit": {"instructions": 3523683, "cycles": 1637515}, "large": {"instructions": 1412846, "cycles": 605901}, "ranks": {"instructions": 24481539, "cycles": 14558275}, "none": {"instructions": 617346, "cycles": 188551}, "field": {"instructions": 8917574, "cycles": 3107277}, "found": {"instructions": 14070214, "cycles": 10469226}} +{"serve": "pure-serve", "per_branch": {"instructions": 27186157, "cycles": 11822639}, "in_credit": {"instructions": 3528111, "cycles": 1641510}, "large": {"instructions": 1412918, "cycles": 591550}, "ranks": {"instructions": 24491803, "cycles": 15034811}, "none": {"instructions": 617346, "cycles": 192547}, "field": {"instructions": 8917574, "cycles": 3098023}, "found": {"instructions": 14069918, "cycles": 10570428}} +{"serve": "pure-serve", "per_branch": {"instructions": 27075881, "cycles": 12587949}, "in_credit": {"instructions": 3503428, "cycles": 1662798}, "large": {"instructions": 1412878, "cycles": 589913}, "ranks": {"instructions": 24430910, "cycles": 14718538}, "none": {"instructions": 617449, "cycles": 189277}, "field": {"instructions": 8917574, "cycles": 3104978}, "found": {"instructions": 14070911, "cycles": 10608808}} +{"serve": "pure-serve", "per_branch": {"instructions": 27187691, "cycles": 11787503}, "in_credit": {"instructions": 3528550, "cycles": 1660533}, "large": {"instructions": 1412766, "cycles": 588274}, "ranks": {"instructions": 24493383, "cycles": 14704362}, "none": {"instructions": 617346, "cycles": 189544}, "field": {"instructions": 8917574, "cycles": 3119834}, "found": {"instructions": 14066702, "cycles": 10432332}} +{"serve": "pure-serve", "per_branch": {"instructions": 27005720, "cycles": 11716175}, "in_credit": {"instructions": 3487749, "cycles": 1557087}, "large": {"instructions": 1412870, "cycles": 592784}, "ranks": {"instructions": 24394115, "cycles": 14783563}, "none": {"instructions": 617346, "cycles": 192543}, "field": {"instructions": 8917574, "cycles": 3114559}, "found": {"instructions": 14071278, "cycles": 10343596}} +{"serve": "pure-serve", "per_branch": {"instructions": 27128475, "cycles": 11757578}, "in_credit": {"instructions": 3515189, "cycles": 1614634}, "large": {"instructions": 1412846, "cycles": 623225}, "ranks": {"instructions": 24460528, "cycles": 15001740}, "none": {"instructions": 617346, "cycles": 189534}, "field": {"instructions": 8917677, "cycles": 3096738}, "found": {"instructions": 14071143, "cycles": 10528194}} +{"serve": "pure-serve", "per_branch": {"instructions": 27154153, "cycles": 11748579}, "in_credit": {"instructions": 3520998, "cycles": 1620124}, "large": {"instructions": 1412790, "cycles": 587241}, "ranks": {"instructions": 24474542, "cycles": 14856006}, "none": {"instructions": 617346, "cycles": 189148}, "field": {"instructions": 8917575, "cycles": 3102194}, "found": {"instructions": 14066870, "cycles": 10315697}} diff --git a/research/experiments/041-batches/2026-10-19-counts.jsonl b/research/experiments/041-batches/2026-10-19-counts.jsonl index df0c154..3746b9e 100644 --- a/research/experiments/041-batches/2026-10-19-counts.jsonl +++ b/research/experiments/041-batches/2026-10-19-counts.jsonl @@ -18,3 +18,13 @@ {"version":"b42","way":"rounds","work":"matmul","instructions":21186409896,"cycles":7038688726} {"version":"b42","way":"batches","work":"bfs","instructions":3341462103,"cycles":1673340823} {"version":"b42","way":"rounds","work":"bfs","instructions":4709148541,"cycles":2184940702} +{"version":"b43","way":"batches","work":"roots","instructions":157246397,"cycles":109505019} +{"version":"b43","way":"rounds","work":"roots","instructions":2715503987,"cycles":999976397} +{"version":"b43","way":"batches","work":"knapsack","instructions":348031324,"cycles":105474340} +{"version":"b43","way":"rounds","work":"knapsack","instructions":22162562687,"cycles":7749754834} +{"version":"b43","way":"batches","work":"join","instructions":1783720391,"cycles":1012592603} +{"version":"b43","way":"rounds","work":"join","instructions":7723677646,"cycles":3345712383} +{"version":"b43","way":"batches","work":"matmul","instructions":702230543,"cycles":227373960} +{"version":"b43","way":"rounds","work":"matmul","instructions":21186409838,"cycles":6817006402} +{"version":"b43","way":"batches","work":"bfs","instructions":3341463903,"cycles":1640907916} +{"version":"b43","way":"rounds","work":"bfs","instructions":4705147005,"cycles":2086468365} |
