SGTIN-96 Encoding, Bit by Bit: Two Worked Examples From the Ratified Tag Data Standard
Encoding a GTIN into a 96-bit EPC is arithmetic, not magic: four fixed-width fields, one 3-bit code that tells a decoder how to split the fifth, and a serial. The trouble starts when an encoder and a trading partner disagree about the result, because the tag reads back cleanly at both ends and the GTIN survives the round trip intact.
This page works the encoding by hand twice, at two partition values, against clause and page numbers from the face of the ratified standard. Both hex results were re-derived bit by bit from the six field values rather than copied from any printed string.
Pin the version first
Every number, clause and page reference below comes from one document: the EPC Tag Data Standard (TDS), Release 2.3, Ratified, Oct 2025. The Document Summary table on page 2 records Document Version 2.3, Document Date Oct 2025 and Document Status Ratified; the running footer of all 448 pages reads Release 2.3, Ratified, Oct 2025.
Pinning the version matters here because the scheme list has moved underneath the SGTIN. Pages 33 and 34 state that TDS 2.3 is fully backward-compatible with 2.2 (p33) and introduce a further twelve EPC schemes, including SGTIN++ and DSGTIN++ (p34); page 28 records that TDS 2.0 itself introduced twelve new schemes that use no partition table at all. The SGTIN-96 layout has not moved in years — which is precisely why a page that is vague about the version can still look right, and why every reference below carries both a clause and a page.
ref.gs1.org/standards/tds/ always resolves to the current ratified release; ref.gs1.org/standards/tds/2.3.0/ is the version-pinned deep link. One note for anyone wiring a check into CI: the www.gs1.org standards landing path answers automated requests with HTTP 403, while both ref.gs1.org URLs return the PDF with HTTP 200.
The SGTIN-96 bit layout, reproduced from Table 14-17
Table 14-17, page 238, gives the layout: six logical segments but only four coding segments, because the partition value, the company prefix and the indicator/item reference collapse into one 47-bit GTIN segment that the partition table alone knows how to split.
| Segment | Bits | Counting down (TDS pre-2.0 convention) | Counting up (b0 = first bit of the memory bank) |
|---|---|---|---|
| EPC Header | 8 | b95–b88 | b0–b7 |
| Filter | 3 | b87–b85 | b8–b10 |
| GTIN coding segment (partition + prefix + indicator/item ref) | 47 | b84–b38 | b11–b57 |
| Serial | 38 | b37–b0 | b58–b95 |
| Total | 96 |
Both conventions are printed because they disagree: page 236 notes that the older “counting down” numbering is the opposite of the way Gen 2 memory addresses are written, where address 0 is the most significant bit. Check which row you are on before counting.
The header comes from Table 14-1, whose caption and column headings sit on page 149 and whose SGTIN rows sit on page 150: binary 0011 0000 = hex 0x30 = SGTIN-96 at 96 bits; binary 0011 0110 = hex 0x36 = SGTIN-198 at 198 bits. Table 14-18, page 239, shows SGTIN-198 keeping exactly the same 47-bit GTIN coding segment and swapping the 38-bit integer serial for a 140-bit String-encoded serial — 20 characters at 7 bits each. That is the whole difference, which is why changing scheme costs tag memory but no re-derivation of the GTIN part.
The partition table, and its two self-checking invariants
Table 14-16, page 237, is the entire mechanism. Here it is in full, with two columns added that the standard does not print but that every implementer should have in front of them.
| Partition value (P) | Company Prefix bits (M) | Company Prefix digits (L) | Indicator + Item Ref bits (N) | Indicator + Item Ref digits (K) | M + N | L + K |
|---|---|---|---|---|---|---|
| 0 | 40 | 12 | 4 | 1 | 44 | 13 |
| 1 | 37 | 11 | 7 | 2 | 44 | 13 |
| 2 | 34 | 10 | 10 | 3 | 44 | 13 |
| 3 | 30 | 9 | 14 | 4 | 44 | 13 |
| 4 | 27 | 8 | 17 | 5 | 44 | 13 |
| 5 | 24 | 7 | 20 | 6 | 44 | 13 |
| 6 | 20 | 6 | 24 | 7 | 44 | 13 |
Those last two columns are constant on every row and are the fastest sanity check in the exercise. M + N = 44 always; add the 3 partition bits and you have the 47-bit GTIN coding segment Table 14-17 demands. L + K = 13 always, which is the grammar rule on page 58 — SGTINURIBody = 2(PaddedNumericComponent “.”) GS3A3Component, with the stated requirement that the two PaddedNumericComponent fields total 13 characters, not counting the dots.
The debugging rule is blunt: count the characters either side of the first two dots in your EPC URI. If they do not total 13, stop looking at the binary — no partition row will match.
Why your SGTIN encodes wrong: the leading-zero trap
This is the failure we see most often, and it is worth understanding why it is so quiet. The validity test in clause 14.3.3, page 155, says the number of digits in C must match a value in the “GS1 Company Prefix Digits (L)” column. The row is selected by the string length of C; only afterwards is C converted to an integer and packed into M bits. Clause 5, page 52, defines PaddedNumericComponent = 1*Digit — digit characters, with no rule stripping leading zeros. A leading zero is a significant character.
Take the standard’s own GTIN-12 example, clause 7.3.1, page 79. GTIN-12 614141123452 becomes 14 digits by prepending two zeros: 00614141123452. The company prefix entering the EPC is therefore the seven-character string 0614141, not the six-digit UPC company prefix 614141, and seven characters selects partition 5. TDS prints the answer outright: urn:epc:id:sgtin:0614141.012345.Serial.
Tell the encoder the prefix is six digits instead and it takes 061414 from the padded GTIN, landing you on partition 6 — which is also internally consistent, because 6 + 7 = 13, the grammar is satisfied and the encoder raises nothing. Note the two different six-digit strings in play: 614141 is the UPC company prefix before padding, 061414 is what an encoder mechanically slices off the padded GTIN once you assert L = 6. Both encodings of the same product, serial 1234, filter 1:
| Correct — partition 5 | Mistaken — partition 6 | |
|---|---|---|
| Tag URI | urn:epc:tag:sgtin-96:1.0614141.012345.1234 | urn:epc:tag:sgtin-96:1.061414.0112345.1234 |
| Partition bits | 101 | 110 |
| C packed into | 24 bits | 20 bits |
| D packed into | 20 bits | 24 bits |
| Hex EPC | 3034257BF40C0E40000004D2 | 30383BF9806DB640000004D2 |
| Decodes back to GTIN-14 | 00614141123452 | 00614141123452 |
Read that last row again. Both tags decode to the identical GTIN. That is why the fault survives testing: anyone comparing GTINs — a warehouse screen, a packing slip, a goods-in scan — sees a perfect match. The divergence lives only in the pure identity URI, and therefore in the hex on the tag, so it surfaces where nobody looks first: an EPCIS event keyed on the EPC URN, where urn:epc:id:sgtin:0614141.012345.1234 and urn:epc:id:sgtin:061414.0112345.1234 are two different objects.
The fix is data governance, not code: the authoritative length of your GS1 Company Prefix comes from your prefix certificate or a GEPIR lookup, counted after the GTIN has been padded to 14 digits.
The indicator digit moves; the check digit goes
Clause 7.3, page 78, writes the EPC URI as urn:epc:id:sgtin:d2…d(L+1).d1 d(L+2)…d13.s1…sK against the element string (01)d1d2…d14 (21)s1…sK. Two things become visible at once: the first URI component starts at d2, and the second begins with d1 — the indicator digit — before continuing from d(L+2). The indicator has jumped across the dot. Step 3 of the same procedure, page 79, states that the check digit d14 is not included in the EPC URI, and Table 14-17’s second footnote adds that for a GTIN-12 or GTIN-13 a zero pad digit takes the indicator’s place.
The GS1 US worksheet turns both into numbered steps with reasons. Step 4, “Drop Your Check Digit”, notes that EPC technology uses other forms of checking. Step 5, “Move Your Indicator Digit”, gives the purpose as speeding up reader performance in finding products from a particular brand owner — put the company prefix contiguously at the front and a Gen 2 Select can mask on it directly. The full ladder:
| Step | Result | What changed |
|---|---|---|
| UPC-A barcode | 614141 00734 9 | UPC company prefix, item reference, check digit |
| 2. Add the leading zero | 0614141 00734 9 | UPC prefix becomes a 7-digit GS1 Company Prefix |
| 3. Pad to 14 digits | 0 0614141 00734 9 | Filler digit added in the indicator position |
| 4. Drop the check digit | 0 0614141 00734 | d14 discarded |
| 5. Move the indicator | 0614141 | 0 00734 | Indicator crosses the dot, leads the item reference |
| EPC URI | urn:epc:id:sgtin:0614141.000734.31459 | 7 + 6 = 13 characters, serial appended |
The check digit is not lost, only unstored. Clause 7.3 recomputes it on decode as d14 = (10 − ((3(d1+d3+d5+d7+d9+d11+d13) + (d2+d4+d6+d8+d10+d12)) mod 10)) mod 10 — how a reader hands you a complete GTIN from a tag that never carried one.
Worked example one: TDS Appendix E.1 at partition 1
Appendix E.1, pages 352–353, encodes (01)09506000134352(21)6789: pure identity urn:epc:id:sgtin:95060001343.05.6789, tag URI urn:epc:tag:sgtin-96:3.95060001343.05.6789. The prefix component is eleven characters, so partition 1 applies — M = 37, N = 7, K = 2. Field by field:
- Header, SGTIN-96: 00110000
- Filter, value 3: 011
- Partition, value 1: 001
- C = 95060001343 as a 37-bit integer: 1011000100010000001001000001000111111
- D = 05 as a 7-bit integer: 0000101
- Serial = 6789 as a 38-bit integer: 00000000000000000000000001101010000101
Concatenated, 8 + 3 + 3 + 37 + 7 + 38 = 96 bits:
001100000110011011000100010000001001000001000111111000010100000000000000000000000001101010000101
That 96-bit string is character-identical to the one printed in Appendix E.1. Converted to hexadecimal it is 3066C4409047E14000001A85. Be precise about what the appendix does and does not print: E.1 gives the binary string and the Gen 2 memory map, not a hex EPC. The only hex TDS prints for this GTIN is in section E.3, page 356 — 3066C4409047E140075BCD15 — which is the same identity at serial 123456789 rather than 6789, so the two differ in the serial field alone. The value above was reached by computation from the six field values.
Two repeatable checks. The check digit: d1…d13 = 0950600013435 gives d14 = 2, so 09506000134352 is self-consistent. And the memory-bank row shows PC Length bits 00110 = 6; those bits count 16-bit words in the EPC field, and 96 / 16 = 6 exactly.
Two things worth knowing before you copy this appendix
The figure states Filter Value = 3 and shows filter bits 011 — and Table 10-1 of the same document marks value 3 as Reserved. Clause 10.1 asks applications not to direct an encoder to write a reserved value. Treat the appendix as an illustration of the arithmetic and substitute filter 1 for an actual point-of-sale trade item.
Second, Note 1 on page 352 says the GS1 Company Prefix here “is shown to be seven digits”, while the figure beneath it uses the eleven-digit prefix 95060001343 at partition 1. Page 29 explains why: the TDS 2.0 change log records that most encoding examples were updated to sample GCP 9521141 and that the section E SGTIN examples moved to GTIN 09506000134352 to illustrate a resolvable GS1 Digital Link URI. The note kept the older example’s digit count. Go by the figure.
Worked example two: the GS1 US UPC worksheet at partition 5
Running the same method at another partition shows which widths are fixed and which move. The GS1 US worksheet takes UPC-A 614141007349 through eleven steps; the check digit arithmetic confirms the trailing 9. Steps 1 to 6 yield prefix 0614141, indicator-plus-item-reference 000734, serial 31459; steps 7 to 9 select header 48 decimal / 30 hex, filter 1 for a point-of-sale trade item, and partition 5. Step 10 converts to binary:
- Header (8 bits): 00110000
- Filter (3 bits): 001
- Partition (3 bits): 101
- GS1 Company Prefix, 614141 (24 bits): 000010010101111011111101
- Indicator + item reference, 734 (20 bits): 00000000001011011110
- Serial, 31459 (38 bits): 00000000000000000000000111101011100011
Step 11 gives hex 3034257BF400B78000007AE3. Rebuilt independently from the six field values, the result matches to the character.
Now decode it back using the clause 14.4.3 validity test, page 164: partition bits 101 select the row where L = 7 and K = 6; C must be less than 10^L, and 614141 < 10,000,000 passes; D must be less than 10^K, and 734 < 1,000,000 passes. Pad each to its digit count and you get urn:epc:id:sgtin:0614141.000734.31459. The leading zero in 0614141 is reconstructed from the partition value alone — which is the whole reason that value has to be right.
GS1 US publishes an EPC Encoder/Decoder; its page notes that each GS1 identification key — GTIN, GLN, SSCC, GRAI, GIAI, GSRN and GDTI among others — may be encoded in an EPC structure. Use it — and use this page to know what it is doing, so that when its output and a partner’s disagree you can name the field that differs.
Across both examples, every width the partition table controls changed — 37 prefix bits became 24, 7 item-reference bits became 20 — while the header, the filter, the 47-bit total and the 38-bit serial never moved. That is the invariant to hold on to when reading an unfamiliar dump in an inventory or stock-count application.
Filter values: the table, the reserved values, and what the field is for
Table 10-1, page 124, in full:
| Filter value | Binary | Type |
|---|---|---|
| 0 | 000 | All Others (see Section 10.1) |
| 1 | 001 | Point of Sale (POS) Trade Item |
| 2 | 010 | Full Case for Transport |
| 3 | 011 | Reserved (see Section 10.1) |
| 4 | 100 | Inner Pack Trade Item Grouping for Handling |
| 5 | 101 | Reserved (see Section 10.1) |
| 6 | 110 | Unit Load |
| 7 | 111 | Unit inside Trade Item or component inside a product not intended for individual sale |
Two of the eight slots — 3 and 5 — are Reserved. Clause 10.1 is explicit: implementations SHALL accept any filter value, reserved or not, but applications SHOULD NOT direct an encoder to write one, nor rely on one decoded from a tag, because a future revision may assign it. Check your encoding tool’s drop-down against this table rather than against whatever label it displays.
The standard’s footnotes define the two most often guessed at: “Full Case for Transport” is a case whose composition of POS trade items is standardised via master data and reorderable by a single GTIN, and “Unit Load” extends that to trade items on a pallet or other load carrier — rolly, dolly, tote, garment rack, bag or sack.
Clause 10 sets out what the field is for, and it is narrower than most assume. The filter value is control information: not part of the EPC, contributing nothing to unique identity, omitted from the pure identity URI, the element string and the GS1 Digital Link URI, and expressly not intended as a reliable packaging-level indicator for business applications. Its job is the air interface — the standard’s own example is finding one pallet tag among thousands of item tags, where a Gen 2 Select on the filter bits deselects the items. That is a real gain at a dock door or a pallet-level read point. Treat it as a data-capture aid and it earns its three bits.
The limits that force your scheme choice, and the codes reserved for other uses
Clause 14.6.1, page 237, states the SGTIN-96 serial constraint in one sentence: numeric-only serial numbers, without leading zeros, whose value is less than 2^38 — that is, from 0 through 274,877,906,943, inclusive. The carve-out for a serial that is itself the single digit 0 sits elsewhere in the document: clause 6.3.1, page 58, and Note 3 of Appendix E.1, page 352. Anything outside those limits calls for SGTIN-198, which buys up to 20 alphanumeric characters for 102 extra bits of EPC memory. Three questions settle it:
- Is every serial digits only? A letter, hyphen or slash means SGTIN-198.
- Does any serial need a leading zero preserved? Batch-style serials like 000417 are the usual culprit.
- Is your largest future serial below 274,877,906,943?
Three yeses and SGTIN-96 is the economical answer; the pure identity URI is identical either way, so the choice is purely tag memory and cost.
A second limit bites on the decode path. Clause 14.4.3 requires C < 10^L and D < 10^K, so not every bit pattern is legal — the binary field is wider than the decimal range it carries, and the headroom varies sharply:
| Partition | Prefix field: 10^L / 2^M | Item-ref field: 10^K / 2^N |
|---|---|---|
| 0 | 90.9% | 62.5% |
| 1 | 72.8% | 78.1% |
| 2 | 58.2% | 97.7% |
| 3 | 93.1% | 61.0% |
| 4 | 74.5% | 76.3% |
| 5 | 59.6% | 95.4% |
| 6 | 95.4% | 59.6% |
At partition 2, more than 40 per cent of the prefix field’s bit patterns decode to an illegal value: M = 34 and L = 10, so 10^10 of 2^34 patterns are legal and the other 41.8 per cent are not. Middleware that packs an integer straight into M bits without a range test will emit an eleven-digit number — 2^34 − 1 is 17,179,869,183 — into a ten-digit field, producing a tag a conformant decoder rejects. The same arithmetic at partition 1 is a twelve-digit value in an eleven-digit field. A few lines prevent both:
- read partition P from the 3 bits following the filter; reject if P > 6
- look up M, L, N, K from Table 14-16
- C = next M bits as unsigned integer; reject unless C < 10^L
- D = next N bits as unsigned integer; reject unless D < 10^K
- emit zero-padded C to L digits, dot, zero-padded D to K digits
Enforcing that at the edge, before an event reaches your application, is one of the quieter jobs a reader-side middleware layer such as ReaderSense Edge earns its place doing.
Two cases belong in a pre-flight check. GTIN-8 has its own rule in clause 7.3.2, page 79: prepend five zeros to the first three digits for an eight-character prefix, take N4 to N7 as the item reference, indicator zero. The standard’s example, GTIN-8 95010939, becomes urn:epc:id:sgtin:00000950.01093.Serial — eight plus five is thirteen, so partition 4. And clauses 7.3.3 to 7.3.5, page 80, reserve ranges for internal and restricted circulation: RCN-8 codes beginning with GS1-8 prefix 0 or 2, codes beginning 04 or 0001–0007, and codes beginning 02 or 20–29. TDS states these SHALL NOT be used to construct SGTIN EPCs. Screen for them before a serialisation run — they turn up often in retail product masters carrying years of in-store codes.
Where the partition table goes next
The mechanism above is now one of two approaches in the standard, and the second shapes what to expect from tags over the next few years.
TDS 2.0 introduced twelve EPC schemes that use no partition table. Page 28 puts it directly: for the new binary encodings the length of the GS1 Company Prefix is neither significant nor does it need to be known, so the leading-zero trap cannot arise in them. The primary identifier is encoded at 4 bits per digit on nibble boundaries, placing the GTIN and company prefix at well-defined bit positions relative to the start of the EPC memory bank irrespective of any indicator or extension digit — which is what makes a Select mask predictable without a lookup.
SGTIN+, SGTIN++, DSGTIN+ and DSGTIN++ carry forward the filter values already defined for SGTIN-96 and SGTIN-198, so Table 10-1 still applies. No URN syntax is defined for them; they map to element strings and GS1 Digital Link URIs, which suffices because EPCIS and CBV 2.0 accept a constrained subset of Digital Link URIs in place of pure identity URNs.
The one to watch is DSGTIN+, which places a critical date before the GTIN in the binary encoding. The payoff, in the standard’s own words on page 28, is that a reader can select products from any brand owner whose use-by or sell-by date matches a specified value, so they can be removed from the sales area or discounted for quick sale. For perishables and crate-level reads at an intake dock, that reordering changes what one inventory pass can do. TDS 2.3 adds the ‘++’ family, which encodes a custom domain name losslessly after the serial so an EPC round-trips to a Digital Link URI on a brand owner’s own hostname.
Existing schemes remain valid and are not deprecated, and the installed base of encoders, label printers and trading-partner integrations means partition tables will be on tags for years yet. If you are specifying a new system, ask your reader supplier which header values their firmware and SDK decode today. We build our reader hardware and software in-house, so that question reaches the people who wrote the decoder.
Frequently asked questions
How do I convert a GTIN to an SGTIN-96 EPC?
Pad the GTIN to 14 digits, drop the check digit, move the indicator digit to the front of the item reference, then count the digits in your GS1 Company Prefix and look that length up in the (L) column of Table 14-16 to get the partition value. Pack header (8 bits), filter (3), partition (3), prefix (M bits), indicator-plus-item-reference (N bits) and serial (38 bits). The widths M and N come from the partition row you selected, and M + N is always 44.
Which partition value should I use for my GS1 company prefix?
The one whose “GS1 Company Prefix Digits (L)” entry equals the character count of your prefix after the GTIN has been padded to 14 digits. Seven characters is partition 5, eight is partition 4, nine is partition 3, and so on up to twelve characters at partition 0. Clause 14.3.3 selects the row by string length, so a leading zero counts as a character.
Why does my UPC need a leading zero before encoding to an EPC?
Because a GTIN-12 becomes a 14-digit GTIN by prepending two zeros, and one of those zeros lands inside the company prefix field. TDS clause 7.3.1 gives the example: GTIN-12 614141123452 becomes 00614141123452, and the EPC company prefix is the seven-character string 0614141, which selects partition 5 rather than partition 6. Both choices produce a tag that decodes back to the same GTIN, so the error stays invisible until an EPC URN is compared with a trading partner’s.
What does the filter value in an SGTIN mean?
It is three bits of control information used to select or deselect tags over the Gen 2 air interface — for example, to find one pallet tag among thousands of item tags. Clause 10 of TDS is explicit that it is not part of the EPC, does not contribute to unique identity, is omitted from the pure identity URI and element string, and is not intended as a reliable packaging-level indicator for business applications.
What is the maximum serial number in SGTIN-96?
274,877,906,943. The serial field is 38 bits, so the value must be less than 2^38. Clause 14.6.1, page 237, gives the constraint as numeric-only serial numbers, without leading zeros, from 0 through 274,877,906,943 inclusive. The carve-out for a serial that is the single digit 0 is stated elsewhere — clause 6.3.1, page 58, and Note 3 of Appendix E.1, page 352.
When do I have to use SGTIN-198 instead of SGTIN-96?
Whenever a serial contains a non-digit character, needs a leading zero preserved, or exceeds 274,877,906,943. SGTIN-198 carries up to 20 characters in a 140-bit String-encoded field while keeping the same 47-bit GTIN coding segment, so only the serial handling changes. The EPC pure identity URI is identical either way.
What is the current version of the EPC Tag Data Standard?
Release 2.3, Ratified, Oct 2025, running to 448 pages. It is fully backward-compatible with TDS 2.2 and adds twelve new EPC schemes including SGTIN++ and DSGTIN++. The current release always resolves at ref.gs1.org/standards/tds/, and the version-pinned copy at ref.gs1.org/standards/tds/2.3.0/.