use crate::MAX_ABS_QUANTITY_POWER;
#[cfg(feature = "no_std")]
use num_traits::float::FloatCore;
#[cfg(not(any(feature = "storage-f64", feature = "storage-f32")))]
const SQRT_10: f64 = 3.162_277_660_168_379;
#[cfg(not(any(feature = "storage-f64", feature = "storage-f32")))]
const INV_SQRT_10: f64 = 0.31622776601683794;
const fn clamp_power_const(power: i64) -> i32 {
if power < -(MAX_ABS_QUANTITY_POWER as i64) {
-MAX_ABS_QUANTITY_POWER
} else if power > MAX_ABS_QUANTITY_POWER as i64 {
MAX_ABS_QUANTITY_POWER
} else {
power as i32
}
}
fn clamp_power(power: i64) -> i32 {
clamp_power_const(power)
}
pub const fn saturating_power_add_const(lhs: i32, rhs: i32) -> i32 {
clamp_power_const(lhs as i64 + rhs as i64)
}
pub const fn saturating_power_sub_const(lhs: i32, rhs: i32) -> i32 {
clamp_power_const(lhs as i64 - rhs as i64)
}
fn pow10(power: i32) -> f64 {
#[cfg(feature = "no_std")]
{
<f64 as FloatCore>::powi(10_f64, power)
}
#[cfg(not(feature = "no_std"))]
{
10_f64.powi(power)
}
}
pub const fn pow10_const(power: i32) -> f64 {
let mut result = 1.0;
if power >= 0 {
let mut remaining = power;
while remaining > 0 {
result *= 10.0;
remaining -= 1;
}
} else {
let mut remaining = power;
while remaining < 0 {
result /= 10.0;
remaining += 1;
}
}
result
}
#[allow(dead_code)]
pub fn saturating_power_add(lhs: i32, rhs: i32) -> i32 {
saturating_power_add_const(lhs, rhs)
}
#[allow(dead_code)]
pub fn saturating_power_sub(lhs: i32, rhs: i32) -> i32 {
saturating_power_sub_const(lhs, rhs)
}
#[allow(dead_code)]
pub fn average_power(lhs: i32, rhs: i32) -> i32 {
let average = (i64::from(lhs) + i64::from(rhs)) / 2;
clamp_power(average)
}
#[cfg(not(any(feature = "storage-f64", feature = "storage-f32")))]
pub const fn canonicalize_quantity_parts_const(multiplier: f64, power: i32) -> (f64, i32) {
if multiplier == 0.0 {
return (0.0, 0);
}
if multiplier.is_nan() {
return (f64::NAN, 0);
}
if multiplier.is_infinite() {
return (multiplier, 0);
}
let mut normalized_multiplier = multiplier;
let mut normalized_power = power;
let mut abs_multiplier = if normalized_multiplier < 0.0 {
-normalized_multiplier
} else {
normalized_multiplier
};
while abs_multiplier >= SQRT_10 {
normalized_multiplier /= 10.0;
abs_multiplier /= 10.0;
normalized_power = saturating_power_add_const(normalized_power, 1);
}
while abs_multiplier < INV_SQRT_10 {
normalized_multiplier *= 10.0;
abs_multiplier *= 10.0;
normalized_power = saturating_power_sub_const(normalized_power, 1);
}
if normalized_power <= -MAX_ABS_QUANTITY_POWER {
(0.0, 0)
} else if normalized_power >= MAX_ABS_QUANTITY_POWER {
if normalized_multiplier < 0.0 {
(f64::NEG_INFINITY, 0)
} else {
(f64::INFINITY, 0)
}
} else {
(normalized_multiplier, normalized_power)
}
}
#[cfg(not(any(feature = "storage-f64", feature = "storage-f32")))]
pub const fn value_to_quantity_parts_const(value: f64) -> (f64, i32) {
if value == 0.0 {
return (0.0, 0);
}
if value.is_nan() {
return (f64::NAN, 0);
}
if value.is_infinite() {
return (value, 0);
}
let mut multiplier = value;
let mut power = 0;
let mut abs_multiplier = if multiplier < 0.0 {
-multiplier
} else {
multiplier
};
while abs_multiplier >= 10.0 {
multiplier /= 10.0;
abs_multiplier /= 10.0;
power = saturating_power_add_const(power, 1);
}
while abs_multiplier < 1.0 {
multiplier *= 10.0;
abs_multiplier *= 10.0;
power = saturating_power_sub_const(power, 1);
}
canonicalize_quantity_parts_const(multiplier, power)
}
#[cfg(not(any(feature = "storage-f64", feature = "storage-f32")))]
pub fn canonicalize_quantity_parts(multiplier: f64, power: i32) -> (f64, i32) {
if multiplier == 0.0 {
return (0.0, 0);
}
if multiplier.is_nan() {
return (f64::NAN, 0);
}
if multiplier.is_infinite() {
return (multiplier, 0);
}
let power_on_multiplier = multiplier.abs().log10().round() as i32;
let normalized_multiplier = multiplier / 10_f64.powi(power_on_multiplier);
let normalized_power = saturating_power_add(power, power_on_multiplier);
if normalized_power <= -MAX_ABS_QUANTITY_POWER {
(0.0, 0)
} else if normalized_power >= MAX_ABS_QUANTITY_POWER {
if normalized_multiplier.is_sign_negative() {
(f64::NEG_INFINITY, 0)
} else {
(f64::INFINITY, 0)
}
} else {
(normalized_multiplier, normalized_power)
}
}
#[allow(dead_code)]
pub const fn quantity_parts_to_value_const(multiplier: f64, power: i32) -> f64 {
if multiplier == 0.0 {
0.0
} else if multiplier.is_nan() {
f64::NAN
} else if multiplier.is_infinite() {
multiplier
} else {
multiplier * pow10_const(power)
}
}
#[allow(dead_code)]
pub fn quantity_parts_to_value(multiplier: f64, power: i32) -> f64 {
if multiplier == 0.0 {
0.0
} else if multiplier.is_nan() {
f64::NAN
} else if multiplier.is_infinite() {
multiplier
} else {
multiplier * pow10(power)
}
}
#[cfg(any(feature = "storage-f64", feature = "storage-f32"))]
#[allow(dead_code)]
pub fn value_to_quantity_parts(value: f64) -> (f64, i32) {
(value, 0)
}
#[cfg(not(any(feature = "storage-f64", feature = "storage-f32")))]
#[allow(dead_code)]
pub fn value_to_quantity_parts(value: f64) -> (f64, i32) {
if value == 0.0 {
(0.0, 0)
} else if value.is_nan() {
(f64::NAN, 0)
} else if value.is_infinite() {
(value, 0)
} else {
let power = value.abs().log10().floor() as i32;
let multiplier = value / 10_f64.powi(power);
canonicalize_quantity_parts(multiplier, power)
}
}
#[allow(dead_code)]
pub fn pow10_delta(lhs: i32, rhs: i32) -> f64 {
pow10(saturating_power_sub(lhs, rhs))
}
#[cfg(not(any(feature = "storage-f64", feature = "storage-f32")))]
pub fn quantity_parts_rem(
lhs_multiplier: f64,
lhs_power: i32,
rhs_multiplier: f64,
rhs_power: i32,
) -> (f64, i32) {
if lhs_multiplier.is_nan() || rhs_multiplier.is_nan() {
return (f64::NAN, 0);
}
if rhs_multiplier == 0.0 {
return (f64::NAN, 0);
}
if lhs_multiplier == 0.0 {
return (0.0, 0);
}
if lhs_multiplier.is_infinite() {
return (f64::NAN, 0);
}
if rhs_multiplier.is_infinite() {
return canonicalize_quantity_parts(lhs_multiplier, lhs_power);
}
let lhs_abs = lhs_multiplier.abs();
let rhs_abs = rhs_multiplier.abs();
let common_power = lhs_power.max(rhs_power);
let scaled_lhs = lhs_abs * pow10_delta(lhs_power, common_power);
let scaled_rhs = rhs_abs * pow10_delta(rhs_power, common_power);
if scaled_lhs < scaled_rhs {
return canonicalize_quantity_parts(lhs_multiplier, lhs_power);
}
if scaled_lhs == scaled_rhs {
return (0.0, 0);
}
let sign = if lhs_multiplier.is_sign_negative() {
-1.0
} else {
1.0
};
let delta = i64::from(lhs_power) - i64::from(rhs_power);
let rem_multiplier = if delta >= 0 {
let mut rem = lhs_abs % rhs_abs;
for _ in 0..delta {
rem = (rem * 10.0) % rhs_abs;
}
rem
} else {
lhs_abs * pow10_delta(lhs_power, rhs_power) % rhs_abs
};
canonicalize_quantity_parts(sign * rem_multiplier, rhs_power)
}
#[cfg(feature = "no_std")]
pub fn sqrt(value: f64) -> f64 {
if value < 0.0 {
return f64::NAN;
}
if value == 0.0 || value.is_nan() || value.is_infinite() {
return value;
}
let mut estimate = if value >= 1.0 { value } else { 1.0 };
for _ in 0..32 {
estimate = 0.5 * (estimate + value / estimate);
}
estimate
}
#[cfg(not(feature = "no_std"))]
pub fn sqrt(value: f64) -> f64 {
value.sqrt()
}