Here is a procedure I wrote to apply a greek prefix to a value so that the coefficient lies between 1 and 1000.
applyGreekPrefix := proc(x)
description
"Accepts a number, and if it has units, a Greek prefix is added so that the coefficient is between 1 and 1000",
"Values without units or a unit of muliple units are returned as-is"
;
local prefixes, powers, value, unit, exponent, new_value, idx, prefix, strUnit, new_unit, i, SIx, returnval;
if Units:-Split(x)[2] = 1 or numelems(indets(op(2, x), 'name')) > 1 then
# Value has no units or consists of multiple units
returnval := x;
else
# Define the SI prefixes and their corresponding powers of 10
prefixes := ["q", "r", "y", "z", "a", "f", "p", "n", "μ", "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y", "R", "Q"];
powers := [seq(i, i = -30 .. 30, 3)];
# Convert to SI unit w/o prefix
SIx := convert(x, 'system', 'SI');
# Separate the value and the unit
value := evalf(op(1, SIx));
unit := op(2, SIx);
strUnit := convert(unit, string); # Convert the unit to a string representation
# Calculate the exponent of 10 for the value
exponent := floor(log10(abs(value)));
# Find the closest power of 10 that makes the coefficient between 1 and 1000
idx := 0;
for i from 1 to nops(powers) do
if exponent >= powers[i] then
idx := i;
end if;
end do;
# Adjust the value based on the selected prefix
new_value := value / 10^powers[idx];
prefix := prefixes[idx];
# Construct the new unit using Units:-Unit if a prefix is needed
if prefix = "" then
new_unit := unit; # No prefix needed, use the original unit
else
new_unit := parse(StringTools:-Substitute(strUnit, "Units:-Unit(", cat("Units:-Unit(", prefix))); # Construct a new unit with the prefix
end if;
# Return the adjusted value with the new unit
returnval := new_value * new_unit;
end if;
return returnval;
end proc:
list_tests:=[0.0001*Unit('kV'), 10000*Unit('V'), 10*Unit('V'/'us'), 12334];
for test in list_tests do
print (test, "becomes", applyGreekPrefix(test));
end do;