95 lines
3.9 KiB
C++
95 lines
3.9 KiB
C++
#include "conversion.h"
|
||
#include <QRegularExpression>
|
||
#include <cmath>
|
||
|
||
namespace Conversion {
|
||
QStringList units(Category category)
|
||
{
|
||
if (category == Category::Temperature)
|
||
return {QStringLiteral("摄氏度 °C"), QStringLiteral("华氏度 °F"),
|
||
QStringLiteral("开尔文 K")};
|
||
return {QStringLiteral("毫米 mm"), QStringLiteral("厘米 cm"),
|
||
QStringLiteral("米 m"), QStringLiteral("千米 km"),
|
||
QStringLiteral("英寸 in"), QStringLiteral("英尺 ft")};
|
||
}
|
||
|
||
Result convert(const QString &text, Category category, int from, int to)
|
||
{
|
||
Result result;
|
||
const QString input = text.trimmed();
|
||
// 先检查完整输入,避免把“12abc”误当成有效数值。
|
||
static const QRegularExpression number(
|
||
QStringLiteral("^[+-]?(?:[0-9]+(?:\\.[0-9]*)?|\\.[0-9]+)(?:[eE][+-]?[0-9]+)?$"));
|
||
if (!number.match(input).hasMatch()) {
|
||
result.error = QStringLiteral("请输入完整的数值,例如 25、-10.5 或 1e3;小数点使用英文句点。");
|
||
return result;
|
||
}
|
||
bool ok = false;
|
||
const double value = input.toDouble(&ok);
|
||
if (!ok || !std::isfinite(value)) {
|
||
result.error = QStringLiteral("输入超出了可计算的数值范围,请减小数值。");
|
||
return result;
|
||
}
|
||
const QStringList names = units(category);
|
||
if (from < 0 || to < 0 || from >= names.size() || to >= names.size()) {
|
||
result.error = QStringLiteral("请选择有效的单位。");
|
||
return result;
|
||
}
|
||
if (category == Category::Temperature) {
|
||
// 先统一为摄氏度,再换算为目标单位,避免为每对单位各写一套公式。
|
||
double celsius = value;
|
||
QString first;
|
||
if (from == 1) {
|
||
celsius = (value - 32.0) / 1.8;
|
||
first = QStringLiteral("先换成摄氏度:C = (F − 32) ÷ 1.8。");
|
||
} else if (from == 2) {
|
||
celsius = value - 273.15;
|
||
first = QStringLiteral("先换成摄氏度:C = K − 273.15。");
|
||
} else {
|
||
first = QStringLiteral("输入已经是摄氏度,可直接作为中间值。");
|
||
}
|
||
if (celsius < -273.15 - 1e-9) {
|
||
result.error = QStringLiteral("温度不能低于绝对零度:−273.15 °C / −459.67 °F / 0 K。");
|
||
return result;
|
||
}
|
||
// 允许边界处的浮点舍入误差,避免把绝对零度误判为非法输入。
|
||
if (celsius < -273.15)
|
||
celsius = -273.15;
|
||
QString second;
|
||
if (to == 1) {
|
||
result.value = celsius * 1.8 + 32.0;
|
||
second = QStringLiteral("再换成华氏度:F = C × 1.8 + 32。");
|
||
} else if (to == 2) {
|
||
result.value = celsius + 273.15;
|
||
second = QStringLiteral("再换成开尔文:K = C + 273.15。");
|
||
} else {
|
||
result.value = celsius;
|
||
second = QStringLiteral("目标是摄氏度,中间值就是结果。");
|
||
}
|
||
result.explanation = first + QStringLiteral("\n") + second;
|
||
} else {
|
||
if (value < 0.0) {
|
||
result.error = QStringLiteral("本工具换算的是长度,输入值不能为负数。");
|
||
return result;
|
||
}
|
||
const double metersPerUnit[] = {0.001, 0.01, 1.0, 1000.0, 0.0254, 0.3048};
|
||
result.value = value * (metersPerUnit[from] / metersPerUnit[to]);
|
||
result.explanation =
|
||
QStringLiteral("以米为统一单位:结果 = 输入值 × 源单位的米数 ÷ 目标单位的米数。\n"
|
||
"本次计算:%1 × %2 ÷ %3。")
|
||
.arg(input)
|
||
.arg(metersPerUnit[from], 0, 'g', 12)
|
||
.arg(metersPerUnit[to], 0, 'g', 12);
|
||
}
|
||
if (!std::isfinite(result.value)) {
|
||
result.error = QStringLiteral("换算结果超出了可显示的数值范围。");
|
||
return result;
|
||
}
|
||
if (result.value == 0.0)
|
||
result.value = 0.0; // 避免显示 -0。
|
||
result.ok = true;
|
||
return result;
|
||
}
|
||
}
|
||
|