# 5V to 3.3V with Preferred Resistors

What do you do when you want to scale a 5V analog signal to 3.3V? You build a resis­tor divider, possi­bly with an op amp-based volt­age follower to drive low imped­ance loads.

But what resis­tors do you buy for your resis­tor bridge? You want resis­tance values of some multi­ple of 1.7 to 3.3, but resis­tors come in preferred values, which are math­emag­i­cat­i­cally spaced out resis­tance scales.

None of the “nice” E24 values that are multi­ples of 170 or 330 really work (330 exists, but not 170; there’s a 510 but no 990; and 680 but no 1320).

There’s gotta be some kind of ideal combi­na­tion of preferred-value resis­tors that give me either the perfect 5V to 3.3V divider, or at least mini­mizes the error. Well, I wasn’t about to do all 24² combi­na­tions of upper/lower resis­tors by hand and for some reason Google couldn’t find it for me, so I wrote a script C++0x program to uh… do it for me1.

So what are the results for each EIA resis­tor value series?

E12 best: r1 = 180, r2 = 330 (-5.556%)
E24 best: r1 = 470, r2 = 910 (-0.258%)
E48 best: r1 = 825, r2 = 1600 (-0.092%)
E96 best: r1 = 491, r2 = 953 (-0.012%)
E192 best: r1 = 102, r2 = 198 (+0.000%)

Those error percent­ages are for volt­age drop over the top half of the resis­tor divider, not for the result­ing volt­ages. For exam­ple, the 180 & 330 combo would have an error of . Not bad for parts you can get at RadioShack.

It turns E192 does have a perfect combo, but it’s not like E192-series resis­tors are avail­able from Digi-Key. However, you can get E96 resis­tors to build a divider with only -0.0042% error, which will have so much error due to the bimodal distri­b­u­tion of binned resis­tor values that the time you’ve spent on making it “correct” would be completely wasted.

Damn.

## Appendix: ‘Dat code

/*
* 2011 Xo Wang
* I hereby release this into the public domain, something about merchantability, blah blah. If you use it, it'd be nice to let me know.
*
* You'd be an idiot to use this; it does many things very wrong for the sake of expedient solution-getting.
*/

#include <algorithm> // <3 lower_bound, <3 set_union
#include <iostream>
#include <limits>
#include <string>
#include <vector>

#include <cstdlib>

static double error(const double r1, const double r2) {
return (r2 * 1.7) / (r1 * 3.3) - 1.0;
}

static void process_series(const std::string &name, const std::vector<int> series) {
using namespace std;

int min_error_r1 = 0;
int min_error_r2 = 0;
double min_error = numeric_limits<double>::max();
for_each(series.begin(), series.end(), [&](const int r1) {
// find nearest resistance
const double r2_wanted = r1 * (3.3 / 1.7);
// r_wanted is too large, so scale it down by ten when searching
const bool r2_wanted_10x = r2_wanted > series.back();
const auto r2_higher_iter = lower_bound(series.begin(), series.end(), r2_wanted_10x ? r2_wanted * 0.1 : r2_wanted);

// nearest two resistance values
const int r2_higher = r2_wanted_10x ? *r2_higher_iter * 10 : *r2_higher_iter;
const int r2_lower = [=]() -> int {
if (r2_higher == series[0] * 10) {
return series.back();
}
return r2_wanted_10x ? *(r2_higher_iter - 1) * 10 : *(r2_higher_iter - 1);
}();

if (fabs(min_error) > fabs(error(r1, r2_lower))) {
min_error = error(r1, r2_lower);
min_error_r1 = r1;
min_error_r2 = r2_lower;
}

if (fabs(min_error) > fabs(error(r1, r2_higher))) {
min_error = error(r1, r2_higher);
min_error_r1 = r1;
min_error_r2 = r2_higher;
}

//cout << "  r1 = " << r1 << ", r2 = { " <<
//		r2_lower << " (" << error(r1, r2_lower) * 100 << "%), " <<
//		r2_higher << " (+" << error(r1, r2_higher) * 100 << "%) }\n";
});

cout << name << " best: r1 = " << min_error_r1 << ", r2 = " << min_error_r2 <<
" (" << (min_error > 0 ? "+" : "") << min_error * 100 << "%)\n";
}

static std::vector<int> merge_series(const std::vector<int> &s1, const std::vector<int> &s2) {
using namespace std;
vector<int> out_series(s1.size() + s2.size());
auto last = set_union(s1.begin(), s1.end(), s2.begin(), s2.end(), out_series.begin());
out_series.resize(distance(out_series.begin(), last));
return out_series;
}

int main() {
using namespace std;

const vector<int> E12 = { 100, 120, 150, 180, 220, 270, 330, 390, 470, 560, 680, 820 };
const vector<int> E24 = { 100, 110, 120, 130, 150, 160, 180, 200, 220, 240, 270, 300, 330, 360, 390, 430, 470,
510, 560, 620, 680, 750, 820, 910 };
const vector<int> E48 = { 100, 105, 110, 115, 121, 127, 133, 140, 147, 154, 162, 169, 178, 187, 196, 205, 215,
226, 237, 249, 261, 274, 287, 301, 316, 332, 348, 365, 383, 402, 422, 442, 464, 487, 511, 536, 562, 590,
619, 649, 681, 715, 750, 787, 825, 866, 909, 953 };
const vector<int> E96 = { 100, 102, 105, 107, 110, 113, 115, 118, 121, 124, 127, 130, 133, 137, 140, 143, 147,
150, 154, 158, 162, 165, 169, 174, 178, 182, 187, 191, 196, 200, 205, 210, 215, 221, 226, 232, 237, 243,
249, 255, 261, 267, 274, 280, 287, 294, 301, 309, 316, 324, 332, 340, 348, 357, 365, 374, 383, 392, 402,
412, 422, 432, 442, 453, 464, 475, 487, 491, 511, 523, 536, 549, 562, 576, 590, 604, 619, 634, 649, 665,
681, 698, 715, 732, 750, 768, 787, 806, 825, 845, 866, 887, 909, 931, 959, 976 };
const vector<int> E192 = { 100, 101, 102, 104, 105, 106, 107, 109, 110, 111, 113, 114, 115, 117, 118, 120, 121,
123, 124, 126, 127, 129, 130, 132, 133, 135, 137, 138, 140, 142, 143, 145, 147, 149, 150, 152, 154, 156,
158, 160, 162, 164, 165, 167, 169, 172, 174, 176, 178, 180, 182, 184, 187, 189, 191, 193, 196, 198, 200,
203, 205, 208, 210, 213, 215, 218, 221, 223, 226, 229, 232, 234, 237, 240, 243, 246, 249, 252, 255, 258,
261, 264, 267, 271, 274, 277, 280, 284, 287, 291, 294, 298, 301, 305, 309, 312, 316, 320, 324, 328, 332,
336, 340, 344, 348, 352, 357, 361, 365, 370, 374, 379, 383, 388, 392, 397, 402, 407, 412, 417, 422, 427,
432, 437, 442, 448, 453, 459, 464, 470, 475, 481, 487, 493, 499, 505, 511, 517, 523, 530, 536, 542, 549,
556, 562, 569, 576, 583, 590, 597, 604, 612, 619, 626, 634, 642, 649, 657, 665, 673, 681, 690, 698, 706,
715, 723, 732, 741, 750, 759, 768, 777, 787, 796, 806, 816, 825, 835, 845, 856, 866, 876, 887, 898, 909,
920, 931, 942, 953, 965, 976, 988 };

cout.setf(ios::fixed, ios::floatfield);
cout.precision(3);
process_series("E12", E12);
vector<int> series = merge_series(E12, E24);
process_series("E24", series);
series = merge_series(series, E48);
process_series("E48", series);
series = merge_series(series, E96);
process_series("E96", series);
series = merge_series(series, E192);
process_series("E192", series);

return EXIT_SUCCESS;
}

1. Actu­ally, the code runs in O(N * log N) time rather than O(N²) time: given some high-side resis­tance, I run a binary search on which low-side resis­tor value gives the best divider. I loop over all high-side resis­tances. []