从 C# 中的双精度数中提取“真实”科学指数

问题描述 投票:0回答:1

在 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)));
    }
}
c# floating-point ieee-754
1个回答
0
投票

一种方法,让您的图书馆的

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 - 现实生活中的电话。

© www.soinside.com 2019 - 2024. All rights reserved.