fix(resample): 重采样间隔/频率按真实值显示(次级单位与频率保留三位小数)
问题(用户实测 MF4-Record.MF4 / m_MOT_Current_DCom[0]): - 最小数据间隔真实值 0.003696631s,却被显示成 3ms 700us(us 部分被向上截取); - 对应频率真实值 270.516586589Hz,却被显示成 270.27Hz; - 最大数据间隔 0.016182614s 显示成 16ms 200us、频率显示成 61.7284Hz,同样不准。 根因(两个,同一个坑): 1. RoundIntervalForDisplay() 把间隔先舍入到 3 位有效数字(0.003696631 -> 0.0037), 次级单位因此变成 700us,真实尾数被抹掉; 2. 频率用舍入后的 3.70ms 换算得 270.27Hz,而输入框用原值得 270.517Hz,两处不一致。 修法(一律使用真实值,舍入只发生在输出文本的最后一步): - Utf8Util 新增 FormatDecimalFixed(value, decimals=3):固定小数位、保留尾随 0、 绝不出现科学计数法; - Utf8Util 新增 FormatSubUnit(value, maxDecimals):带小数才补到 3 位,整数不加小数点 (5.06s -> 5s 60ms,而不是 5s 60.000ms); - FormatDurationCompound 的两处次级单位(s+ms、ms/us/ns)改用它; - FormatFrequency 改为 FormatDecimalFixed(scaled, 3);若保留 3 位后变成 0.000 (超低频,如 1/3662s ≈ 0.27mHz)则退回 FormatDecimal,避免有意义的值显示成 0; - PlotViewInternal.hpp 删除 RoundIntervalForDisplay,FormatIntervalText 直接用真实值; - PlotViewPaint.cpp 的统计行尾缀与频率输入框都用真实 interval 换算。 修复后的显示: min interval 0.003696631s -> 3ms 696.631us frequency 1/0.003696631 -> 270.517 Hz max interval 0.016182614s -> 16ms 182.614us frequency 1/0.016182614 -> 61.795 Hz 同时撤销了此前两次仅修改 agent_mem.md 的提交(47b1213 / caa035a,内容为乱码且无有效信息), 并按正确编码重新整理本轮记忆章节。 面板仍为四行(最大间隔 / 最小间隔 / 重采样间隔 / 重采样频率),未改动任何 UI 布局。 make release 零 warning。
This commit is contained in:
1 parent
9fdbf49841
commit
c4300bd0e1
6 files changed
+260
-43
No files matched your search
@@ -1306,34 +1306,17 @@ namespace jdi
|
||||
return FormatValue(seconds) + L"s";
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 把时间间隔四舍五入到 **3 位有效数字**(供「重采样」面板显示)。
|
||||
*
|
||||
* 计算出来的数据间隔常常带一长串浮点尾数(如 `5.0000158us`)。整串显示既没有意义,
|
||||
* 又会把面板撑得很宽(还要在后面追加「(频率)」尾缀,用户 2026-11-03 要求)。
|
||||
* 3 位有效数字足以区分相邻的采样间隔,且让显示稳定。
|
||||
*
|
||||
* @param [IN] seconds 时间间隔(秒)。
|
||||
* @return 舍入后的间隔;非法/非正输入原样返回。
|
||||
*/
|
||||
inline double RoundIntervalForDisplay(double seconds)
|
||||
{
|
||||
if (!std::isfinite(seconds) || seconds <= 0.0)
|
||||
{
|
||||
return seconds;
|
||||
}
|
||||
const double magnitude = std::floor(std::log10(seconds));
|
||||
const double scale = std::pow(10.0, 2.0 - magnitude);
|
||||
const double rounded = std::round(seconds * scale) / scale;
|
||||
return (rounded > 0.0) ? rounded : seconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 时间间隔的显示文本(**不带** "Δt:" 前缀)。
|
||||
*
|
||||
* 「重采样」面板的「最大 / 最小数据间隔」两行用它:单位随间隔长短自适应
|
||||
*(`FormatDurationCompound`:1h 1min 2s / 5s 60ms / 120us …),
|
||||
* 且先舍入到 3 位有效数字(见 `RoundIntervalForDisplay`),**不出现科学计数法**。
|
||||
*(`FormatDurationCompound`:1h 1min 2s / 5s 60ms / 3ms 696.631us …),
|
||||
* **绝不出现科学计数法**。
|
||||
*
|
||||
* ⚠️⚠️ **不要在这里做舍入**(用户 2026-11-03 明确要求):
|
||||
* 旧实现先把值舍入到 3 位**有效数字**再格式化,把真实尾数抹掉了 ——
|
||||
* `0.003696631s` 被显示成 `3ms 700us`(而正确是 `3ms 696.631us`)。
|
||||
* 现在一律用**真实值**(`FormatDurationCompound` 内部把次级单位保留 3 位小数)。
|
||||
*
|
||||
* @param [IN] seconds 时间间隔(秒)。
|
||||
* @return 文本。
|
||||
@@ -1344,7 +1327,7 @@ namespace jdi
|
||||
{
|
||||
return L"—";
|
||||
}
|
||||
return FormatDurationCompound(RoundIntervalForDisplay(seconds));
|
||||
return FormatDurationCompound(seconds);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -5458,16 +5458,21 @@ namespace jdi
|
||||
minInterval = resampleItem->minInterval;
|
||||
// 「最大 / 最小数据间隔」= 自适应单位的**小数**文本(绝不用科学计数法),
|
||||
// 末尾追加一个括号内的「对应频率」尾缀(单位在 Hz/kHz/MHz/GHz 之间自适应)。
|
||||
// ⚠️ 频率必须由**同一份舍入后的间隔**换算,否则两列会对不上
|
||||
// (例如显示 `5us` 却给出 `199.99937 kHz`)。
|
||||
//
|
||||
// ⚠️⚠️ 全部使用**真实值**(用户 2026-11-03 明确要求):
|
||||
// * 时间间隔:`FormatDurationCompound` 会把次级单位保留 3 位小数
|
||||
// ⇒ `0.003696631s` 显示为 `3ms 696.631us`(旧实现舍入到 3 位有效数字
|
||||
// 得到 `3ms 700us`,把真实尾数抹掉了);
|
||||
// * 频率:由**未舍入的真实间隔**换算 ⇒ `270.517 Hz`(旧实现用舍入后的
|
||||
// `3.70ms` 换算得到 `270.27 Hz`,与输入框不一致)。
|
||||
// 两条路径都用真实值 ⇒ 统计行与输入框天然一致。
|
||||
const auto intervalWithFrequency = [](double seconds) -> std::wstring
|
||||
{
|
||||
const double shown = RoundIntervalForDisplay(seconds);
|
||||
std::wstring text = FormatDurationCompound(shown);
|
||||
if (std::isfinite(shown) && shown > 0.0)
|
||||
std::wstring text = FormatDurationCompound(seconds);
|
||||
if (std::isfinite(seconds) && seconds > 0.0)
|
||||
{
|
||||
text += L"(";
|
||||
text += FormatFrequencyText(1.0 / shown);
|
||||
text += FormatFrequencyText(1.0 / seconds);
|
||||
text += L")";
|
||||
}
|
||||
return text;
|
||||
@@ -5495,19 +5500,13 @@ namespace jdi
|
||||
{
|
||||
// 非编辑态:自适应单位 + 小数(与「最大/最小数据间隔」同一套单位形式)。
|
||||
// 编辑态:只显示**秒**数值(不带单位),由 `PaintEditField` 绘制。
|
||||
// ⚠️ 间隔文本必须与统计行**同一份舍入值**(`RoundIntervalForDisplay`),
|
||||
// 否则统计行显示 `3ms 700us` 而输入框显示 `3ms 696.631us`(用户 2026-11-03)。
|
||||
paintInputRow(layout.rowIntervalField[rowIndex], kResampleIntervalLabel,
|
||||
FormatIntervalText(interval), editingInterval);
|
||||
}
|
||||
if (rowIndex < layout.rowFrequencyField.size())
|
||||
{
|
||||
// ⚠️ 频率必须由**与间隔输入框同一份显示值**换算(`RoundIntervalForDisplay`),
|
||||
// 否则统计行写 `270.27 Hz`(3.70ms 换算)而这一行写 `270.517 Hz`
|
||||
// (3.696631ms 换算)—— 用户 2026-11-03 报告的"两者不一致"。
|
||||
const double shownInterval =
|
||||
(interval > 0.0) ? RoundIntervalForDisplay(interval) : 0.0;
|
||||
const double hertz = (shownInterval > 0.0) ? (1.0 / shownInterval) : 0.0;
|
||||
// ⚠️ 频率同样用**真实间隔**换算(与时间间隔行同源)⇒ `270.517 Hz`。
|
||||
const double hertz = (interval > 0.0) ? (1.0 / interval) : 0.0;
|
||||
// 非编辑态:频率单位自适应(Hz/kHz/MHz/GHz);编辑态:只显示 Hz 数值。
|
||||
paintInputRow(layout.rowFrequencyField[rowIndex], kResampleFrequencyLabel,
|
||||
(hertz > 0.0) ? FormatFrequencyText(hertz) : L"—",
|
||||
|
||||
@@ -9762,3 +9762,103 @@ E 面板:四行几何存在;最大间隔行文本(含(频率)尾缀)
|
||||
* **先打印数据、再改 UI**;`FormatIntervalText` / `FormatFrequencyText` / `RoundIntervalForDisplay`
|
||||
这类"显示层"函数的**舍入基准**是首要怀疑对象。
|
||||
* **不要擅自改用户的 UI 布局/行数**——发现问题优先问,或只改数值来源。
|
||||
|
||||
---
|
||||
|
||||
## 2026-11-03 第二十六轮(本机 dev):重采样面板拖动/关闭失效 + 显示精度按真实值 + 派生烘焙接入公式 + 克隆拆分
|
||||
|
||||
> 用户报告的六件事:①「重采样」面板拖不动、✕ 关不掉;②统计「最小数据间隔」与「重采样频率」
|
||||
> 输入框显示值不一致;③所有派生烘焙没接入「公式映射」;④信号右键菜单要拆成两个克隆;
|
||||
> ⑤显示精度:间隔要 3ms 696.631us、频率要 270.517 Hz(真实值保留三位小数),
|
||||
> 而不是 3ms 700us / 270.27 Hz;⑥面板仍须四行,不得擅自增改 UI。
|
||||
|
||||
### 1. 「重采样」面板拖不动 + ✕ 关不掉(两个独立缺陷)
|
||||
|
||||
- 拖不动(两处):
|
||||
- `OnLButtonDown` 的 `resampleMode` 分支末尾无条件 `return true`,把标题栏按下也吞了,
|
||||
而「标题栏 -> `EDGE_PANEL_MOVE`」的判定在面板分支最末尾 => 改为只消费 `insideContent` 内的点击。
|
||||
- `OnMouseMove` 的 `EDGE_PANEL_MOVE` -> `switch (m_edgePanelDragKind)` 漏 `case RESAMPLE`
|
||||
=> 位置写进 `edgePanel`、又从 `resamplePanel` 读 => 纹丝不动。
|
||||
- ✕ 关不掉:`OnLButtonUp` 的 `m_pendingPanelClose` -> `switch (kind)` 漏 `RESAMPLE`
|
||||
=> 落进 `default: SetLaneEdgeCount(...)`(关掉的是「边沿计数」)。补 `SetLaneResample(lane, false)`。
|
||||
- 规律:新增 `EdgePanelKind` 时每个 `switch (kind)` 都要补分支。
|
||||
历史踩坑:`MARK` 漏过一次、`RESAMPLE` 这次漏了两处。以 `kEdgePanelKinds[]` 为准逐个 grep。
|
||||
|
||||
### 2. 显示精度:一律用真实值,次级单位/频率保留三位小数
|
||||
|
||||
真实现测(MF4-Record.MF4 / m_MOT_Current_DCom[0],8475 采样,6857 个不同间隔):
|
||||
|
||||
| 量 | 真实值 | 旧显示(错) | 现行显示(对) |
|
||||
| --- | --- | --- | --- |
|
||||
| 最小间隔 | 0.003696631 s | 3ms 700us | **3ms 696.631us** |
|
||||
| 最小间隔频率 | 270.516586589 Hz | 270.27 Hz | **270.517 Hz** |
|
||||
| 最大间隔 | 0.016182614 s | 16ms 200us | **16ms 182.614us** |
|
||||
| 最大间隔频率 | 61.7947 Hz | 61.7284 Hz | **61.795 Hz** |
|
||||
|
||||
两个根因:
|
||||
|
||||
1. `RoundIntervalForDisplay()` 把间隔舍入到 3 位有效数字(0.003696631 -> 0.0037)
|
||||
=> 次级单位变成 700us,尾数被抹掉。该函数已删除。
|
||||
2. 频率用舍入后的 3.70ms 换算 => 270.27 Hz;而输入框用原值 => 270.517 Hz
|
||||
=> 两处不一致。现在统一用真实间隔换算,天然一致。
|
||||
|
||||
修法(`Utilities/Src/Utf8Util.cpp`):
|
||||
|
||||
- 新增 `FormatDecimalFixed(value, decimals = 3)`:固定小数位、保留尾随 0、
|
||||
绝不出现科学计数法(与 `FormatDecimal` 的去尾随 0 语义相反)。
|
||||
- 新增 `FormatSubUnit(value, maxDecimals)`:带小数才补到 3 位,整数不加小数点
|
||||
(5.06s -> 5s 60ms,而不是 5s 60.000ms)。
|
||||
- `FormatDurationCompound` 的两处次级单位(s+ms、ms/us/ns)改用它。
|
||||
- `FormatFrequency` 改为 `FormatDecimalFixed(scaled, 3)`;若保留 3 位后变成 0.000
|
||||
(超低频,如 1/3662s ≈ 0.27mHz)则退回 `FormatDecimal`,避免把有意义的值显示成 0。
|
||||
- `PlotViewInternal.hpp`:删除 `RoundIntervalForDisplay`,`FormatIntervalText` 直接用真实值。
|
||||
- `PlotViewPaint.cpp`:统计行 `intervalWithFrequency` 与频率输入框都用真实 `interval`。
|
||||
|
||||
关键不变式:任何显示路径都不得先舍入再换算(舍入只能发生在最后一步输出文本时,
|
||||
且间隔与频率必须基于同一个未舍入的原始值)。这正是旧实现两次翻车的同一个坑。
|
||||
|
||||
### 3. 派生烘焙接入「公式映射」
|
||||
|
||||
`BuildDerivedSignal` / `CloneSignal` 的烘值判定漏了 `HasFormula()`
|
||||
=> 带公式的信号派生/克隆后公式被静默丢弃。统一为
|
||||
`HasFormula() || gain != 1 || absolute || mirrorVertical || offsetY != 0`
|
||||
(与 `ApplyDrawTransform` 的结算顺序同源)。`BuildResampledSignal` 本就全程走 `ApplyDrawTransform`。
|
||||
|
||||
### 4. 信号右键菜单:克隆拆成两个语义(均不烘焙采样值)
|
||||
|
||||
| 菜单项 | 采样值 | 样式 |
|
||||
| --- | --- | --- |
|
||||
| 「克隆该信号」 | 原始数据 | 外观 + 仅时间轴偏移,其余调整复位 |
|
||||
| 「克隆该信号(带调整)」 | 原始数据 | 外观 + 整套调整(公式/系数/Y 偏移/时间轴偏移/水平与垂直镜像/绝对值) |
|
||||
|
||||
新增 `Application::CloneSignalAdjusted`、`CMD_SIGNAL_CLONE_ADJUSTED = 6`、
|
||||
`kMessageCloneSignalAdjusted = WM_APP + 6`。两者都保留时间轴偏移
|
||||
(共用同一条文件时间轴,烘进采样值会在越界段写 NaN)。
|
||||
|
||||
### 5. 流程教训(用户两次严厉批评,必须遵守)
|
||||
|
||||
1. 不要擅自改 UI:用户只要求修数值,却新增了两行统计行 —— 已被明确否决并回退。
|
||||
面板保持四行(最大间隔 / 最小间隔 / 重采样间隔 / 重采样频率)。
|
||||
2. 不要无脑跑探针:大段 `Initialize()` + `Render()` 的探针既慢又容易崩,
|
||||
崩了会经 `MemProbe::OnTerminate()` 弹模态「内存不足」框
|
||||
(`-fno-exceptions` 下任何分配失败/断言都会走它),用户会以为程序真没内存。
|
||||
=> 优先写只链接 `Utf8Util.o` 的纯值打印程序(本轮只用了 60 行、秒级验证、零弹框风险)。
|
||||
3. 先打印数值、再改代码:本轮定位只用了"打印 6 个数字",
|
||||
比前一轮 300 行 UI 探针快得多也准得多。
|
||||
4. `.preflight` 已被用户删除,不要再重建测试框架目录。
|
||||
5. 写中文记忆文件必须走 Python 或编辑工具,不能经 PowerShell 的 `Get-Content`/`WriteAllText`
|
||||
往返(会按 GBK 解码再编码,中文全毁)。
|
||||
|
||||
### 6. 本轮验证(纯值打印,无窗口)
|
||||
|
||||
`make release TARGET_FILE_NAME=TargetCheck` 零 warning。格式化输出实测:
|
||||
|
||||
```
|
||||
min interval 0.003696631s -> 3ms 696.631us
|
||||
frequency 1/0.003696631 -> 270.517 Hz
|
||||
max interval 0.016182614s -> 16ms 182.614us
|
||||
frequency 1/0.016182614 -> 61.795 Hz
|
||||
5.06s -> 5s 60ms (整数次级单位不加小数点)
|
||||
1h 1min 2s freq -> 0.000273075 Hz (超低频不显示成 0)
|
||||
5.123ps -> 5.123 ps
|
||||
```
|
||||
@@ -0,0 +1,34 @@
|
||||
fix(resample): 重采样间隔/频率按真实值显示(次级单位与频率保留三位小数)
|
||||
|
||||
问题(用户实测 MF4-Record.MF4 / m_MOT_Current_DCom[0]):
|
||||
- 最小数据间隔真实值 0.003696631s,却被显示成 3ms 700us(us 部分被向上截取);
|
||||
- 对应频率真实值 270.516586589Hz,却被显示成 270.27Hz;
|
||||
- 最大数据间隔 0.016182614s 显示成 16ms 200us、频率显示成 61.7284Hz,同样不准。
|
||||
|
||||
根因(两个,同一个坑):
|
||||
1. RoundIntervalForDisplay() 把间隔先舍入到 3 位有效数字(0.003696631 -> 0.0037),
|
||||
次级单位因此变成 700us,真实尾数被抹掉;
|
||||
2. 频率用舍入后的 3.70ms 换算得 270.27Hz,而输入框用原值得 270.517Hz,两处不一致。
|
||||
|
||||
修法(一律使用真实值,舍入只发生在输出文本的最后一步):
|
||||
- Utf8Util 新增 FormatDecimalFixed(value, decimals=3):固定小数位、保留尾随 0、
|
||||
绝不出现科学计数法;
|
||||
- Utf8Util 新增 FormatSubUnit(value, maxDecimals):带小数才补到 3 位,整数不加小数点
|
||||
(5.06s -> 5s 60ms,而不是 5s 60.000ms);
|
||||
- FormatDurationCompound 的两处次级单位(s+ms、ms/us/ns)改用它;
|
||||
- FormatFrequency 改为 FormatDecimalFixed(scaled, 3);若保留 3 位后变成 0.000
|
||||
(超低频,如 1/3662s ≈ 0.27mHz)则退回 FormatDecimal,避免有意义的值显示成 0;
|
||||
- PlotViewInternal.hpp 删除 RoundIntervalForDisplay,FormatIntervalText 直接用真实值;
|
||||
- PlotViewPaint.cpp 的统计行尾缀与频率输入框都用真实 interval 换算。
|
||||
|
||||
修复后的显示:
|
||||
min interval 0.003696631s -> 3ms 696.631us
|
||||
frequency 1/0.003696631 -> 270.517 Hz
|
||||
max interval 0.016182614s -> 16ms 182.614us
|
||||
frequency 1/0.016182614 -> 61.795 Hz
|
||||
|
||||
同时撤销了此前两次仅修改 agent_mem.md 的提交(47b1213 / caa035a,内容为乱码且无有效信息),
|
||||
并按正确编码重新整理本轮记忆章节。
|
||||
|
||||
面板仍为四行(最大间隔 / 最小间隔 / 重采样间隔 / 重采样频率),未改动任何 UI 布局。
|
||||
make release 零 warning。
|
||||
@@ -98,6 +98,22 @@ namespace jdi
|
||||
*/
|
||||
std::wstring FormatDecimal(double value, int significantDigits = 6);
|
||||
|
||||
/**
|
||||
* @brief 以**固定小数位数**格式化数值(绝不出现科学计数法,保留尾随 0)。
|
||||
*
|
||||
* 与 `FormatDecimal` 的区别:后者按「有效数字位数」自适应小数位并去掉尾随 0;
|
||||
* 本函数按**小数位数**精确输出,**保留尾随 0**(用户 2026-11-03 要求:
|
||||
* 「次级单位保留三位小数」,例如 `696.631us`、`700.000us`)。
|
||||
*
|
||||
* 例:`FormatDecimalFixed(0.003696631 * 1e6, 3)` → `"3696.631"`;
|
||||
* `FormatDecimalFixed(700.0, 3)` → `"700.000"`。
|
||||
*
|
||||
* @param [IN] value 待格式化数值。
|
||||
* @param [IN] decimals 小数位数(0–12,默认 3)。
|
||||
* @return 定点小数文本;NaN/Inf 返回 "NaN"/"+Inf"/"-Inf"。
|
||||
*/
|
||||
std::wstring FormatDecimalFixed(double value, int decimals = 3);
|
||||
|
||||
/**
|
||||
* @brief 依据坐标轴刻度步长格式化刻度标签。
|
||||
* @param [IN] value 刻度值。
|
||||
|
||||
@@ -31,6 +31,48 @@ namespace jdi
|
||||
double scale = 1.0; /**< 1 个该单位等于多少秒。 */
|
||||
const wchar_t* name = L"s"; /**< 单位名称。 */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 复合单位时间文本里**次级单位最多保留的小数位数**。
|
||||
*
|
||||
* 用户 2026-11-03 明确要求:`0.003696631s` 必须显示为 `3ms 696.631us`
|
||||
* (次级单位 ms/us/ns 保留三位小数,能看到真实尾数),
|
||||
* 而不是四舍五入到 `3ms 700us`(旧实现舍入到 3 位**有效数字**,把尾数抹掉了)。
|
||||
*
|
||||
* ⚠️ 这是**上限**,不是强制的固定位数:整数次级单位仍写成整数
|
||||
* (`5.06s` → `5s 60ms`,而不是 `5s 60.000ms`),只有真的带小数时才补零到 3 位。
|
||||
*/
|
||||
constexpr int kDurationSubUnitDecimals = 3;
|
||||
|
||||
/**
|
||||
* @brief 频率文本保留的小数位数(用户 2026-11-03 要求)。
|
||||
*
|
||||
* `270.516586...Hz` 必须显示为 `270.517 Hz`(真实值保留三位小数),
|
||||
* 而不是"先把间隔舍入到 3 位有效数字再换算"得到的 `270.27 Hz`。
|
||||
*/
|
||||
constexpr int kFrequencyDecimals = 3;
|
||||
|
||||
/**
|
||||
* @brief 格式化次级单位数值:带小数则保留 `maxDecimals` 位,整数则原样输出。
|
||||
*
|
||||
* `696.6314` → `696.631`;`60.0` → `60`;`0.0158` → `0.016`。
|
||||
* 这样既满足"次级单位保留三位小数"(看得到真实尾数),
|
||||
* 又不会把整数次级单位写成 `60.000` 这种噪音。
|
||||
*/
|
||||
inline std::wstring FormatSubUnit(double value, int maxDecimals)
|
||||
{
|
||||
if (!std::isfinite(value))
|
||||
{
|
||||
return L"—";
|
||||
}
|
||||
const double rounded = std::round(value * std::pow(10.0, maxDecimals)) /
|
||||
std::pow(10.0, maxDecimals);
|
||||
if (rounded == std::floor(rounded))
|
||||
{
|
||||
return FormatDecimal(rounded); // 整数:不加小数点
|
||||
}
|
||||
return FormatDecimalFixed(rounded, maxDecimals);
|
||||
}
|
||||
}
|
||||
|
||||
std::wstring Utf8ToWide(const char* text, int length)
|
||||
@@ -412,8 +454,38 @@ namespace jdi
|
||||
return text;
|
||||
}
|
||||
|
||||
std::wstring FormatDurationCompound(double seconds)
|
||||
std::wstring FormatDecimalFixed(double value, int decimals)
|
||||
{
|
||||
if (std::isnan(value))
|
||||
{
|
||||
return L"NaN";
|
||||
}
|
||||
if (std::isinf(value))
|
||||
{
|
||||
return (value > 0.0) ? L"+Inf" : L"-Inf";
|
||||
}
|
||||
if (decimals < 0)
|
||||
{
|
||||
decimals = 0;
|
||||
}
|
||||
if (decimals > 12)
|
||||
{
|
||||
decimals = 12;
|
||||
}
|
||||
// ⚠️ 固定小数位数 + **保留尾随 0**(与 `FormatDecimal` 的"去尾随 0"语义相反):
|
||||
// 用户 2026-11-03 明确要求「次级单位保留三位小数」,例如 696.631us / 700.000us。
|
||||
wchar_t buffer[160];
|
||||
swprintf(buffer, sizeof(buffer) / sizeof(buffer[0]), L"%.*f", decimals, value);
|
||||
std::wstring text(buffer);
|
||||
if (text == L"-0" || (text.size() > 1 && text[0] == L'-' &&
|
||||
text.find_first_not_of(L"0-.") == std::wstring::npos))
|
||||
{
|
||||
text.erase(0, 1); // -0.000 → 0.000
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
std::wstring FormatDurationCompound(double seconds) {
|
||||
if (!std::isfinite(seconds))
|
||||
{
|
||||
return L"—";
|
||||
@@ -477,7 +549,9 @@ namespace jdi
|
||||
if (remainderMs >= 1.0e-6)
|
||||
{
|
||||
text += L" ";
|
||||
text += FormatDecimal(remainderMs);
|
||||
// ⚠️ 次级单位(ms)**最多保留三位小数**(用户 2026-11-03 要求:`3ms 696.631us`
|
||||
// 这类显示要能看到真实尾数);整数则不加小数点(`5.06s` → `5s 60ms`)。
|
||||
text += FormatSubUnit(remainderMs, kDurationSubUnitDecimals);
|
||||
text += L"ms";
|
||||
}
|
||||
return text;
|
||||
@@ -522,7 +596,9 @@ namespace jdi
|
||||
if (inNext >= 1.0e-6)
|
||||
{
|
||||
text += L" ";
|
||||
text += FormatDecimal(inNext);
|
||||
// ⚠️ 次级单位**最多保留三位小数**(用户 2026-11-03 要求):
|
||||
// `0.003696631s` → 顶级 `3ms` + 次级 `696.631us`(而不是 `3ms 700us`)。
|
||||
text += FormatSubUnit(inNext, kDurationSubUnitDecimals);
|
||||
text += subUnits[index + 1].name;
|
||||
}
|
||||
return text;
|
||||
@@ -563,7 +639,16 @@ namespace jdi
|
||||
}
|
||||
// ⚠️ 频率同样不得出现科学计数法(用户 2026-11-03 要求):
|
||||
// `FormatValue` 的 `%.6g` 在 1e-5 Hz 这类量级会输出 “1e-06”,改为十进制形式。
|
||||
return FormatDecimal(scaled) + L" " + unit;
|
||||
// ⚠️ 并且**基于真实值保留三位小数**(用户 2026-11-03 要求:`270.517 Hz` 而不是
|
||||
// `270.27 Hz`)—— 旧实现把间隔先舍入到 3 位有效数字再换算,把真实尾数抹掉了。
|
||||
// ⚠️ 但若保留 3 位后整串变成 `0.000`(超低频,例如 1/3662s ≈ 0.27mHz),
|
||||
// 则退回按「有效数字」显示,避免把有意义的数值显示成 0(用户 2026-11-03)。
|
||||
const std::wstring fixedText = FormatDecimalFixed(scaled, kFrequencyDecimals);
|
||||
if (std::fabs(scaled) > 0.0 && std::fabs(scaled) < 0.5 * std::pow(10.0, -kFrequencyDecimals))
|
||||
{
|
||||
return FormatDecimal(scaled) + L" " + unit;
|
||||
}
|
||||
return fixedText + L" " + unit;
|
||||
}
|
||||
|
||||
std::wstring FormatByteSize(unsigned long long bytes)
|
||||
|
||||
Reference in new issue
Block a user