# How to select standard resistor values for a voltage divider?

I wanted to select resistor values for a voltage divider that sets the output voltage of a buck converter. I picked the values recommended in the datasheet, because 3.3V is a standard value there were recommended resistor values. But what if there weren't?

How would I go about optimally determining resistor sizes, that are available in standard packages (100k, 470k etc.), I don't mean ideal values that you could calculate.

Is there a software or a method for me to determine the resistor values optimally, as close to the ideal case, but with readily available standard resistor values?

Edit after Winnys comment:

The particular buck converter that made me ask this was BD9E302EFJ. Its voltage output is determined as given in datasheet. As I said there is already a recommended selection in the datasheet as well.

I usually go about selecting the smaller resistor as 100k then calculate the other. But most often this results in an error that is not to my liking due to rounding. For instance a .2V deviation in the output voltage etc.

• Depends on your impedance or leakage current needs. Once you know that and how much accuracy you need, the top resistor maximum residence is set. From there you select the best ratio you can in the highest E-value you are allowed. Do you have a particular example? Apr 28, 2021 at 11:02
• @winny yes I do Apr 28, 2021 at 11:08
• Do what @winny recommended, using Excel Apr 28, 2021 at 11:16
• Use the E96 table, CHoose your smaller value, compute the larger and pick nearest value using 1% values , compute tolerance stackup error, redo if not met, or use a spredsheet Apr 28, 2021 at 11:17
• The tolerance stackup for X% pairs ranges from X% to ~2X% depending on how far the from the ratio =1 is. Apr 28, 2021 at 12:25

I while back I wrote a script that accomplishes the same thing as the Douxchamps site above, but not limited to dividers - it does an exhaustive search on any system you feed it.

I haven't published it on GitHub have by request published it on GitHub.

For posterity, here it is in its entirety, with your 3.0/0.8 voltages in an example at the bottom:

"""
Do a quick, sequential, numerical (not symbolic) exploration of some electronic
component values to propose solutions that use standard, inexpensive parts.
"""

from bisect import bisect_left
from itertools import islice
from math import log10, floor
from typing import (
Callable,
Iterable,
List,
Optional,
Sequence,
Set,
Tuple,
)

# See https://en.wikipedia.org/wiki/E_series_of_preferred_numbers
E3 = (1.0, 2.2, 4.7)

E6 = (1.0, 1.5, 2.2, 3.3, 4.7, 6.8)

E12 = (1.0, 1.2, 1.5, 1.8, 2.2, 2.7, 3.3, 3.9, 4.7, 5.6, 6.8, 8.2)

E24 = (
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,
)

E48 = (
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,
)

E96 = (
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,
)

def bisect_lower(a: Sequence[float], x: float) -> int:
"""
Run bisect, but use one index before the return value of bisect_left
:param a: The sorted haystack
:param x: The needle
:return: The index of the array element that equals or is lesser than x
"""
i = bisect_left(a, x)
if (
(i < len(a) and a[i] > x) or
(i >= len(a) and a[i % len(a)]*10 > x)
):
i -= 1
return i

def approximate(x: float, series: Sequence[float]) -> (int, float):
"""
Approximate a value by using the given series.
:param x: Any positive value
:param series: Any of E3 through E96
:return: An integer index into the series for the element lesser than or
equal to the value's mantissa, and the value's decade - a power of
ten
"""
if x == float('inf'):
return None, float('inf')

index = bisect_lower(series, mantissa)
if index >= len(series):

def fmt_eng(x: float, unit: str, sig: int = 2) -> str:
"""
Format a number in engineering (SI) notation
:param x: Any number
:param unit: The quantity unit (Hz, A, etc.)
:param sig: Number of significant digits to show
:return: The formatted string
"""
if x == 0:
p = 0
elif x == float('inf'):
return '∞'
else:
p = floor(log10(abs(x)))
e = int(floor(p / 3))
digs = max(0, sig - p%3 - 1)
mantissa = x / 10**(3*e)

if e == 0:
prefix = ''
elif 0 < e < 9:
# See https://en.wikipedia.org/wiki/Metric_prefix
prefix = ' kMGTPEZY'[e]
elif 0 > e > -8:
prefix = 'mμnpfazy'[-e-1]
else:
raise IndexError(f'Number out of SI range: {x:.1e}')

fmt = '{:.%df} {:}{:}' % digs
return fmt.format(mantissa, prefix, unit)

# a callable with any number of floating-point
# arguments, returning a float
CalculateCall = Callable #[
#    [float, ...],
#    float
#]

class ComponentValue:
"""
A value associated with a component - to track approximated values
"""

def __init__(
self,
component: 'Component',
index: Optional[int] = None,
exact: Optional[float] = None,
):
"""
Valid combinations:
exact - approximated value will be calculated
exact=approximate
:param index: The integer index into the series for the quantity's
mantissa
:param exact: The exact quantity, if known
"""

self.component = component

if index is None:
assert exact is not None
self.exact = exact
else:

self.approx = float('inf')
else:

if index is not None:
if exact is None:
self.exact = self.approx
else:
self.exact = exact

@property
def error(self) -> float:
return self.approx / self.exact - 1

def get_other(self) -> Optional['ComponentValue']:
"""
:return: If this approximated value is below its exact value, then the
next-highest E24 value; otherwise None
"""
if self.approx >= self.exact:
return None

if index >= len(self.component.series):
index = 0
return ComponentValue(component=self.component, exact=self.exact,

def get_best(self) -> 'ComponentValue':
other = self.get_other()
if other is None:
return self

if self.error**2 < other.error**2:
return self
return other

def __str__(self):
return fmt_eng(self.approx, self.component.unit, self.component.digits)

def fmt_exact(self) -> str:
return fmt_eng(self.exact, self.component.unit, 4)

class Component:
"""
A component, without knowledge of its value - only bounds and defining
formula
"""

def __init__(
self,
prefix: str,
suffix: str,
unit: str,
series: Sequence[float] = E24,
calculate: Optional[CalculateCall] = None,
minimum: float = 0,
maximum: Optional[float] = None,
use_for_err: bool = True,
):
"""
:param prefix: i.e. R, C or L
:param suffix: Typically a number, i.e. the "2" in R2
:param unit: i.e. Hz, A, F, ...
:param series: One of E3 through E96
:param calculate: A callable that will be given all values of previous
components in the calculation sequence. These values
are floats, and the return must be a float.
If this callable is None, the component will be
interpreted as a degree of freedom.
:param minimum: Min allowable value; the return of calculate will be
checked against this and failures will be silently
dropped.
Must be at least zero, or greater than zero if
calculate is not None.
:param maximum: Max allowable value; the return of calculate will be
checked against this and failures will be silently
dropped.
:param use_for_err: If True, error from this component's ideal to
approximated value will influence the solution rank.
"""
(
self.prefix, self.suffix, self.unit, self.series,
self.calculate, self.min, self.max, self.use_for_err,
) = (
prefix, suffix, unit, series, calculate, minimum, maximum,
use_for_err,
)

assert minimum >= 0
assert maximum is None or maximum >= minimum

if calculate:
self.values = self._calculate_values
elif minimum > 0:
self.values = self._iter_values

self.digits: int = 3 if len(series) > 24 else 2

self.fmt_field: Callable[[str], str] = (
('{:>%d}' % (4 + self.digits)).format
)

def __str__(self):
return self.name

@property
def name(self) -> str:
return f'{self.prefix}{self.suffix}'

def _calculate_values(
self, prev: Sequence[ComponentValue]
) -> Iterable[ComponentValue]:

def values():
# Get the value based on exact values first
from_exact_val = self.calculate(*(p.exact for p in prev))
if from_exact_val <= 0:
return

from_exact = ComponentValue(self, exact=from_exact_val)
yield from_exact
other = from_exact.get_other()
if other:
yield other

# See if there's a difference when calculating against approximated
# values
from_approx_val = self.calculate(*(p.approx for p in prev))
if from_approx_val > 0:
from_approx = ComponentValue(self, exact=from_approx_val)
if from_approx.exact != from_exact.exact:
yield from_approx
other = from_approx.get_other()
if other:
yield other

for v in values():
if (
self.min <= v.exact and
(self.max is None or self.max >= v.exact)
):
yield v

def _all_values(self) -> Iterable[Tuple[int, float]]:
for index in range(self.start_index, len(self.series)):
while True:
for index in range(len(self.series)):

def _iter_values(
self, prev: Sequence[ComponentValue]
) -> Iterable[ComponentValue]:
if value.approx > self.max:
return
yield value

class Resistor(Component):
def __init__(
self,
suffix: str,
series: Sequence[float] = E24,
calculate: Optional[CalculateCall] = None,
minimum: float = 0,
maximum: Optional[float] = None,
use_for_err: bool = True,
):
super().__init__('R', suffix, 'Ω', series, calculate, minimum, maximum,
use_for_err)

class Capacitor(Component):
def __init__(
self,
suffix: str,
series: Sequence[float] = E24,
calculate: Optional[CalculateCall] = None,
minimum: float = 0,
maximum: Optional[float] = None,
use_for_err: bool = True,
):
super().__init__('C', suffix, 'F', series, calculate, minimum, maximum,
use_for_err)

class Output:
"""
A calculated parameter - potentially but not necessarily a circuit output -
to be calculated and checked for error in the solution ranking process.
"""

def __init__(
self, name: str, unit: str, expected: float,
calculate: CalculateCall):
"""
:param name: i.e. Vout
:param unit: i.e. V, A, Hz...
:param expected: The value that this parameter would assume under ideal
circumstances
:param calculate: A callable accepting a sequence of floats - one per
component, in the same order as they were passed to
the Solver constructor; returning a float.
"""
self.name, self.unit, self.expected, self.calculate = (
name, unit, expected, calculate,
)

def error(self, value: float) -> float:
"""
:return: Absolute error, since the expected value might be 0
"""
return value - self.expected

def __str__(self):
return self.name

class Solver:
"""
Basic recursive solver class that does a brute-force search through some
component values.
"""

def __init__(
self,
components: Sequence[Component],
outputs: Sequence[Output],
threshold: Optional[float] = 1e-3,
):
"""
:param components: A sequence of Component instances. The order of this
sequence determines the order of parameters passed to
Output.calculate and Component.calculate.
:param outputs: A sequence of Output instances - can be empty.
:param threshold: Maximum error above which solutions will be discarded
"""
self.components, self.outputs = components, outputs
self.candidates: List[Tuple[
float,                     # error
Sequence[float],           # output values
Sequence[ComponentValue],  # component values to get the above
]] = []
self.approx_seen: Set[Tuple[float, ...]] = set()
self.threshold = threshold

def _recurse(self, values: List[Optional[ComponentValue]], index: int = 0):
if index >= len(self.components):
self._evaluate(values)
else:
comp = self.components[index]
for v in comp.values(values[:index]):
values[index] = v
self._recurse(values, index+1)

def solve(self):
"""
Recurse through all of the components, doing a brute-force search.
Results are stored in self.candidates and sorted in order of increasing
error.
"""
values = [None]*len(self.components)
self._recurse(values)
self.candidates.sort(key=lambda v: v[0])

def _evaluate(self, values: Sequence[ComponentValue]):
approx = tuple(v.approx for v in values)
if approx in self.approx_seen:
return

outputs = tuple(
o.calculate(*approx)
for o in self.outputs
)
err = sum(
o.error(v)**2
for o, v in zip(self.outputs, outputs)
) + sum(
v.error**2
for c, v in zip(self.components, values)
if c.use_for_err
)
if self.threshold is None or err < self.threshold:
self.candidates.append((err, outputs, tuple(values)))

def print(self, top: int = 10):
"""
Print a table of all component values, output values and output error.
:param top: Row limit.
"""

print(' '.join(
comp.fmt_field(comp.name)
for comp in self.components
), end=' ')
print(' '.join(
f'{output.name:>10} {"Err":>8}'
for output in self.outputs
))

for err, outputs, values in islice(self.candidates, top):
print(' '.join(
value.component.fmt_field(str(value))
for value in values
), end=' ')
print(' '.join(
f'{fmt_eng(value, output.unit, 4):>10} '
f'{output.error(value):>8.1e}'
for value, output in zip(outputs, self.outputs)
))

def buck():
# https://fscdn.rohm.com/en/products/databook/datasheet/ic/power/switching_regulator/bd9e302efj-e.pdf
# page 30

Vout = 3.0
Vref = 0.8
R12max = 700e3
# 700e3 / R2 * Vref = Vout at limit
R2max = Vref / Vout * R12max

svout = Solver(
(
Resistor('2', E96, None, R2max/10, R2max, False),
Resistor(
'1', E96,
lambda R2: R2*(Vout/Vref - 1),
0, None, False,
),
),
(
Output('Vout', 'V', Vout, lambda R2, R1: Vref*(1 + R1/R2)),
),
threshold=1e-3,
)
svout.solve()
svout.print()

buck()


with output

     R2      R1       Vout      Err
115 kΩ  316 kΩ    2.998 V -1.7e-03
107 kΩ  294 kΩ    2.998 V -1.9e-03
150 kΩ  412 kΩ    2.997 V -2.7e-03
130 kΩ  357 kΩ    2.997 V -3.1e-03
68.1 kΩ  187 kΩ    2.997 V -3.2e-03
59.0 kΩ  162 kΩ    2.997 V -3.4e-03
118 kΩ  324 kΩ    2.997 V -3.4e-03
84.5 kΩ  232 kΩ    2.996 V -3.6e-03
169 kΩ  464 kΩ    2.996 V -3.6e-03
49.9 kΩ  137 kΩ    2.996 V -3.6e-03

• So cool! Why don't you document its usage and put it somewhere for everyone to use? Apr 28, 2021 at 18:36
• @Sohail Thanks :) I should get around to doing that, yeah. Apr 28, 2021 at 19:43
• This is amazing! I was thinking of doing sth like this would be very cool. Beat me to it :) Apr 28, 2021 at 20:25
• Wow... this is the first time I see the use of value tables for the job. You can get rid of about 70% of the code by using simple formulas. See for example Excel formulas for E24, E48 and E96 in the comment by ivica at the end of this post. Or simply use Excell to do the same without coding. Apr 29, 2021 at 7:55
• @Maple Re. the formulas - they are value tables "in real life". There are entries that do not follow the logarithm, and so attempting to be clever and calculate as many as possible offers no benefit. As for Excel... For the level of complexity of systems I solve with this tool for real application, it would be a poor fit, and I'd only fall back to it if I wasn't familiar-enough with Python. Apr 29, 2021 at 13:27

Yes, there are online calculators that will allow you to find resistor pairs that will closely approximate the desired ratio. For example, this one.

If you wanted to have (say) 3.0V out and your reference is 0.8V and we're ignoring bias current, and we're using E96 series (1%) values we get the following results:

Obviously you can scale the values by decades up or down, for example you could use 1.18K and 3.24K.

This particular calculator (and some others) works well in this case because we're not overly concerned with the total resistance of the divider.

Note that a 0.108% (for example) error in the nominal values is dwarfed by the 1.47% worst-case error due to resistor tolerances.

You can also do it manually, takes a bit longer, but just try values from the E96 table and find the closest pair.

And if you really want to hit the nominal values virtually bang-on you can add a resistor series or parallel to one of the pair. That may not be as bad it seems because you can probably pick two of the three resistors as values that you are already stocking, have a reel of, etc. and only one "weird" value is required.

As with anything else online, best pull your your calculator or fire up a spreadsheet and double-check the numbers for yourself.

• great answer, thank you :) Apr 28, 2021 at 11:20
• I said last comment backwards , it shud be, the closer Rl/Rh is to 1, the stackup error = the sum of individual tolerance or 2x. The further away , it approaches the 1X tolerance of the smaller value , thus 1% R’s result in 1 to 2% tolerance stackup error. Rather than rethink the design or use a trimmer as the web author indicates, for the 5% example, the better solution for very low or inversely high ratios is use tighter tolerance resistor for the lower R value only. e.g. 1000:1 0.25% max , using 0.2% for R1 ($) and 1% for 1000R1 will achieve this. Or laser trimmed R network ratios ($) Apr 28, 2021 at 13:08