C++ 如何检测以10为基数的小数点是否可以在以2为基数的小数点中精确表示

C++ 如何检测以10为基数的小数点是否可以在以2为基数的小数点中精确表示,c++,C++,作为数字库测试的一部分,我需要选择以10为基数的十进制数字,这些数字可以以2为基数精确表示。如果在基2中精确地表示基10十进制数,则如何检测C++中的?< /p> 我的第一个猜测如下: bool canBeRepresentedInBase2(const double &pNumberInBase10) { //check if a number in base 10 can be represented exactly in base 2 //reference: ht

作为数字库测试的一部分,我需要选择以10为基数的十进制数字,这些数字可以以2为基数精确表示。如果在基2中精确地表示基10十进制数,则如何检测C++中的?< /p> 我的第一个猜测如下:

bool canBeRepresentedInBase2(const double &pNumberInBase10)
{
    //check if a number in base 10 can be represented exactly in base 2
    //reference: http://en.wikipedia.org/wiki/Binary_numeral_system
    bool funcResult = false;

    int nbOfDoublings = 16*3;
    double doubledNumber = pNumberInBase10;
    for (int i = 0; i < nbOfDoublings ; i++)
    {
        doubledNumber = 2*doubledNumber;
        double intPart;
        double fracPart = modf(doubledNumber/2, &intPart);
        if (fracPart == 0) //number can be represented exactly in base 2
        {
            funcResult = true;
            break;
        }
    }
    return funcResult;
}
bool canBeRepresentedInBase2(常量双精度和pNumberInBase10)
{
//检查以10为基数的数字是否能以2为基数精确表示
//参考:http://en.wikipedia.org/wiki/Binary_numeral_system
bool funcResult=false;
int-nbOfDoublings=16*3;
double doubledNumber=pNumberInBase10;
for(int i=0;i
我使用以下值测试了此函数:-1.0/4.0、0.0、0.1、0.2、0.205、1.0/3.0、7.0/8.0、1.0、256.0/255.0、1.02、99.005。对于-1.0/4.0、0.0、7.0/8.0、1.0、99.005,它返回true,这是正确的


有更好的主意吗?

如果您检查它是否为二进制,它将始终返回true。如果您的方法采用
double
作为参数,则该数字已经用二进制表示(
double
是二进制类型,通常为64位)。看看你的代码,我想你实际上是想看看它是否可以精确地表示为一个整数,在这种情况下,你为什么不能直接转换为
int
,然后返回到
double
,并与原始代码进行比较呢。存储在double中的任何整数,如果在
int
表示的范围内,则应该是精确的,IIRC,因为64位
double
有53位尾数(我假设是32位int)。这意味着如果它们相等,则为整数。

或者更简单:

return pNumber == floor(pNumber);
另一方面,如果你有一些奇怪的分数表示法(分子-分母对,或带小数点的字符串,或其他),你真的想知道这个值是否可以精确地表示为双精度,这就有点困难了


但是您需要一个不同的参数…

正如rmeador所指出的,接受double可能不是一个好主意,因为该数字已转换为double,这可能是您试图检查的数字的近似值

因此,以一种非常抽象的方式,您应该将支票拆分为整数和小数。整数不应太大,以致尾数不能表示所有整数(例如,9007199254740993不应正确表示为64位fp) 从心理上讲,小数点可能更容易一些,因为如果小数点之后的任何内容(例如xxx.yyy中的yyy)包含除2以外的任何因素,则浮点会重复以尝试表示它。这就是为什么1/3不能用基数为10=基数(2*5)的有限位数表示的原因。。。看


编辑:正如评论所指出的,如果十进制数的系数不是1/2,那么这在数学上是正确的表达方式…

我不认为这是他所要求的。。。我想他正在寻找一个解决方案,告诉他一个数字是否可以用二进制形式精确表示。例如,33.3。。这是一个不能用二进制表示的数字,因为它将永远持续,所以根据您的FPU设置,它将被表示为类似“33.33333 6”的内容。所以,看起来他的方法可以解决这个问题。我不知道还有什么更好的方法可以让我头脑清醒。
\

我想你要找的是一个有小数部分的数字,它是一系列负2次幂的和(也就是1除以2的幂)。我相信这应该总是能够在IEEE浮点数/双精度中准确地表示出来

例如:

0.375=(1/4+1/8),应具有精确表示

如果您想生成这些。您可以尝试这样做:

#include <iostream>
#include <cstdlib>

int main() {
    srand(time(0));
    double value = 0.0;
    for(int i = 1; i < 256; i *= 2) {
        // doesn't matter, some random probability of including this
        // fraction in our sequence..
        if((rand() % 3) == 0) {
            value += (1.0 / static_cast<double>(i));        
        }
    }
    std::cout << value << std::endl;
}

因为不是所有分数都有精确的表示法,但是当你把它放入一个双精度的时候,你选择了一个二进制的表示法。。。与测试目的背道而驰。

正如其他人所提到的,您的方法不符合您的意思,因为您传递了一个表示为(二进制)双精度的数字。如果传递的数字是
integer/2^48
格式,该方法实际上会检测。对于像是二进制的
(1+2^-50)
,和不是二进制的
259/255
,这样的数字应该失败


如果你真的想测试一个数字是否可以用有限的二进制字符串精确表示,你必须以精确的形式传递一个数字。

如果你传递的是双精度,那么根据定义,它已经用二进制表示了,如果不是,那么你已经失去了准确性


也许可以尝试将分数的分子和分母传递给函数。然后你没有失去准确性,你可以检查一下,看看你是否能想出一个二进制表示的答案,这是相同的分数,你已经通过了

>你问C++,但这个算法可能会有帮助。我用“EE”来表示“完全可以表达为float”

从要测试的数字的十进制表示形式开始。删除任何尾随零(即,0.123450000变为0.12345)

1) 如果数字不是整数,请检查最右边的数字是否为5。如果不是,那么停下来——号码不是EE。 2) 把这个数字乘以2。如果结果是一个整数,那么停止——数字是EE。否则,返回步骤1

我没有严格的证据证明这一点,但有一个“温暖的模糊”。启动计算器,输入你最喜欢的分数幂2,比如0.0000152587890625。将它自身添加几十次(我只点击了一次“+”,然后多次点击“=”)。如果小数点右侧有任何非零数字,
bool canBeRepresentedExactly(int numerator, int denominator);
public static Boolean canBeRepresentedInBase2(String thenumber)
{
    // Reuturns true of the parsed Double did not loose precision.
    // Only works for numbers that are not converted into scientific notation by toString.
    return thenumber.equals(Double.parseDouble(thenumber).toString())
}
static bool canBeRepresentedInBase2(decimal pNumberInBase10)
{
    //check if a number in base 10 can be represented exactly in base 2
    //reference: http://en.wikipedia.org/wiki/Binary_numeral_system
    bool funcResult = false;

    int nbOfDoublings = 16*3;
    decimal doubledNumber = pNumberInBase10;
    for (int i = 0; i < nbOfDoublings ; i++)
    {
        doubledNumber = 2*doubledNumber;
        decimal intPart;
        decimal fracPart = ModF(doubledNumber/2, out intPart);
        if (fracPart == 0) //number can be represented exactly in base 2
        {
                funcResult = true;
                break;
        }
    }
    return funcResult;
}

static decimal ModF(decimal number, out decimal intPart)
{
    intPart = Math.Floor(number);
    decimal fractional = number - (intPart);
    return fractional;
}