libxr  1.0
Want to be the best embedded framework
Loading...
Searching...
No Matches
writer_float_math.hpp
1#pragma once
2
13template <typename Float>
15{
16 int exponent = 0;
17 Float scale = 1;
18};
19
25template <typename Float>
27{
28 Float digits =
29 0;
30 Float scale = 1;
32 int exponent = 0;
33};
34
41template <typename Float>
42Float Writer::Power10(int exponent)
43{
44 Float result = 1;
45 Float base = 10;
46 unsigned int remaining = static_cast<unsigned int>(exponent < 0 ? -exponent : exponent);
47
48 while (remaining != 0)
49 {
50 if ((remaining & 1U) != 0U)
51 {
52 if (exponent < 0)
53 {
54 result /= base;
55 }
56 else
57 {
58 result *= base;
59 }
60 }
61
62 remaining >>= 1U;
63 if (remaining != 0U)
64 {
65 base *= base;
66 }
67 }
68
69 return result;
70}
71
81template <typename Float>
82Float Writer::RoundDecimal(Float value, uint8_t precision)
83{
84 Float scale = Power10<Float>(static_cast<int>(precision));
85 Float scaled = value * scale;
86 if (!std::isfinite(scaled))
87 {
88 // Scaling overflowed: propagate as infinity so callers' isfinite() guard
89 // rejects the result cleanly instead of silently returning the original
90 // value and later producing OUT_OF_RANGE. Input is expected to be a
91 // non-negative magnitude, so plain infinity is sufficient.
92 return std::numeric_limits<Float>::infinity();
93 }
94
95 return std::nearbyint(scaled) / scale;
96}
97
109template <typename Float>
110Writer::ScientificDigits<Float> Writer::RoundScientificDigits(Float value,
111 uint8_t precision)
112{
113 ScientificDigits<Float> result{};
114 auto normalized = NormalizeDecimal(value);
115 result.exponent = (value == 0) ? 0 : normalized.exponent;
116 Float decimal_scale = Power10<Float>(result.exponent);
117 Float mantissa = (value == 0) ? 0 : value / decimal_scale;
118 result.scale = Power10<Float>(static_cast<int>(precision));
119 result.digits = std::nearbyint(mantissa * result.scale);
120 if (result.digits >= static_cast<Float>(10) * result.scale)
121 {
122 result.digits /= 10;
123 ++result.exponent;
124 }
125
126 return result;
127}
128
137template <typename Float>
138Writer::DecimalScale<Float> Writer::NormalizeDecimal(Float value)
139{
140 DecimalScale<Float> normalized{};
141 if (value == 0)
142 {
143 return normalized;
144 }
145
146 int binary_exponent = 0;
147 std::frexp(value, &binary_exponent);
148 constexpr Float log10_of_2 = static_cast<Float>(0.30102999566398119521373889472449L);
149 normalized.exponent =
150 static_cast<int>(static_cast<Float>(binary_exponent - 1) * log10_of_2);
151 normalized.scale = Power10<Float>(normalized.exponent);
152
153 Float scaled = value / normalized.scale;
154 while (scaled < 1)
155 {
156 normalized.scale /= 10;
157 --normalized.exponent;
158 scaled *= 10;
159 }
160 while (scaled >= 10)
161 {
162 normalized.scale *= 10;
163 ++normalized.exponent;
164 scaled /= 10;
165 }
166
167 return normalized;
168}
169
179template <typename Float>
180uint8_t Writer::ExtractDigit(Float& value, Float scale)
181{
182 Float scaled = value / scale;
183 // Bias to correct floating-point rounding when the true digit value is an
184 // integer but division leaves it just below (e.g. 1.9999999... instead of 2).
185 // 1e-12 is effective for double (epsilon ~2.2e-16) but effectively zero for
186 // float (epsilon ~1.2e-7). For float, digit extraction may have up to 1-ULP
187 // error in the last digit; this is a known limitation of the approach.
188 // Do NOT increase this bias to fix float: values like 1.999999f have a
189 // legitimate float representation ~9.5e-7 below 2.0, so any bias large
190 // enough to "fix" float rounding would also cause false carry on such values.
191 auto digit = static_cast<int>(scaled + static_cast<Float>(1e-12L));
192 if (digit < 0)
193 {
194 digit = 0;
195 }
196 else if (digit > 9)
197 {
198 digit = 9;
199 }
200
201 value -= static_cast<Float>(digit) * scale;
202 // Zero-clamp: clear tiny negative residuals that are FP rounding artifacts.
203 // 1e-9 is effective for double but coarse for long double; for float it is
204 // large enough to clear genuine residuals without clamping real fractional
205 // remainders (float residuals after digit extraction are < epsilon * scale).
206 Float epsilon = scale * static_cast<Float>(1e-9L);
207 if (value < 0 && value > -epsilon)
208 {
209 value = 0;
210 }
211
212 return static_cast<uint8_t>(digit);
213}
通用浮点文本格式化器使用的数学辅助函数。 / Math helpers used by the generic float text formatter.
Float scale
10 ^ exponent / 10 的 exponent 次幂
int exponent
decimal exponent / 十进制指数
科学计数法归一化后的尾数数字、缩放因子与十进制指数 / Rounded mantissa digits, scale factor, and decimal exponent after scienti...
int exponent
decimal exponent / 十进制指数
Float digits
rounded mantissa scaled to integer digits / 舍入后按整数位缩放的尾数