NSNumber如何获得最小的共同点?像0.375的3/8?

时间:2012-11-13 01:02:19

标签: objective-c ios algorithm nsnumber

假设我有一个NSNumber,介于0和1之间,并且可以使用X / Y表示,在这种情况下如何计算X和Y?我不想比较:

if (number.doubleValue == 0.125)
{
    X = 1;
    Y = 8;
}

所以我得到1/8的0.125

3 个答案:

答案 0 :(得分:7)

这相对简单。例如,0.375相当于0.375/1

第一步是将分子和分母相乘,直到分子为整数值(a),给你375/1000

然后找出最大的公约数除以此除以分子和分母。

GCD的(递归)函数是:

int gcd (int a, int b) {
    return (b == 0) ? a : gcd (b, a%b);
}

如果您使用3751000来调用它,它会吐出125,这样,当您将分子和分母除以时,就会得到3/8


(a)正如评论中所指出的那样,数字的精度位数可能比整数类型更高(例如IEEE754加倍,32位整数)。您可以通过选择范围较大的整数(longs,或像MPIR这样的bignum库)或选择" close-enough"来解决这个问题。策略(当分数部分与积分部分相比无关紧要时,将其视为整数。)

另一个问题是IEEE754中某些数字甚至存在,例如臭名昭​​着的0.10.3

除非数字可以表示为2-n值的总和,其中n受可用精度限制(例如0.3751/4 + 1/8),否则最好的希望是一种近似。

例如,考虑单精度(你会在下面看到为什么,我很懒,不能做整个64位)1/3。作为单个精度值,它存储为:

s eeeeeeee mmmmmmmmmmmmmmmmmmmmmmm
0 01111101 01010101010101010101010

在此示例中,该符号为0,因此它是一个正数。

指数位给出125,当你减去127偏差时,给出-2。因此,乘数将为2-20.25

尾数位有点棘手。它们形成显式1的总和以及2-n位的所有1值,其中n为1到23(从左到右。所以尾数是计算如此:

s eeeeeeee mmmmmmmmmmmmmmmmmmmmmmm
0 01111101 01010101010101010101010
            | | | | | | | | | | |
            | | | | | | | | | | +-- 0.0000002384185791015625
            | | | | | | | | | +---- 0.00000095367431640625
            | | | | | | | | +------ 0.000003814697265625
            | | | | | | | +-------- 0.0000152587890625
            | | | | | | +---------- 0.00006103515625
            | | | | | +------------ 0.000244140625
            | | | | +-------------- 0.0009765625
            | | | +---------------- 0.00390625
            | | +------------------ 0.015625
            | +-------------------- 0.0625
            +---------------------- 0.25
                           Implicit 1
                                    ========================
                                    1.3333332538604736328125

当你乘以0.25时(参见前面的指数),你得到:

0.333333313465118408203125

现在 为什么他们说你只得到大约7个十进制数字的精度(15个用于IEEE754双精度)。

如果您通过我上面的算法传递实际数字,您将无法获得1/3,而是获得:

 5,592,405
---------- (or 0.333333313465118408203125)
16,777,216

但这对算法本身来说不是问题,更多的是你可以代表的数字的限制。

Wolfram Alpha求助于计算。如果您需要执行任何任何数学计算器,那么这将是您计算器的最佳工具之一。


  

顺便说一句,您毫无疑问会注意到尾数位遵循某种模式:0101010101...。这是因为1/3是无限重复的二进制值以及无限重复的十进制值。您需要在末尾使用无限数量的01位来准确表示1/3

答案 1 :(得分:1)

你可以试试这个:

- (CGPoint)yourXAndYValuesWithANumber:(NSNumber *)number
{
    float x = 1.0f;
    float y = x/number.doubleValue;
    for(int i = 1; TRUE; i++)
    {
        if((float)(int)(y * i) == y * i)
        // Alternatively floor(y * i), instead of (float)(int)(y * i)
        {
            x *= i;
            y *= i;
            break;
        }
    }
    /* Also alternatively
    int coefficient = 1;
    while(floor(y * coefficient) != y * coefficient)coefficient++;
    x *= coefficient, y *= coefficient;*/
    return CGPointMake(x, y);
}

如果输入无效,则无效。 X和Y必须存在并且是有效的自然数(1到无穷大)。打破它的一个好例子是1 / pi。如果你有限制,你可以做一些批判性的思考来实现它们。

答案 2 :(得分:0)

paxdiablo概述的方法是定点。

我只是想提供一个有效的GCD功能(迭代实现):

int gcd (int a, int b){
    int c;
    while ( a != 0 ) {
        c = a; a = b%a; b = c;
    }
    return b;
}

Source