To convert the fraction decimal 0.625 to binary, the exact answer is 0.101. The formula used is repeated multiplication by 2, extracting the integer bit at each step: (0.625 × 2 = 1.25), (0.25 × 2 = 0.5), (0.5 × 2 = 1.0). Reading the integer parts top-to-bottom yields 0.101. However, in embedded systems like an Arduino Uno (8-bit) or ESP32 (32-bit), the 'correct' answer shifts entirely based on your assumed bit-width and whether you are using fixed-point or IEEE 754 floating-point formats.

Before writing a single line of C++ or MicroPython, you must define your precision architecture. Below is a data-dense reference showing how common decimal fractions map across different embedded data types.

Decimal Fraction to Binary Representation by Architecture
Decimal Fraction Exact Binary 8-Bit Fixed (Q0.8) 16-Bit Fixed (Q0.16) 32-bit IEEE 754 Hex
0.1 0.000110011... 0x1A (0.1015) 0x199A (0.100006) 0x3DCCCCCD
0.25 0.01 0x40 (0.250) 0x4000 (0.250) 0x3E800000
0.375 0.011 0x60 (0.375) 0x6000 (0.375) 0x3EC00000
0.5 0.1 0x80 (0.500) 0x8000 (0.500) 0x3F000000
0.625 0.101 0xA0 (0.625) 0xA000 (0.625) 0x3F200000
0.875 0.111 0xE0 (0.875) 0xE000 (0.875) 0x3F600000

The Multiplication Formula and Neighboring Value Shifts

The standard algorithm to convert fraction decimal to binary relies on isolating the integer portion after multiplying by the base (2). If the integer is 1, that binary bit is 1; if 0, the bit is 0. You then carry the remaining fractional part forward. This works flawlessly for dyadic fractions (numbers that can be expressed as a sum of inverse powers of 2, like 0.625 = 1/2 + 1/8).

But what happens when we look at the neighboring values within a ±20% range of our target 0.625 (spanning 0.500 to 0.750)? The exactness of the conversion rapidly degrades.

Neighboring Values (±20% of 0.625)
Decimal Binary Expansion Termination Status 8-Bit Truncation Error
0.500 0.1 Exact (Terminates) 0.0%
0.550 0.1000110011... Infinite Repeating +0.36%
0.600 0.1001100110... Infinite Repeating -0.23%
0.625 0.101 Exact (Terminates) 0.0%
0.650 0.1010011001... Infinite Repeating -0.33%
0.700 0.1011001100... Infinite Repeating -0.11%
0.750 0.11 Exact (Terminates) 0.0%

Notice that only the dyadic fractions (0.5, 0.625, 0.75) terminate cleanly. The rest result in infinite repeating binaries, forcing your microcontroller to truncate the value and introduce a rounding error. According to the IEEE 754 standard for floating-point arithmetic, managing these truncation errors is the primary reason 32-bit floats dedicate 23 bits solely to the mantissa.

Bit-Width Assumptions: How the Answer Shifts Across Architectures

In AC power theory, the 'correct' current draw is fixed by assuming a specific voltage and phase angle. In binary fraction conversion, the assumption that fixes the answer is your bit-width and data type. A single-voltage answer presented as universal is a trap; similarly, presenting '0.101' as the universal answer for 0.625 ignores how the hardware actually stores it.

How the Answer Shifts: 8-bit vs 16-bit vs 32-bit

  • 8-bit AVR (Arduino Uno / ATmega328P): Native hardware math is integer-only. To represent 0.625, you must use an 8-bit fixed-point format (like Q0.8). You multiply 0.625 by 256, yielding 160 (Hex 0xA0). The binary answer is 10100000. If you attempt to use the float keyword, the AVR-GCC compiler invokes software emulation, consuming hundreds of clock cycles and 32 bits of SRAM to store 0x3F200000.
  • 16-bit DSP / Fixed-Point ARM: Using a Q0.16 format, you multiply 0.625 by 65,536. The result is 40960 (Hex 0xA000). The binary answer shifts to a 16-bit sequence: 1010000000000000. This provides much higher precision for repeating fractions like 0.1 (which becomes 0x199A).
  • 32-bit ESP32 / ARM Cortex-M: These chips feature hardware Floating Point Units (FPUs). Here, 0.625 is stored natively in IEEE 754 single-precision format. The binary answer shifts to a 32-bit sequence comprising a sign bit (0), an 8-bit exponent (01111110), and a 23-bit mantissa (01000000000000000000000).

When the Conversion is Meaningless

Converting a decimal fraction to binary becomes mathematically meaningless in two specific embedded scenarios:

  1. Integer Registers without Scaling: If you try to store 0.625 directly into a standard uint8_t or int variable without applying a Q-format scaling factor, the C++ compiler will truncate it to 0. The fractional data is instantly destroyed.
  2. Exact Equality Checks on Repeating Fractions: If you convert 0.1 to a 32-bit float and then write if (sensor_val == 0.1), the code will fail. The decimal 0.1 converts to an infinite binary fraction. The Arduino float documentation explicitly warns that floats are approximations; comparing them for exact equality without an epsilon threshold (e.g., abs(val - 0.1) < 0.0001) is a fundamental logic error.

Fixed-Point Implementation and Conversion FAQ

When building high-speed control loops (like PID motor controllers or digital PLLs on an ESP32), floating-point math can introduce unacceptable jitter. Seasoned firmware engineers convert decimal fractions to binary fixed-point integers to guarantee deterministic execution times.

Bench Tip: The Q-Format Shift Trick
Instead of using the * and / operators for fractional math, convert your decimal constants to binary fixed-point and use bitwise shifts. For a Q8.8 format (8 integer bits, 8 fraction bits), multiplying by 0.625 is identical to multiplying by 160 and then right-shifting by 8: result = (input * 160) >> 8;. This executes in a single clock cycle on most 32-bit MCUs.

Frequently Asked Questions

Why does my ESP32 output a slightly different decimal when I print a binary fraction?
The ESP32 uses 32-bit IEEE 754 floats, which only guarantee about 6 to 7 significant decimal digits of precision. If you convert a fraction that results in a repeating binary sequence, the hardware truncates the 24th bit of the mantissa. When Serial.print() converts that binary back to decimal for your monitor, the rounding error becomes visible.

Can I convert negative decimal fractions to binary using the same formula?
The multiplication formula works for the magnitude, but the storage format shifts. In embedded systems, negative fractions are stored using Two's Complement. For example, in an 8-bit Q0.8 format, -0.625 is not simply a sign bit plus 0.101. You must calculate the two's complement of 0xA0 (160), which yields 0x60 (96) when inverted and added to 1, representing -96/256.

What is the fastest way to convert fraction decimal to binary in Python for testing?
Use the struct module to pack the float into bytes, then format it. This bypasses Python's infinite-precision string representations and shows you exactly what the microcontroller's FPU will see:
import struct; print(bin(struct.unpack('!I', struct.pack('!f', 0.625))[0]))