代码之家  ›  专栏  ›  技术社区  ›  JW.

这个数字的小数位数是否超过D位?

  •  3
  • JW.  · 技术社区  · 14 年前

    给定一个数字N,

    结果是否包含超过D2个小数位,不包括尾随的零?

    例如,假设N是.01001,D1是4,D2是2。问题是.0100是否包含超过2个小数位,不包括尾随的0?答案是“不”。但如果N是.00101,答案是“是”。

    考虑到浮点数的限制,我正在寻找一种使用标准C库函数来实现这一点的有效方法。

    (这不是一个家庭作业问题——这是一个专业程序员的结果,他在学生时代没有做过这些家庭作业问题。)

    5 回复  |  直到 14 年前
        1
  •  4
  •   Community CDub    7 年前

    使用标准库进行此操作的唯一简单方法是使用 snprintf (或者只是 sprintf )用正确的格式,然后自己数零。正确计算(二进制)浮点数的十进制表示法是一项非常困难的任务,您不想自己去做,而且您几乎没有机会编写比标准库更快的正确版本。

    我希望我是对的,这是未经考验的:

    double n; /* the number N */
    int d1, d2; /* the parameters d1 and d2 */
    char s[MAXLEN], *z;
    snprintf(s, sizeof s, "%.*f", d1, n);
    for (z=s+strlen(s)-1; *z==0; z--);
    if (strlen(++z)<d1-d2) puts("yes");
    else puts("no");
    

    编辑: 正如桑基所说, 可能对打印精度有限制。实际上,只要实现文档是这样的,C标准允许几乎任何浮点运算都会毫无理由地给出错误的结果,但是IEEE行为是受鼓励的,POSIX还要求正确地四舍五入结果 DECIMAL_DIG 但允许实现在打印足够多的位置以唯一地确定实际浮点值之后打印无意义(例如全零)。长话短说,如果 d1 snprintf公司 -基于知识的方法可能不会给出正确的答案。(实际上,他们会在GNU系统上给出正确答案,而在Microsoft系统上给出错误答案。)

    如果您关心这个缺点,并希望任何值的结果都是正确的 implement exact float-to-decimal code yourself . 涉及将浮点值重复乘以10的循环将不能满足此要求。

    查看OP的“预期用途”,使用 好像没什么问题。如果您想打印要开始的值,并且只是想决定要使用多少个小数位,只需将它打印成一个字符串,然后在显示它之前去掉后面的零。事实上 %g printf 格式说明符甚至可能已经做了OP想要做的事情。。。

        2
  •  1
  •   Philip Starhill    14 年前

    你可以先将你的数字乘以10^D1,然后四舍五入到最接近的整数,然后检查它是否可以被10^D2整除。有一些取整函数可供选择,它们将向上/向下/远离零/等等,所以一定要检查您使用的是您想要的那个。

    下面的函数是这样编写的,它很小并且是独立的,但是生产实现应该使用一个查找表来查找10的幂,如注释行中所示。这将产生更快的性能,并避免在浮点乘积中累积错误。

    int f (double N, unsigned int D1, unsigned int D2)
    {
       int i, n_mult_round, ten_d2;
       /* instead of the loop below, a real implementation should use */
       /* N *= dbl_powers_of_ten[D1]; */
       /* where dbl_powers_of_ten is a double array containing powers of ten */
       for (i = 0; i < D1; ++i) {
          N *= 10.0;
       }
       n_mult_round = (int) round (N);
       /* instead of the loop below, a real implementation should use */
       /* ten_d2 = int_powers_of_ten[D2]; */
       /* where int_powers_of_ten is an int array containing powers of ten */
       ten_d2 = 1;
       for (i = 0; i < D2; ++i) {
          ten_d2 *= 10;
       }
       if ( n_mult_round % ten_d2 == 0 ) {
          return ( 1 );
       }
       else {
          return ( 0 );
       }
    }
    
        3
  •  0
  •   egrunin    14 年前
    1. 将其格式化为最大精度的字符串。
    2. 从右边删除零,直到达到最小精度。

    这里有一个 开始实施:

    char buff[20]; /* make as big as necessary */
    int maxPrecision = 4;
    int minPrecision = 2;
    
    sprintf(buff, "%.*f", maxPrecision, myFloat);
    
    char *p = buff + (strlen(buff) - 1);
    char *punct = strchr(buff, ".");
    
    /* remove trailing zeros until we reach minPrecision */
    while (*p == '0' && p > (punct + minPrecision))
        *p-- = 0; 
    
        4
  •  0
  •   Jason Goemaat    14 年前

    int needsD1Decimals(float N, int D1, int D2) {
        float pow1 = pow(0.1f, D1); // 0.0001
        float pow2 = pow(0.1f, D2); // 0.01
        float mod1 = fmod(N, pow1); // 0.00001
        float mod2 = fmod(N, pow2); // 0.00001
        if (fabs(mod1 - mod2) > (pow1 / 2)) { // > 0.00005 to handle errors
            return 1;
        }
        return 0;
    }
    

    如果您只是想打印出正确的答案,那么只打印出小数点最多的答案并截断结尾的零可能会更快:

    void printNumber(float N, int D1, int D2) {
        char format[256];
        char result[256];
        sprintf(format, "%%.%df", D1);
        sprintf(result, format, N);
        char *end = result + strlen(result) - 1;
        int zeros = 0;
        while (end > result && end[0] == '0' && zeros < (D1 - D2))
        {
            zeros++;
            end--;
        }
        if (zeros >= (D1 - D2))
        {
            end[1] = '\0';
        }
        puts(result);
    }
    
    void doNumber(float N, int D1, int D2) {
        printf("%f, %d, %d: ", N, D1, D2);
        printNumber(N, D1, D2);
        printf("\n");
    }
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        doNumber(0.01001f, 4, 2);
        doNumber(0.010101f, 4, 2);
        doNumber(0.011001f, 4, 2);
        doNumber(5000.0f, 4, 2);
        return 0;
    }
    
        5
  •  -1
  •   ssianky    14 年前
    int foo(float number, int d1, int d2)
    {
        assert((number > 0.0f) && (number < 1.0f) && (d1 <= FLT_DIG) && (d2 < d1) && (d2 > 0));
    
        int count = d1;
        int n = (int)(number * pow(10.0f, d1));
    
        if (n == 0) return 0;
    
        while ((n % 10) == 0)
        {
            n /= 10;
            count--;
        }
    
        return (count > d2) ? 1 : 0;
    }