在 C# 中,IEEE-754 数字(在本例中为
System.Double
)可以用 Base 10 科学计数法声明;例如:1.234e+9 (1234000000),但由于浮点数存储方式的限制,这些值的 Base 2 浮点表示通常只是一个近似值。尊重,考虑下表:
科学计数法 | IEEE-754 近似 |
---|---|
1e-1 | 0.10000000000000001 |
1e-2 | 0.01 |
1e-3 | 0.001 |
1e-4 | 0.0001 |
1e-5 | 1.0000000000000001E-05 |
1e-6 | 9.9999999999999995E-07 |
1e-7 | 9.9999999999999995E-08 |
1e-8 | 1E-8 |
1e-9 | 1.0000000000000001E-09 |
1e-10 | 1E-10 |
忽略 IEEE-754 近似值的格式都不同这一事实。这些值都是直接从调试器中获取的。以不同的形式查看它们很有用,因为它有助于解释问题。
我想做的是从数字中提取指数。到目前为止我有这个功能:
private static int GetScientificExponent(double value)
{
return (int)double.Floor(double.Log10(double.Abs(value)));
}
结合上面的结果表,我们可以确定每个案例的科学指数:
科学计数法 | IEEE-754 近似 | 科学指数 |
---|---|---|
1e-1 | 0.10000000000000001 | -1 |
1e-2 | 0.01 | -2 |
1e-3 | 0.001 | -3 |
1e-4 | 0.0001 | -4 |
1e-5 | 1.0000000000000001E-05 | -5 |
1e-6 | 9.9999999999999995E-07 | -6 |
1e-7 | 9.9999999999999995E-08 | -7 |
1e-8 | 1E-8 | -8 |
1e-9 | 1.0000000000000001E-09 | -9 |
1e-10 | 1E-10 | -10 |
科学指数与 Scientific Notation 对于每种情况都匹配,但它们不匹配 IEEE-754 近似值 对于每种情况,正如我在此表中所说明的那样。注意异常值,以粗体突出显示:
科学计数法 | IEEE-754 表示 | 科学指数 | 注意事项 |
---|---|---|---|
1e-1 | 0.10000000000000001 | -1 | 好 |
1e-2 | 0.01 | -2 | 好 |
1e-3 | 0.001 | -3 | 好 |
1e-4 | 0.0001 | -4 | 好 |
1e-5 | 1.0000000000000001E-05 | -5 | 好 |
1e-6 | 9.9999999999999995E-07 | -6 | E-6 与 E-7 |
1e-7 | 9.9999999999999995E-08 | -7 | E-7 与 E-8 |
1e-8 | 1E-8 | -8 | 好 |
1e-9 | 1.0000000000000001E-09 | -9 | 好 |
1e-10 | 1E-10 | -10 | 好 |
考虑到如果您以 actual 形式表示这些数字,而不是依赖 .NET 的底层舍入机制(我相信是 Dragon4 左右),那么这似乎是合理的,那么您最终会得到:
科学计数法 | 10 进制表示 | IEEE-754 近似 |
---|---|---|
1e-6 | 0.000001 | 0.000000999999999999999954748111825886258685613938723690807819366455078125 |
值得注意的是,在 Base 10 中,
1
位于小数点后的第 6 位,而在 IEEE-754 近似值中,9
位于第 7 位,因此 -6 与 -7...这使得感觉。
鉴于我的
GetScientificExponent
函数,是否可以从近似中获得指数?例如; 1e-2
应该像现在一样返回 -2
,但是 1e-6
应该返回 -7
因为那是近似值的指数?
为了完整性,这是我用来测试的代码:
using static System.Double;
internal static class Program
{
private static void Main(string[] args)
{
double[] values = { 3e-1, 1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6, 1e-7, 1e-8, 1e-9, 1e-10 };
foreach (double value in values)
{
Console.WriteLine($"|{value:F10}|{GetScientificExponent(value)}|");
}
}
private static int GetScientificExponent(double value)
{
return (int)Floor(Log10(Abs(value)));
}
}
一种方法,让您的图书馆的
double
到 text 例程来执行所需的更高精度数学。使用用户double
数学是不够的/不方便。
“技巧”是打印到足够高的精度,使得接近 10 的幂的值在四舍五入时不会导致指数发生变化,然后对指数进行简单解析。
下面是C代码来说明。 (稍后我会尝试使用 C++ 代码)。
特别是,在
DBL_DECIMAL_DIG
的情况下,打印到 17 (1e-79
) 位有效数字是不够的。相反,代码打印 DBL_DECIMAL_DIG + 1
有效的小数位。在某种程度上,使用更多数字几乎没有什么损失。我推荐DBL_DECIMAL_DIG + 3
。除此之外,图书馆不需要打印正确的数字并保持 IEEE-754 合规性。
示例代码
#include <float.h>
#include <limits.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int GetScientificExponent(double value) {
return (int) floor(log10(fabs(value)));
}
// 0.0 and NAN return INT_MIN
int GetScientificExponent_alt1(double value) {
#define PREC ((DBL_DECIMAL_DIG - 1) + 1 /* or + 3 */)
// - d . ddddd e - eee \0
char buf[1 + 1 + 1 + PREC + 1 + 1 + 5 + 1];
sprintf(buf, "%.*e", PREC, value);
printf("<%-30s>", buf);
const char *e = strchr(buf, 'e');
if (e) {
return atoi(e + 1);
}
return INT_MIN;
}
#include <stdio.h>
#include <stdlib.h>
void test1(double x) {
printf("%30.*e %4d %4d\n", //
DBL_DECIMAL_DIG * 3 / 2, x,
GetScientificExponent_alt1(x), GetScientificExponent(x));
}
void test3(int power10) {
double x = pow(10.0, power10);
//test1(nextafter(x, +INFINITY));
test1(x);
//test1(nextafter(x, -INFINITY));
//puts("");
}
int main() {
for (int power10 = 0; power10 <= DBL_DECIMAL_DIG - DBL_MIN_10_EXP; power10++) {
test3(-power10);
}
}
输出
<1.00000000000000000e+00 >1.0000000000000000000000000e+00 0 0
<1.00000000000000006e-01 >1.0000000000000000555111512e-01 -1 -1
<1.00000000000000002e-02 >1.0000000000000000208166817e-02 -2 -2
<1.00000000000000002e-03 >1.0000000000000000208166817e-03 -3 -3
<1.00000000000000005e-04 >1.0000000000000000479217360e-04 -4 -4
<1.00000000000000008e-05 >1.0000000000000000818030539e-05 -5 -5
<9.99999999999999955e-07 >9.9999999999999995474811183e-07 -7 -6
<9.99999999999999955e-08 >9.9999999999999995474811183e-08 -8 -7
<1.00000000000000002e-08 >1.0000000000000000209225608e-08 -8 -8
<1.00000000000000006e-09 >1.0000000000000000622815915e-09 -9 -9
<1.00000000000000004e-10 >1.0000000000000000364321973e-10 -10 -10
<9.99999999999999939e-12 >9.9999999999999993949696928e-12 -12 -11
<9.99999999999999980e-13 >9.9999999999999997988664763e-13 -13 -12
<1.00000000000000003e-13 >1.0000000000000000303737456e-13 -13 -13
<9.99999999999999999e-15 >9.9999999999999999881930935e-15 -15 -14
<1.00000000000000008e-15 >1.0000000000000000777053999e-15 -15 -15
<9.99999999999999979e-17 >9.9999999999999997909778672e-17 -17 -16
<1.00000000000000007e-17 >1.0000000000000000715424241e-17 -17 -17
<1.00000000000000007e-18 >1.0000000000000000715424241e-18 -18 -18
<9.99999999999999975e-20 >9.9999999999999997524592684e-20 -20 -19
<9.99999999999999945e-21 >9.9999999999999994515327145e-21 -21 -20
<9.99999999999999908e-22 >9.9999999999999990753745223e-22 -22 -21
<1.00000000000000005e-22 >1.0000000000000000485967743e-22 -22 -22
<9.99999999999999960e-24 >9.9999999999999996043469801e-24 -24 -23
<9.99999999999999924e-25 >9.9999999999999992370049955e-25 -25 -24
<1.00000000000000004e-25 >1.0000000000000000384948697e-25 -25 -25
<1.00000000000000004e-26 >1.0000000000000000384948697e-26 -26 -26
<1.00000000000000004e-27 >1.0000000000000000384948697e-27 -27 -27
<9.99999999999999971e-29 >9.9999999999999997123254346e-29 -29 -28
<9.99999999999999943e-30 >9.9999999999999994320657418e-30 -30 -29
<1.00000000000000008e-30 >1.0000000000000000833364206e-30 -30 -30
...
<9.99999999999999958e-76 >9.9999999999999995765001370e-76 -76 -75
<9.99999999999999927e-77 >9.9999999999999992696817954e-77 -77 -76
<9.99999999999999927e-78 >9.9999999999999992696817954e-78 -78 -77
<9.99999999999999999e-79 >9.9999999999999999887872835e-79 -79 -78
<9.99999999999999999e-80 >9.9999999999999999887872835e-80 -80 -79
<9.99999999999999961e-81 >9.9999999999999996142531751e-81 -81 -80
<9.99999999999999961e-82 >9.9999999999999996142531751e-82 -82 -81
<9.99999999999999961e-83 >9.9999999999999996142531751e-83 -83 -82
...
<9.99999983659714433e-317 >9.9999998365971443346061921e-317 -317 -317
<1.00000023069253735e-317 >1.0000002306925373540838913e-317 -317 -317
<9.99998748495599830e-319 >9.9999874849559983034425877e-319 -319 -319
<9.99988867182683005e-320 >9.9998886718268300541337524e-320 -320 -320
<9.99988867182683005e-321 >9.9998886718268300541337524e-321 -321 -321
<9.98012604599318019e-322 >9.9801260459931801923666896e-322 -322 -322
<9.88131291682493088e-323 >9.8813129168249308835313759e-323 -323 -323
<9.88131291682493088e-324 >9.8813129168249308835313759e-324 -324 -324
<0.00000000000000000e+00 >0.0000000000000000000000000e+00 0 -2147483648
我稍后再回顾这个。 GTG - 现实生活中的电话。