Project 4.06 Section 4 ⚡ Embedded Relevance: High Floating Point FPU Fixed-Point Math Q15 Format ARM Cortex-M0

4.06 Float vs Double Array Footprint & Q15/Q31 Fixed-Point Math on Hardware without FPU

Executive Summary: Converting temperature sensor readings stored in arrays. We explore the memory difference between float (32-bit IEEE 754) and double (64-bit), and show how to implement fixed-point arithmetic (Q-format) for microcontrollers without a hardware Floating Point Unit (FPU).

💻 1. Annotated Source Code

#include <iostream>
#include <array>
using namespace std;

int main() {
	const int NUM_DAYS = 7;
	array<double, NUM_DAYS> fahrenheitTemps;

	for (int i = 0; i < NUM_DAYS; i++) {
		cout << "Enter the temperature in Fahrenheit for day "
			<< (i + 1) << ": ";
		cin >> fahrenheitTemps[i];
	}

	cout << "\nHere are the temperatures converted to Celsius:"<<endl;

	for (double tempF : fahrenheitTemps) {
		double tempC = (tempF - 32) * 5.0 / 9;
		cout << "F: " << tempF << " ->  C:" << tempC << endl;
	}

	return 0;
}

📐 2. Architecture & UML Class Model

📐 Array Temperature Conversion & DSP Scaling Model
+ Public - Private # Protected
<<compilation-unit>> TemperatureConverter Scaling Pipeline
-fahrenheitTemps[5] : double
-celsiusTemps[5] : double
+convertFtoC(f: double) : double
+batchConvert(fArr: const double*, cArr: double*, len: size_t) : void

📚 3. Core C++ Concepts Deep-Dive

1. IEEE 754 Floating-Point Sizing

  • float: 32 bits (1 sign bit, 8 exponent bits, 23 mantissa bits) $\approx 7$ decimal digits precision.
  • double: 64 bits (1 sign bit, 11 exponent bits, 52 mantissa bits) $\approx 15-17$ decimal digits precision.

2. Float Literal Suffix

In C++, floating literals without a suffix (e.g. 32.0) default to double. Writing 32.0f ensures single-precision operations.

⚡ 4. Embedded Systems & Hardware Reality

1. Software Emulation vs Hardware FPU

Microcontrollers like ARM Cortex-M0, M0+, and M3 lack a hardware FPU. Floating-point operations pull in software emulation library routines (__aeabi_fadd, __aeabi_fmul), adding 4KB-10KB of Flash bloat and taking 20-100 clock cycles per operation.

2. Q-Format Fixed-Point Arithmetic

Fixed-point representation uses standard integer registers to represent fractions with deterministic, single-cycle integer instructions.

💡 5. Production-Ready Embedded Refactoring

Q8.8 Fixed-Point Temperature Representation (Single-Cycle Arithmetic):

💡 Production-Ready Refactor
#include <cstdint>

// Fixed-Point Q8.8 (16-bit integer: 8 bits integer, 8 bits fraction)
struct FixedQ8_8 {
    int16_t raw;

    static constexpr FixedQ8_8 from_float(float val) noexcept {
        return FixedQ8_8{static_cast<int16_t>(val * 256.0f)};
    }

    constexpr int16_t to_celsius_int() const noexcept {
        return raw >> 8; // Fast single-cycle bit shift
    }
};

// Celsius to Fahrenheit in Q8.8 fixed-point: (C * 9/5) + 32
constexpr FixedQ8_8 celsiusToFahrenheit(FixedQ8_8 c) noexcept {
    int32_t intermediate = (static_cast<int32_t>(c.raw) * 9) / 5;
    return FixedQ8_8{static_cast<int16_t>(intermediate + (32 << 8))};
}

📝 Knowledge Verification Quiz

Test your understanding of the C++ concepts and embedded microcontroller trade-offs covered in this guide. Click any option for instant feedback.

Q1. What happens when floating-point math is executed on a microcontroller without a hardware FPU (e.g. Cortex-M0)?
A The compiler links software emulation library routines, increasing code size and taking dozens of clock cycles per operation
B A hardware HardFault occurs immediately
C The CPU automatically upgrades to 64-bit mode
D Floating point operations are rounded to zero instantly
Detailed Explanation: Without an FPU, floating-point operations are emulated in software via math routines, consuming extra Flash space and hundreds of clock cycles.
Q2. Why is 'float x = 5.0;' suboptimal on a 32-bit MCU with single-precision FPU?
A 5.0 is a double literal, causing the compiler to perform double-precision promotion before converting back to float
B 5.0 is interpreted as an integer
C It causes a memory leak on the heap
D It disables the compiler optimizer
Detailed Explanation: Unadorned floating literals default to double (64-bit). On MCUs with only a single-precision (32-bit) FPU, this invokes slow software double-precision emulation.
Q3. What does Q8.8 fixed-point format represent?
A A 16-bit integer where the upper 8 bits represent the integer part and the lower 8 bits represent the fractional part
B An 8-bit float with 8 exponent bits
C A quaternion rotation matrix
D An encrypted 8-byte buffer
Detailed Explanation: Q8.8 uses an integer variable where the radix point is fixed: 8 bits for integer magnitude and 8 bits for fractional precision ($1/256$ resolution).
Q4. How fast is a fixed-point division by 256 compared to floating-point division on a Cortex-M0?
A Fixed point uses a single-cycle arithmetic right-shift (ASR #8), executing 20x-50x faster than software float division
B Both take exactly 1 clock cycle
C Floating point division is faster
D Fixed point cannot perform division
Detailed Explanation: Dividing by $2^8 = 256$ in fixed-point is a single-cycle bit shift (ASR #8), whereas software float division takes dozens of cycles.