确定小数点右边的非零数字的数量

时间:2009-09-09 23:11:36

标签: c++

我想计算小数点右边的非零和非重复数(即1.999999999)。例如:

x.xx = 2
x.xxx = 3
x.xxx000 = 3

我可以通过将数字转换为字符串来实现这一点,但我想知道是否有更快的方式使用数学。有什么想法吗?

感谢。

编辑: 许多人似乎认为这是一个愚蠢的差事,因为数字在计算机中的表现方式。但是,请允许我解释问题的背景。假设您要编写一个方法来随机生成浮点数。您生成的数字必须具有一定的精度,因此您必须将随机生成的数字舍入到指定的精度。例如,如果精度为2,则随机数不能为0.012,必须舍入为0.01。问题是你没有提供精度,而是给你增量,在上述情况下将是0.01。给定0.01或0.002或任何其他小于1的增量,您必须找到精度。

编辑:删除了我对“有效数字”一词的错误用法。

8 个答案:

答案 0 :(得分:6)

significant digits的数量不是通过查看数字来计算的。您可以根据用于计算正在查看的数字的其他数字中的有效位数来计算,或者根据用于执行测量的仪器的精度来计算。

不显示或使用不重要的数字,因此您的数字x.xxx000在小数点右侧有六位数这一事实意味着所有六位数都是重要的。数字2.3与数字2.300不同。

当您有一个像2300这样的数字时,是否将零计数为重要的问题会发挥作用,其中数字是10的整数倍。是否有两个有效数字,或四个?用科学记数法写出来确定:2.3×10 3 或2.300×10 3

大多数语言的本机数字类型(包括C ++)都不处理这个有效数字的概念。一般来说,他们甚至根本不处理数字;他们处理比特。如果您的实现的float类型是32位,那么所有32位都被视为重要的。

答案 1 :(得分:2)

一种技术将数字乘以10,直到那个地方的数字为0(你可以用%来辨别):

float x = /*...*/;
int   count = 0;

while (true)
{
    x *= 10;

    if (static_cast<long>(x) % 10 == 0)
        break;

    ++count;
}

// at this point count has the number of digits after the decimal point.

这是我写的东西的牛肉:

  • 它永远不会终止(理论上)。
  • 上述算法中存在大量的乘法和模数,因此非常昂贵。
  • 每次迭代都会增加遇到浮点舍入错误的几率。
  • 对于x.x0x,这将失败,其中x表示任意数量的数字。 (感谢DanielBrückner)

简而言之,我倾向于进行分类搜索。

答案 2 :(得分:1)

廉价的方法是:

  1. 将数字转换为字符串
  2. 通过评估字符a n =字符串长度 - 1到0来找到字符串右边的第一个非零数字。
  3. 取步骤#2中找到的数字,并从中减去该字符串中小数点的索引。

答案 3 :(得分:1)

找到小数点。将小数移动到第一个非零数字的右侧。计算从小数点到数字末尾的位数。这将是指数。删除所有零

答案 4 :(得分:0)

答案 5 :(得分:0)

在C#中有一个函数可以舍入到给定的十进制数字。您可以增加十进制数字的数量,直到数字和舍入数字之间的差异小到固定阈值 - 可能是epsilon的两倍或三倍(最小可能的正值 - 对于双倍,这是4.94065645841247e-324)。

Double epsilon = 2 * Double.Epsilon;
Int32 significantDigits = 0;
while (Math.Abs(number - Math.Round(number, significantDigits)) > epsilon)
{
    significantDigits++;
}

这只是一个未经测试的建议,这种处理浮点数的算法往往表现得非常意外 - 所以这应该在使用前进行充分测试。

答案 6 :(得分:0)

这个怎么样?

int sigfigs (double value)
{
    int count = 0;
    for (; value!=floor(value); value *= 10)
          count++;
    return count;
}

这似乎在99%的时间内起作用(除了一些非常奇怪的极端情况,例如数字30.22054将返回正确的值5,而20.22054返回15)

答案 7 :(得分:0)

正如其他人所指出的,这里主要关注的是许多小数部分在二进制中不能完全表示,因此简单地将实数存储为浮点数可能会增加有效数字的外观。

但是,如果您愿意为具有许多有效数字的数字得到错误答案,则可以进行公平的近似。这是C ++的一个想法。我并不认为这是完全可移植的 - 它假设一个架构使用例如IEEE-759浮动。但是,这应该涵盖99.9%的体系结构。这也可能需要一些工作来支持真正的,非常小/大的数字。

#include <limits>
#include <cmath>

using std::numeric_limits;
using std::abs;
using std::floor;

unsigned significant_digits(double target);
unsigned significant_digits_right_of_decimal_point(double target)
{
    target=abs(target);
    return significant_digits(target-floor(target));
}

unsigned significant_digits(double target)
{
    // The number 0.0 is a special case.  How many significant digits should 0
    // have?
    if (target==0.0)
    {
        return 0;
    }

    // make sure our number is positive -- won't change number 
    // of significant digits
    target=abs(target);

    // significant digits don't depend on position of decimal point.
    // divide or multiply until we are between 0.1 and 1.0.
    // FIXME: dividing by 10 may lose some precision
    while (target<0.1)
    {
        target*=10;
    }
    while (target>=1.0)
    {
         target/=10;
    }

    // Multiply by 2 until we've got 1.0 or higher.  This shouldn't change the
    // mantissa.
    unsigned exponent=0;
    while (target<1.0)
    {
        ++exponent;
        target*=2.0;
    }

    // ok, now we know the exponent.  Figure out what the "round off" could be
    double epsilon=numeric_limits<double>::epsilon();

    // return our number back to where it had been, correcting epsilon along
    // the way.
    while (exponent>0)
    {
        --exponent;
        target/=2.0;
        epsilon/=2.0;
    }

    // now that we have an error bound, we can calculate the number of
    // significant digits.  First, shave off any leading '0's
    while (target<1.0)
    {
        target*=10;
        epsilon*=10;
    }

    // now, extract digits until nothing significant is left
    unsigned result=0;
    do 
    {
        ++result;
        target=target-floor(target);
        target*=10;
        epsilon*=10;
    } 
    while (target<=epsilon);
    return result;
 }