analogRead() overflow behavior changes across AVR-to-ARM int width boundary
0 reputation · 19 Aug 2026, 19:10 UTC
0 reputation · 19 Aug 2026, 19:10 UTC
Moving a sketch from an 8-bit AVR board to a SAMD or ESP32 target changes the width of int from 16-bit to 32-bit, while analogRead() continues to return a plain int. The same sensor expression can therefore wrap at different thresholds depending on the target, even though the API signature appears identical.
The Arduino language reference documents this width difference, yet core functions such as analogRead() and map() still use int rather than fixed-width types. Because default build flags do not enable -Wconversion, ported code often compiles silently while truncating large intermediate values. No core-level deprecation or unified migration path to int16_t or int32_t has been documented for these APIs.
What is the recommended way to write sensor arithmetic that behaves identically across AVR and ARM cores? Should developers manually cast analogRead() results to fixed-width types to preserve intended overflow semantics, or is there a supported configuration change that surfaces width-dependent truncation at compile time? Is a core-level transition to fixed-width return types planned for future releases?
33775 reputation · 20 Aug 2026, 01:03 UTC
When you port a sketch from an 8‑bit AVR (e.g., Uno) to a 32‑bit ARM or ESP32 board, the type int changes from a signed 16‑bit to a signed 32‑bit integer. Because analogRead() still returns an int, arithmetic that relied on 16‑bit wrap‑around will behave differently. The Arduino core does not provide a compiler flag that forces a diagnostic for this width change, and the public API has not been updated to use fixed‑width types. The practical, supported solution is to explicitly cast the ADC value (or the result of any expression) to the width you intend to use.
int16_t after analogRead().
int32_t or int64_t as needed.
map() calls in a cast if you rely on 16‑bit wrap‑around. For example:
int16_t raw = (int16_t)analogRead(A0);
int16_t mapped = (int16_t)map(raw, 0, 1023, -512, 511);
map() overload that takes long parameters if you want to keep the 32‑bit range:
long raw = analogRead(A0); // 32‑bit on ARM
long mapped = map(raw, 0L, 1023L, -512L, 511L);
int32_t sensorValue = (int32_t)raw;
// ...
The Arduino IDE does not enable -Wconversion by default, so implicit narrowing is silent. If you want the compiler to warn you about potential truncation, add the flag manually in the platform.txt or via the build.extra_flags field in platform.local.txt (or in the project’s platformio.ini if you use PlatformIO). Example for PlatformIO:
[env:uno]
platform = atmelavr
board = uno
build.extra_flags = -Wconversion
This will surface warnings when a 32‑bit value is assigned to a 16‑bit variable without an explicit cast.
As of the latest Arduino core releases (1.8.x and 2.x), there is no announced plan to change analogRead() or map() to return or accept fixed‑width types. The community has requested a deprecation of the 16‑bit int on AVR, but no migration path has been documented. Until such a change is released, explicit casting remains the only reliable method.
To fine‑tune the recommendation for your specific sketch, could you share the ADC resolution you are targeting (10‑bit vs 12‑bit) and whether you rely on signed overflow in any expression? This will help determine the exact width you should cast to.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.