Extracting Numeric Values¶
Learn when and how to extract raw numbers from quantities.
Goal: Get numeric values in specific units for output or legacy APIs
Time: ~20 minutes
Why Extract Values?¶
Sometimes you need just the number:
- Interfacing with external systems:
- Displaying to users
- Calling legacy APIs that take raw numbers
- Storing in databases
- Plotting graphs
- Performance-critical computations:
- Linear algebra operations on large matrices/vectors
- High-Performance Computing (HPC) simulations
- Data crunching and numerical analysis
- GPU computations requiring contiguous memory arrays
In performance-critical scenarios, you may need to extract values into uniform arrays for efficient processing, trading type safety for computational speed.
The .numerical_value_in() Method¶
// ce-embed height=600 compiler=clang2110 flags="-std=c++23 -stdlib=libc++ -O3" mp-units=trunk
#include <mp-units/systems/si.h>
#include <mp-units/systems/yard_pound.h>
#include <iostream>
int main()
{
using namespace mp_units;
using namespace mp_units::si::unit_symbols;
using namespace mp_units::yard_pound::unit_symbols;
quantity distance = 5.0 * km;
// Extract numeric value in specific unit
double meters = distance.numerical_value_in(m);
double kilometers = distance.numerical_value_in(km);
double miles = distance.numerical_value_in(mi);
std::cout << "Distance: " << distance << "\n\n";
std::cout << "As number in meters: " << meters << "\n";
std::cout << "As number in kilometers: " << kilometers << "\n";
std::cout << "As number in miles: " << miles << "\n";
}
Key insight: .numerical_value_in() extracts the raw numeric value in a specified unit,
completely removing type safety. The result is just a number with no compile-time protection
against unit errors.
Warning
After extraction, the number has NO unit safety! Use carefully.
Difference: .in() vs .numerical_value_in()¶
// ce-embed height=550 compiler=clang2110 flags="-std=c++23 -stdlib=libc++ -O3" mp-units=trunk
#include <mp-units/systems/si.h>
#include <iostream>
int main()
{
using namespace mp_units;
using namespace mp_units::si::unit_symbols;
quantity speed = 100.0 * km / h;
// .in() returns a quantity (still type-safe!)
quantity speed_mps = speed.in(m / s);
std::cout << "With .in(): " << speed_mps << " (still a quantity)\n";
// .numerical_value_in() returns just the number
double speed_value = speed.numerical_value_in(m / s);
std::cout << "With .numerical_value_in(): " << speed_value << " (raw double)\n";
// Calculate distance after 30 minutes
quantity time = 30.0 * min;
// With quantity: automatic unit handling
quantity distance = speed_mps * time;
std::cout << "\nDistance with quantity: " << distance.in(km) << "\n";
// With raw number: you must handle units manually!
double time_seconds = time.numerical_value_in(s); // Need to extract in compatible unit
double distance_meters = speed_value * time_seconds; // Manual calculation
std::cout << "Distance with raw numbers: " << distance_meters << " m\n";
std::cout << " (had to manually ensure compatible units and track result unit!)\n";
// If you need to calculate speed again from these raw numbers:
double calculated_speed = distance_meters / time_seconds; // Just a double!
std::cout << "\nCalculated speed from raw numbers: " << calculated_speed << " m/s\n";
std::cout << " (no automatic derived quantity type, no dimensional analysis!)\n";
}
Key insight: When you extract numerical values, you lose not only type safety, but also
the ability to scale the unit easily or get automatically derived quantity types. The result
of double / double is just a double, not a quantity<speed>. Prefer .in() when you
can stay type-safe; use .numerical_value_in() only when necessary.
Common Use Case: Legacy APIs¶
// ce-embed height=650 compiler=clang2110 flags="-std=c++23 -stdlib=libc++ -O3" mp-units=trunk
#include <mp-units/systems/si.h>
#include <iostream>
// Legacy function that takes raw numbers (be careful!)
void legacy_draw_circle(double x, double y, double radius)
{
std::cout << "Drawing circle at (" << x << ", " << y << ") with radius " << radius << "\n";
}
int main()
{
using namespace mp_units;
using namespace mp_units::si::unit_symbols;
// Work with type-safe quantities
quantity x_pos = 100.0 * mm;
quantity y_pos = 50.0 * mm;
quantity radius = 25.0 * mm;
// Extract only when calling legacy API
legacy_draw_circle(x_pos.numerical_value_in(mm),
y_pos.numerical_value_in(mm),
radius.numerical_value_in(mm));
// Stay type-safe for your own calculations
quantity diameter = 2.0 * radius;
// Note: `pi` is a compile-time unit constant provided by mp-units
quantity circumference = 2.0 * pi * radius;
std::cout << "\nDiameter: " << diameter << "\n";
std::cout << "Circumference: " << circumference << " = " << circumference.in(mm) << "\n";
}
Key insight: Extract values only at system boundaries (legacy APIs, databases, external
libraries) while keeping all internal calculations type-safe with quantity. This minimizes
the surface area where unit errors can occur.
Custom Text Output¶
// ce-embed height=650 compiler=clang2110 flags="-std=c++23 -stdlib=libc++ -O3" mp-units=trunk
#include <mp-units/systems/si.h>
#include <mp-units/systems/yard_pound.h>
#include <iostream>
#include <format>
int main()
{
using namespace mp_units;
using namespace mp_units::si::unit_symbols;
using namespace mp_units::yard_pound::unit_symbols;
quantity mass = 72.5 * kg;
quantity speed = 65.3 * (mi / h);
quantity distance = 142.7 * mi;
// mp-units default formatting may not satisfy your requirements
std::println("\nWith automatic formatting:");
std::println("Mass: {}", mass);
std::println("Speed: {}", speed);
std::println("Distance: {}", distance);
// You might be tempted to extract numerical value explicitly
std::println("\nUsing extracted values in specified unit:");
std::println("Mass: {:.1f} in kg", mass.numerical_value_in(kg));
std::println("Speed: {:.0f} in mph", speed.numerical_value_in(mi / h));
std::println("Distance: {:.1f} in miles", distance.numerical_value_in(mi));
// Typically it is better to benefit from the mp-units rich formatting options
std::println("\nUsing format specifiers:");
std::println("Mass: {:%N in %U:N[.1f]}", mass.in(kg));
std::println("Speed: {:%N:N[.0f]} in mph", speed.in(mi / h));
std::println("Distance: {:%N:N[.1f]} in miles", distance.in(mi));
}
Key insight: Prefer format specifiers (%N, %U) over .numerical_value_in() for output
formatting. Use %U when the unit symbol matches your desired text (e.g., "kg"), and provide
custom text when you need specific names (e.g., "mph" instead of "mi/h"). This keeps your
code type-safe while providing flexible formatting.
Best Practices¶
✅ DO: Keep calculations in quantity as long as possible
✅ DO: Extract values only at boundaries (display, APIs, storage)
✅ DO: Document the expected unit when extracting
❌ DON'T: Extract early and pass raw numbers around
❌ DON'T: Mix extracted values from different units
Challenges¶
- Database storage: Extract mass in kg, volume in L, temperature in K for storage
- Plotting: Create 5 time values (0-100 s) and 5 distance values, extract for plotting
- User input: Read a number as mph, create quantity, extract as km/h for display
What You Learned?¶
✅ Use .numerical_value_in(unit) to extract raw numbers (removes all type safety)
✅ Use format specifiers (%N, %U) for output instead of extracting values
✅ Use %U when unit symbol matches desired text, custom text otherwise
✅ .in() keeps type safety, .numerical_value_in() loses it
✅ Calculations with raw numbers return double, not derived quantity types
✅ Extract values only at system boundaries (APIs, display, storage, performance)
✅ Keep all internal calculations in quantity as long as possible
✅ Document expected units when extracting values