resistor_color
Resistor colour code per IEC 60062: decode 3, 4, 5 or 6 bands into resistance, tolerance, limits and temperature coefficient, or encode a value (4700, "4k7", "R47", "10 kΩ") into its 4-, 5- and 6-band codes; find the nearest standard value in the E3 to E192 series of IEC 60063 and which series a value belongs to. Bundled dated table of the colours and the official series values.
Install
/tools install resistor_color /tools install resistor_color@1.0.0
In a console session, or let the model find it in normal mode: axlr_catalog describes it and axlr_install_tool asks for the same install card. The card reads catalog axlr · resistor_color 1.0.0 · verified once the index signature, the manifest digest and every file digest check out. Then the model runs it with axlr_run_tool resistor_color; each run is approved as a local_exec.
Arguments
One JSON object on stdin, validated against the schema before the program starts.
| name | type | required | default | meaning |
|---|---|---|---|---|
operation | string | no | Default: decode when bands is given, else encode. one of "decode", "encode", "nearest" | |
bands | array of string | no | Colour names from the head end: black brown red orange yellow green blue violet grey white gold silver pink. items ≥ 3; items ≤ 6 | |
value | number | string | no | Ohms: 4700, "4.7k", "4k7", "R47", "2.2 MΩ" | |
tolerance | number | string | no | Percent for the tolerance band (default 5 for 4 bands, 1 for 5 bands) | |
tcr | integer | no | ppm/K for a sixth band: 250, 100, 50, 15, 25, 20, 10, 5, 1 | |
series | string | no | "E24" | one of "E3", "E6", "E12", "E24", "E48", "E96", "E192" |
Input schema (JSON)
{
"type": "object",
"additionalProperties": false,
"properties": {
"operation": {
"type": "string",
"enum": [
"decode",
"encode",
"nearest"
],
"description": "Default: decode when bands is given, else encode"
},
"bands": {
"type": "array",
"minItems": 3,
"maxItems": 6,
"items": {
"type": "string"
},
"description": "Colour names from the head end: black brown red orange yellow green blue violet grey white gold silver pink"
},
"value": {
"type": [
"number",
"string"
],
"description": "Ohms: 4700, \"4.7k\", \"4k7\", \"R47\", \"2.2 MΩ\""
},
"tolerance": {
"type": [
"number",
"string"
],
"description": "Percent for the tolerance band (default 5 for 4 bands, 1 for 5 bands)"
},
"tcr": {
"type": "integer",
"description": "ppm/K for a sixth band: 250, 100, 50, 15, 25, 20, 10, 5, 1"
},
"series": {
"type": "string",
"enum": [
"E3",
"E6",
"E12",
"E24",
"E48",
"E96",
"E192"
],
"default": "E24"
}
}
}
Example
Input
{
"bands": [
"brown",
"black",
"red",
"gold"
]
}
Output (stdout)
{
"ohms": "1000",
"value": "1 kΩ",
"tolerance_pct": "5",
"min_ohms": "950",
"max_ohms": "1050"
}
Files
The command the install card registers is python3 resistor_color.py, run in the workspace with PATH and HOME as its only environment. Each file's SHA-256 is in the signed index; the console checks them before showing the card and again before writing.
codes.json · sha256 3e1a22a9572b48f10268bd8db241f6333579261aac44658ff862599fdcb647a3
{
"generated": "2026-10-18",
"generator": "tools/catalog/data/resistor_color.py",
"source": "IEC 60062:2016 colour code; IEC 60063:2015 E series (as reproduced in Wikipedia, 'Electronic color code' and 'E series of preferred numbers', retrieved 2026-10-18)",
"licence": "factual values only (no text of IEC 60062/60063 or of Wikipedia, CC BY-SA 4.0, is copied)",
"no_band_tolerance_pct": "20",
"colours": {
"pink": {
"digit": null,
"multiplier_exp": -3,
"tolerance_pct": null,
"tcr_ppm_per_k": null,
"code": "PK"
},
"silver": {
"digit": null,
"multiplier_exp": -2,
"tolerance_pct": "10",
"tcr_ppm_per_k": null,
"code": "SR"
},
"gold": {
"digit": null,
"multiplier_exp": -1,
"tolerance_pct": "5",
"tcr_ppm_per_k": null,
"code": "GD"
},
"black": {
"digit": 0,
"multiplier_exp": 0,
"tolerance_pct": null,
"tcr_ppm_per_k": 250,
"code": "BK"
},
"brown": {
"digit": 1,
"multiplier_exp": 1,
"tolerance_pct": "1",
"tcr_ppm_per_k": 100,
"code": "BN"
},
"red": {
"digit": 2,
"multiplier_exp": 2,
"tolerance_pct": "2",
"tcr_ppm_per_k": 50,
"code": "RD"
},
"orange": {
"digit": 3,
"multiplier_exp": 3,
"tolerance_pct": "0.05",
"tcr_ppm_per_k": 15,
"code": "OG"
},
"yellow": {
"digit": 4,
"multiplier_exp": 4,
"tolerance_pct": "0.02",
"tcr_ppm_per_k": 25,
"code": "YE"
},
"green": {
"digit": 5,
"multiplier_exp": 5,
"tolerance_pct": "0.5",
"tcr_ppm_per_k": 20,
"code": "GN"
},
"blue": {
"digit": 6,
"multiplier_exp": 6,
"tolerance_pct": "0.25",
"tcr_ppm_per_k": 10,
"code": "BU"
},
"violet": {
"digit": 7,
"multiplier_exp": 7,
"tolerance_pct": "0.1",
"tcr_ppm_per_k": 5,
"code": "VT"
},
"grey": {
"digit": 8,
"multiplier_exp": 8,
"tolerance_pct": "0.01",
"tcr_ppm_per_k": 1,
"code": "GY"
},
"white": {
"digit": 9,
"multiplier_exp": 9,
"tolerance_pct": null,
"tcr_ppm_per_k": null,
"code": "WH"
}
},
"series": {
"E3": {
"tolerance_pct": "40",
"values": [
"1.0",
"2.2",
"4.7"
],
"formula_exceptions": [
{
"n": 2,
"official": "4.7",
"formula": "4.6"
}
]
},
"E6": {
"tolerance_pct": "20",
"values": [
"1.0",
"1.5",
"2.2",
"3.3",
"4.7",
"6.8"
],
"formula_exceptions": [
{
"n": 3,
"official": "3.3",
"formula": "3.2"
},
{
"n": 4,
"official": "4.7",
"formula": "4.6"
}
]
},
"E12": {
"tolerance_pct": "10",
"values": [
"1.0",
"1.2",
"1.5",
"1.8",
"2.2",
"2.7",
"3.3",
"3.9",
"4.7",
"5.6",
"6.8",
"8.2"
],
"formula_exceptions": [
{
"n": 5,
"official": "2.7",
"formula": "2.6"
},
{
"n": 6,
"official": "3.3",
"formula": "3.2"
},
{
"n": 7,
"official": "3.9",
"formula": "3.8"
},
{
"n": 8,
"official": "4.7",
"formula": "4.6"
},
{
"n": 11,
"official": "8.2",
"formula": "8.3"
}
]
},
"E24": {
"tolerance_pct": "5",
"values": [
"1.0",
"1.1",
"1.2",
"1.3",
"1.5",
"1.6",
"1.8",
"2.0",
"2.2",
"2.4",
"2.7",
"3.0",
"3.3",
"3.6",
"3.9",
"4.3",
"4.7",
"5.1",
"5.6",
"6.2",
"6.8",
"7.5",
"8.2",
"9.1"
],
"formula_exceptions": [
{
"n": 10,
"official": "2.7",
"formula": "2.6"
},
{
"n": 11,
"official": "3.0",
"formula": "2.9"
},
{
"n": 12,
"official": "3.3",
"formula": "3.2"
},
{
"n": 13,
"official": "3.6",
"formula": "3.5"
},
{
"n": 14,
"official": "3.9",
"formula": "3.8"
},
{
"n": 15,
"official": "4.3",
"formula": "4.2"
},
{
"n": 16,
"official": "4.7",
"formula": "4.6"
},
{
"n": 22,
"official": "8.2",
"formula": "8.3"
}
]
},
"E48": {
"tolerance_pct": "2",
"values": [
"1.00",
"1.05",
"1.10",
"1.15",
"1.21",
"1.27",
"1.33",
"1.40",
"1.47",
"1.54",
"1.62",
"1.69",
"1.78",
"1.87",
"1.96",
"2.05",
"2.15",
"2.26",
"2.37",
"2.49",
"2.61",
"2.74",
"2.87",
"3.01",
"3.16",
"3.32",
"3.48",
"3.65",
"3.83",
"4.02",
"4.22",
"4.42",
"4.64",
"4.87",
"5.11",
"5.36",
"5.62",
"5.90",
"6.19",
"6.49",
"6.81",
"7.15",
"7.50",
"7.87",
"8.25",
"8.66",
"9.09",
"9.53"
],
"formula_exceptions": []
},
"E96": {
"tolerance_pct": "1",
"values": [
"1.00",
"1.02",
"1.05",
"1.07",
"1.10",
"1.13",
"1.15",
"1.18",
"1.21",
"1.24",
"1.27",
"1.30",
"1.33",
"1.37",
"1.40",
"1.43",
"1.47",
"1.50",
"1.54",
"1.58",
"1.62",
"1.65",
"1.69",
"1.74",
"1.78",
"1.82",
"1.87",
"1.91",
"1.96",
"2.00",
"2.05",
"2.10",
"2.15",
"2.21",
"2.26",
"2.32",
"2.37",
"2.43",
"2.49",
"2.55",
"2.61",
"2.67",
"2.74",
"2.80",
"2.87",
"2.94",
"3.01",
"3.09",
"3.16",
"3.24",
"3.32",
"3.40",
"3.48",
"3.57",
"3.65",
"3.74",
"3.83",
"3.92",
"4.02",
"4.12",
"4.22",
"4.32",
"4.42",
"4.53",
"4.64",
"4.75",
"4.87",
"4.99",
"5.11",
"5.23",
"5.36",
"5.49",
"5.62",
"5.76",
"5.90",
"6.04",
"6.19",
"6.34",
"6.49",
"6.65",
"6.81",
"6.98",
"7.15",
"7.32",
"7.50",
"7.68",
"7.87",
"8.06",
"8.25",
"8.45",
"8.66",
"8.87",
"9.09",
"9.31",
"9.53",
"9.76"
],
"formula_exceptions": []
},
"E192": {
"tolerance_pct": "0.5",
"values": [
"1.00",
"1.01",
"1.02",
"1.04",
"1.05",
"1.06",
"1.07",
"1.09",
"1.10",
"1.11",
"1.13",
"1.14",
"1.15",
"1.17",
"1.18",
"1.20",
"1.21",
"1.23",
"1.24",
"1.26",
"1.27",
"1.29",
"1.30",
"1.32",
"1.33",
"1.35",
"1.37",
"1.38",
"1.40",
"1.42",
"1.43",
"1.45",
"1.47",
"1.49",
"1.50",
"1.52",
"1.54",
"1.56",
"1.58",
"1.60",
"1.62",
"1.64",
"1.65",
"1.67",
"1.69",
"1.72",
"1.74",
"1.76",
"1.78",
"1.80",
"1.82",
"1.84",
"1.87",
"1.89",
"1.91",
"1.93",
"1.96",
"1.98",
"2.00",
"2.03",
"2.05",
"2.08",
"2.10",
"2.13",
"2.15",
"2.18",
"2.21",
"2.23",
"2.26",
"2.29",
"2.32",
"2.34",
"2.37",
"2.40",
"2.43",
"2.46",
"2.49",
"2.52",
"2.55",
"2.58",
"2.61",
"2.64",
"2.67",
"2.71",
"2.74",
"2.77",
"2.80",
"2.84",
"2.87",
"2.91",
"2.94",
"2.98",
"3.01",
"3.05",
"3.09",
"3.12",
"3.16",
"3.20",
"3.24",
"3.28",
"3.32",
"3.36",
"3.40",
"3.44",
"3.48",
"3.52",
"3.57",
"3.61",
"3.65",
"3.70",
"3.74",
"3.79",
"3.83",
"3.88",
"3.92",
"3.97",
"4.02",
"4.07",
"4.12",
"4.17",
"4.22",
"4.27",
"4.32",
"4.37",
"4.42",
"4.48",
"4.53",
"4.59",
"4.64",
"4.70",
"4.75",
"4.81",
"4.87",
"4.93",
"4.99",
"5.05",
"5.11",
"5.17",
"5.23",
"5.30",
"5.36",
"5.42",
"5.49",
"5.56",
"5.62",
"5.69",
"5.76",
"5.83",
"5.90",
"5.97",
"6.04",
"6.12",
"6.19",
"6.26",
"6.34",
"6.42",
"6.49",
"6.57",
"6.65",
"6.73",
"6.81",
"6.90",
"6.98",
"7.06",
"7.15",
"7.23",
"7.32",
"7.41",
"7.50",
"7.59",
"7.68",
"7.77",
"7.87",
"7.96",
"8.06",
"8.16",
"8.25",
"8.35",
"8.45",
"8.56",
"8.66",
"8.76",
"8.87",
"8.98",
"9.09",
"9.20",
"9.31",
"9.42",
"9.53",
"9.65",
"9.76",
"9.88"
],
"formula_exceptions": [
{
"n": 185,
"official": "9.20",
"formula": "9.19"
}
]
}
}
}
resistor_color.py · sha256 1e0ac77d8d9c9233800d6dc39d07128aa3f253b9675abdbcea54fb2b6dbaacc3
#!/usr/bin/env python3
"""Resistor colour code (IEC 60062) and preferred values (IEC 60063).
Operations:
decode bands -> resistance, tolerance, temperature coefficient, E-series membership
encode value -> the 4-band and 5-band colour codes (and 6-band with a temperature coefficient)
nearest value -> the nearest standard values of an E series (below, above, nearest by ratio)
Input (JSON on stdin):
operation "decode" | "encode" | "nearest" (default: decode when bands is given, else encode)
bands ["brown", "black", "red", "gold"] 3 to 6 colour names, head end first decode
value ohms: number, decimal string or RKM/SI text ("4k7", "R47", "2.2M", "10 kΩ") encode, nearest
tolerance percent for the tolerance band (default 5 for 4 bands, 1 for 5 and 6) encode
tcr ppm/K for a sixth band (100, 50, 15, 25, 20, 10, 5, 1, 250) encode
series "E3" ... "E192" (default E24) nearest
"""
import json
import math
import re
import sys
from decimal import Decimal, InvalidOperation, getcontext
getcontext().prec = 40
PREFIXES = [(Decimal("1e9"), "G"), (Decimal("1e6"), "M"), (Decimal("1e3"), "k"), (Decimal(1), ""), (Decimal("1e-3"), "m")]
ALIASES = {"gray": "grey", "purple": "violet", "none": "none", "": "none", "gold": "gold", "silver": "silver"}
def fail(message):
print(json.dumps({"error": message}))
sys.exit(2)
def load_table():
try:
with open("codes.json", encoding="utf-8") as handle:
return json.load(handle)
except (OSError, ValueError) as error:
fail(f"cannot read the bundled codes.json: {error}")
def plain(x):
text = format(x.normalize(), "f")
return text
def si(ohms):
if ohms == 0:
return "0 Ω"
for scale, prefix in PREFIXES:
if abs(ohms) >= scale:
return f"{plain(ohms / scale)} {prefix}Ω"
return f"{plain(ohms / Decimal('1e-3'))} mΩ"
def parse_value(value):
if isinstance(value, bool) or not isinstance(value, (int, float, str)):
fail("value must be a number or a string such as \"4k7\" or \"10 kΩ\"")
text = str(value).strip()
for suffix in ("ohms", "ohm", "Ω", "Ω"):
if text.endswith(suffix):
text = text[: -len(suffix)].strip()
break
multipliers = {"R": Decimal(1), "r": Decimal(1), "K": Decimal(1000), "k": Decimal(1000), "M": Decimal(10) ** 6,
"G": Decimal(10) ** 9, "T": Decimal(10) ** 12, "m": Decimal("0.001"), "L": Decimal("0.001")}
match = re.fullmatch(r"(\d*)([RrKkMGTmL])(\d*)", text)
if match and (match.group(1) or match.group(3)):
number = Decimal((match.group(1) or "0") + "." + (match.group(3) or "0"))
result = number * multipliers[match.group(2)]
else:
match = re.fullmatch(r"([0-9]*\.?[0-9]+(?:[eE][-+]?\d+)?)\s*([kKMGmT]?)", text)
if not match:
fail(f"value {value!r} is not a resistance (use 4700, \"4.7k\", \"4k7\", \"R47\", \"10 kΩ\")")
try:
result = Decimal(match.group(1)) * (multipliers[match.group(2)] if match.group(2) else 1)
except InvalidOperation:
fail(f"value {value!r} is not a number")
if result <= 0:
fail("value must be positive")
return result
def colour_name(name, field):
if not isinstance(name, str):
fail(f"{field} must be a colour name")
key = name.strip().lower()
return ALIASES.get(key, key)
def series_of(table, mantissa):
"""E series whose list contains this mantissa (1 <= m < 10)."""
found = []
for name, info in table["series"].items():
if any(Decimal(v) == mantissa for v in info["values"]):
found.append(name)
return found
def normalise(ohms):
exponent = ohms.adjusted()
return ohms.scaleb(-exponent), exponent
def decode(table, bands):
colours = table["colours"]
if not isinstance(bands, list) or not 3 <= len(bands) <= 6:
fail("bands must be a list of 3 to 6 colour names")
names = [colour_name(b, f"bands[{i}]") for i, b in enumerate(bands)]
for i, name in enumerate(names):
if name not in colours:
fail(f"bands[{i}]: unknown colour {bands[i]!r}; colours: {', '.join(colours)}")
n = len(names)
digits_count = 2 if n <= 4 else 3
digit_bands = names[:digits_count]
for i, name in enumerate(digit_bands):
if colours[name]["digit"] is None:
fail(f"band {i + 1} ({name}) is not a digit colour; a {n}-band code starts with {digits_count} digit bands")
mult_name = names[digits_count]
exp = colours[mult_name]["multiplier_exp"]
significand = int("".join(str(colours[c]["digit"]) for c in digit_bands))
ohms = Decimal(significand).scaleb(exp)
if n == 3:
tol = Decimal(table["no_band_tolerance_pct"])
tol_name = "none (3-band code)"
else:
tol_name = names[digits_count + 1]
if colours[tol_name]["tolerance_pct"] is None:
fail(f"band {digits_count + 2} ({tol_name}) is not a tolerance colour")
tol = Decimal(colours[tol_name]["tolerance_pct"])
result = {"operation": "decode", "bands": names, "ohms": plain(ohms), "value": si(ohms),
"tolerance_pct": plain(tol), "tolerance_band": tol_name,
"min_ohms": plain(ohms * (100 - tol) / 100), "max_ohms": plain(ohms * (100 + tol) / 100)}
if n == 6:
tcr_name = names[5]
tcr = colours[tcr_name]["tcr_ppm_per_k"]
if tcr is None:
fail(f"band 6 ({tcr_name}) is not a temperature-coefficient colour")
result["tcr_ppm_per_k"] = tcr
warnings = []
if colours[names[0]]["digit"] == 0:
warnings.append("the first band is black (a leading zero): check that the resistor is not read backwards")
if names[-1] in ("gold", "silver") and n >= 4 and names[0] in ("gold", "silver"):
warnings.append("gold or silver at both ends: the direction cannot be told")
if ohms > 0:
mantissa, _ = normalise(ohms)
result["e_series"] = series_of(table, mantissa)
if warnings:
result["warnings"] = warnings
result["standard"] = table["source"]
return result
def digits_for(ohms, count):
"""(digits, exponent) with ohms = digits x 10^exponent and `count` digits, or None."""
mantissa, exponent = normalise(ohms)
scaled = mantissa.scaleb(count - 1)
if scaled != scaled.to_integral_value():
return None
digits = int(scaled)
exp = exponent - (count - 1)
return digits, exp
def encode(table, request):
colours = table["colours"]
ohms = parse_value(request.get("value"))
by_digit = {info["digit"]: name for name, info in colours.items() if info["digit"] is not None}
by_exp = {info["multiplier_exp"]: name for name, info in colours.items()}
by_tol = {Decimal(info["tolerance_pct"]): name for name, info in colours.items() if info["tolerance_pct"] is not None}
by_tcr = {info["tcr_ppm_per_k"]: name for name, info in colours.items() if info["tcr_ppm_per_k"] is not None}
def tol_colour(default):
raw = request.get("tolerance", default)
try:
tol = Decimal(str(raw).rstrip("%").strip())
except InvalidOperation:
fail("tolerance must be a percentage such as 5 or \"1%\"")
if tol not in by_tol:
fail(f"tolerance {raw} has no colour; IEC 60062 tolerances: {', '.join(plain(t) for t in sorted(by_tol))} %")
return by_tol[tol], tol
result = {"operation": "encode", "ohms": plain(ohms), "value": si(ohms)}
for count, key, default in ((2, "bands_4", 5), (3, "bands_5", 1)):
found = digits_for(ohms, count)
if found is None:
result[key] = None
result[key + "_note"] = f"needs more than {count} significant digits"
continue
digits, exp = found
if exp not in by_exp:
result[key] = None
result[key + "_note"] = f"multiplier 10^{exp} has no colour (pink 10^-3 to white 10^9)"
continue
tol_name, tol = tol_colour(default)
bands = [by_digit[int(d)] for d in str(digits).zfill(count)] + [by_exp[exp], tol_name]
result[key] = bands
result[key + "_tolerance_pct"] = plain(tol)
if "tcr" in request:
tcr = request["tcr"]
if isinstance(tcr, bool) or not isinstance(tcr, int) or tcr not in by_tcr:
fail(f"tcr must be one of {sorted(by_tcr)} ppm/K")
result["bands_6"] = (result["bands_5"] + [by_tcr[tcr]]) if result.get("bands_5") else None
mantissa, _ = normalise(ohms)
result["e_series"] = series_of(table, mantissa)
result["standard"] = table["source"]
return result
def nearest(table, request):
ohms = parse_value(request.get("value"))
name = request.get("series", "E24")
if not isinstance(name, str) or name.upper() not in table["series"]:
fail(f"series must be one of {', '.join(table['series'])}")
name = name.upper()
values = [Decimal(v) for v in table["series"][name]["values"]]
mantissa, exponent = normalise(ohms)
candidates = [values[-1].scaleb(exponent - 1)] + [v.scaleb(exponent) for v in values] + [values[0].scaleb(exponent + 1)]
below = max(c for c in candidates if c <= ohms)
above = min(c for c in candidates if c >= ohms)
ratio_pick = min((below, above), key=lambda c: abs(math.log(float(c) / float(ohms))))
error = (ratio_pick - ohms) / ohms * 100
result = {"operation": "nearest", "ohms": plain(ohms), "value": si(ohms), "series": name,
"series_tolerance_pct": table["series"][name]["tolerance_pct"],
"nearest": plain(ratio_pick), "nearest_value": si(ratio_pick),
"error_pct": format(error.quantize(Decimal("0.001")), "f"),
"below": plain(below), "above": plain(above), "exact_match": below == ohms,
"rule": "nearest on a logarithmic scale (smaller ratio), as the E series are geometric",
"standard": table["source"]}
return result
def main():
try:
request = json.load(sys.stdin)
except ValueError as error:
fail(f"stdin is not JSON: {error}")
if not isinstance(request, dict):
fail("input must be a JSON object")
extra = set(request) - {"operation", "bands", "value", "tolerance", "tcr", "series"}
if extra:
fail(f"unknown fields: {sorted(extra)}")
table = load_table()
operation = request.get("operation") or ("decode" if "bands" in request else "encode")
if operation == "decode":
result = decode(table, request.get("bands"))
elif operation == "encode":
if "value" not in request:
fail("encode needs value")
result = encode(table, request)
elif operation == "nearest":
if "value" not in request:
fail("nearest needs value")
result = nearest(table, request)
else:
fail("operation must be decode, encode or nearest")
print(json.dumps(result, ensure_ascii=False))
if __name__ == "__main__":
main()
Where it is served
The registry serves only AXLR clients, so a browser gets 403; fetch with a User-Agent that starts with axlr:
curl -A axlr-docs/1.0 -O https://registry.underpassai.com/tools/resistor_color/1.0.0/manifest.json
curl -A axlr-docs/1.0 -O https://registry.underpassai.com/tools/resistor_color/1.0.0/codes.json
curl -A axlr-docs/1.0 -O https://registry.underpassai.com/tools/resistor_color/1.0.0/resistor_color.py