Resources Numerical accuracy Calculation policy

Numbers in a DWG file, read and written bit for bit

Reads and writes the number codes of a DWG file: full and compressed 64-bit values, whole numbers of several sizes and object handles.

How the precision is kept

A 64-bit value is moved between the file and memory as its eight bytes through a Float64Array view, with no arithmetic, so what is read is exactly what the file holds and what is written is exactly the value in the model. The compressed forms (a two-bit code for 0.0 and 1.0, a value that shares bytes with a default) are exact as well. Whole numbers wider than 53 bits are the only place a value can be changed.

Calculations under this policy

16 calculations in 2 source files. The code is read from the released app when this page loads. It is the calculation people run, not a copy kept for this page.

dwg/bits-read.js CadCraft 0.8.2 64-bit 7 calculations Reviewed

Tests bin/test-cadcraft-3d.mjs

Raw double, read

64-bit

A 64-bit value read from eight bytes of the bit stream.

The eight bytes are placed into a Float64Array's byte view and the number read back, with no arithmetic: every finite value, negative zero included, is exactly the stored one. The byte order is the machine's, so the code assumes a little-endian machine.

  • dwg/bits-read.js
  • lines 78 to 81
  • CadCraft 0.8.2
RD() {
  for (let i = 0; i < 8; i++) f64b[i] = this.RC();
  return f64[0];
}

Bit double, read

64-bit

A value stored as a two-bit code: a full 64-bit value, exactly 1.0 or exactly 0.0.

Codes 1 and 2 return the constants 1.0 and 0.0; code 0 reads a raw double. Code 3 is an error, not a value.

  • dwg/bits-read.js
  • lines 115 to 123
  • CadCraft 0.8.2
/** 2.5 BITDOUBLE. */
BD() {
  switch (this.BB()) {
    case 0: return this.RD();
    case 1: return 1.0;
    case 2: return 0.0;
    default: throw new DwgReadError(msg('ccdwg.err.bitdouble'));
  }
}

Double with default, read

64-bit

A value stored as the bytes in which it differs from a default value.

The default's bytes are loaded and the low four, or the low four and the next two, are replaced by bytes from the file; no arithmetic touches the value. The result is exact when the default passed in is the one the writer used.

  • dwg/bits-read.js
  • lines 124 to 141
  • CadCraft 0.8.2
/** 2.9 BitDouble with default. */
DD(def) {
  switch (this.BB()) {
    case 0: return def;
    case 1: {
      f64[0] = def;
      for (let i = 0; i < 4; i++) f64b[i] = this.RC();
      return f64[0];
    }
    case 2: {
      f64[0] = def;
      f64b[4] = this.RC(); f64b[5] = this.RC();
      for (let i = 0; i < 4; i++) f64b[i] = this.RC();
      return f64[0];
    }
    default: return this.RD();
  }
}

64-bit whole number, read

64-bit

An unsigned 64-bit whole number from two 32-bit halves.

value = high × 2³² + low

The halves are combined as an ordinary 64-bit number, which holds whole numbers exactly only up to 2^53. A larger value is rounded to the nearest representable number.

  • dwg/bits-read.js
  • line 77
  • CadCraft 0.8.2
RLL() { const lo = this.RL(), hi = this.RL(); return hi * 4294967296 + lo; }

Bit long long, read

64-bit

A whole number of up to seven bytes, the count given by three bits.

value = Σ byteᵢ × 256ⁱ

Summed as an ordinary number, low byte first. Seven bytes can hold up to 2^56, so a value above 2^53 is rounded.

  • dwg/bits-read.js
  • lines 108 to 114
  • CadCraft 0.8.2
/** 2.4 BITLONGLONG. */
BLL() {
  const n = this.bits(3);
  let v = 0, m = 1;
  for (let i = 0; i < n; i++) { v += this.RC() * m; m *= 256; }
  return v;
}

Handle reference, read

64-bit

An object handle, absolute or as an offset from another handle.

value = Σ byte × 256ᵏ; codes 6, 8, A, C: ref + 1, ref − 1, ref + value, ref − value

Handles are ordinary numbers, exact up to 2^53; the code comment says larger ones do not occur in practice. A relative code below a small reference can give a negative handle; the function does not check.

  • dwg/bits-read.js
  • lines 167 to 184
  • CadCraft 0.8.2
/**
 * 2.13 handle reference → absolute handle (Number; handles above 2^53 do not occur in practice).
 * `ref` is the handle offsets are taken from (codes 6, 8, A, C). `this.hcode` keeps the code.
 */
H(ref = 0) {
  const b = this.RC();
  const code = b >> 4, n = b & 15;
  let v = 0;
  for (let i = 0; i < n; i++) v = v * 256 + this.RC();
  this.hcode = code;
  switch (code) {
    case 6: return ref + 1;
    case 8: return ref - 1;
    case 0xA: return ref + v;
    case 0xC: return ref - v;
    default: return v;
  }
}

Modular char, read

64-bit

A signed whole number stored seven bits per byte.

value = Σ (byte & 0x7F) × 2^(7i); the last byte carries 6 bits and the sign

The groups are added with multiplications by powers of two, not with 32-bit shifts, so values above 2^31 are not truncated; exact up to 2^53.

  • dwg/bits-read.js
  • lines 267 to 278
  • CadCraft 0.8.2
/** Modular char, signed (2.6). */
export function readMC(b, pos) {
  let v = 0, shift = 0, byte;
  for (;;) {
    byte = b[pos++];
    if (byte & 0x80) { v += (byte & 0x7F) * 2 ** shift; shift += 7; continue; }
    const neg = byte & 0x40;
    v += (byte & 0x3F) * 2 ** shift;
    mod.pos = pos;
    return neg ? -v : v;
  }
}
dwg/bits-write.js CadCraft 0.8.2 64-bit 9 calculations Reviewed

Tests None found by the review

Raw double, written

64-bit

A 64-bit value written as its eight bytes.

The value is stored into a Float64Array and its eight bytes are written in order: no rounding and no conversion to text.

  • dwg/bits-write.js
  • line 54
  • CadCraft 0.8.2
RD(v) { f64[0] = v; for (let i = 0; i < 8; i++) this.RC(f64b[i]); }

Bit double, written

64-bit

A value written as a two-bit code for exactly 0.0 or 1.0, or as a full 64-bit value.

The short codes are used only for values exactly equal to 0 or 1. Negative zero is written in full, so its sign survives.

  • dwg/bits-write.js
  • lines 80 to 82
  • CadCraft 0.8.2
BD(v) {
  if (v === 0 && !Object.is(v, -0)) this.BB(2); else if (v === 1) this.BB(1); else { this.BB(0); this.RD(v); }
}

Double with default, written

64-bit

A value written as a two-bit code when it equals its default, or in full.

Only two forms are written: equal to the default, or all eight bytes. The partial forms that share bytes with the default are never written. Negative zero compares equal to a default of zero and is then written as the default.

  • dwg/bits-write.js
  • lines 83 to 84
  • CadCraft 0.8.2
/** BitDouble with default (2.9): same as the default → 00, else a full RD (11). */
DD(v, def) { if (v === def || (Number.isNaN(v) && Number.isNaN(def))) this.BB(0); else { this.BB(3); this.RD(v); } }

Thickness, written

64-bit

A thickness written as one bit when it is zero, or as a bit double.

The zero test is a truth test, so a thickness that is not a number is written as zero as well.

  • dwg/bits-write.js
  • lines 85 to 86
  • CadCraft 0.8.2
/** BitThickness (2.10): R2000+ one bit for 0.0. */
BT(v) { if (!v) this.B(1); else { this.B(0); this.BD(v); } }

Bit long, written

Exact

A 32-bit whole number in its short or full form.

The value is truncated toward zero to a whole number first, and a value that is not a number becomes 0. A value outside 32 bits is wrapped by the unsigned shift, not refused.

  • dwg/bits-write.js
  • lines 61 to 66
  • CadCraft 0.8.2
BL(v) {
  v = Math.trunc(v) || 0;
  if (v === 0) this.BB(2);
  else if (v > 0 && v < 256) { this.BB(1); this.RC(v); }
  else { this.BB(0); this.RL(v >>> 0); }
}

Bit short, written

Exact

A 16-bit whole number in its short or full form.

Truncated toward zero to a whole number; the full form keeps the low 16 bits, so a value outside 16 bits is wrapped.

  • dwg/bits-write.js
  • lines 55 to 60
  • CadCraft 0.8.2
BS(v) {
  v = Math.trunc(v) || 0;
  if (v === 0) this.BB(2); else if (v === 256) this.BB(3);
  else if (v > 0 && v < 256) { this.BB(1); this.RC(v); }
  else { this.BB(0); this.RS(v & 0xFFFF); }
}

Bit long long, written

Exact

A 64-bit whole number as a three-bit byte count and its bytes.

The value goes through BigInt, limited to 64 unsigned bits, and is split into bytes with BigInt shifts, so every bit of a BigInt input is kept. An ordinary number passed in is exact only up to 2^53, and one with a fraction is refused by BigInt with an error.

  • dwg/bits-write.js
  • lines 73 to 79
  • CadCraft 0.8.2
BLL(v) {
  let x = BigInt.asUintN(64, BigInt(v || 0));
  const bytes = [];
  while (x > 0n) { bytes.push(Number(x & 0xFFn)); x >>= 8n; }
  this.bits(bytes.length, 3);
  for (const b of bytes) this.RC(b);
}

Point, written

64-bit

The three coordinates of a point as bit doubles.

A missing coordinate, and one that is not a number, is written as 0; every other value is written unchanged.

  • dwg/bits-write.js
  • line 96
  • CadCraft 0.8.2
P3(p) { this.BD3(p?.x || 0, p?.y || 0, p?.z || 0); }

Handle bytes, written

Exact

A handle as its bytes, most significant first.

byte = v mod 256; v = ⌊v ÷ 256⌋

Division and remainder on an ordinary number: exact for handles up to 2^53. A handle given as text is read as hexadecimal; a negative one is written as 0.

  • dwg/bits-write.js
  • lines 179 to 184
  • CadCraft 0.8.2
export function handleBytes(value) {
  const bytes = [];
  let v = typeof value === 'string' ? parseInt(value, 16) || 0 : Math.max(0, Math.trunc(value || 0));
  while (v > 0) { bytes.unshift(v % 256); v = Math.floor(v / 256); }
  return bytes;
}

In detail

Method
Bit codes of the Open Design Specification for .dwg files v5.4.1, chapter 2, as the file headers cite: RD, BD, DD, BT, BE, BS, BL, BLL, RLL, handles, modular chars and shorts.
Number types
64-bit values as raw bytes; whole numbers as 64-bit numbers on reading, BigInt for the 64-bit forms on writing.
Limits
A 64-bit whole number or handle above 2^53 read from a file is rounded, because it is assembled as an ordinary number. The byte copy assumes a little-endian machine. A whole-number field wider than its code is wrapped, not refused.

Approach: 64-bit, direct

Every value is a 64-bit number from input to result. Formulas are closed form or a direct solve, and every comparison uses a stated tolerance.

Every policy that takes this approach