0

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

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

所以我得到 0.125 的 1/8

4

3 回答 3

7

这是相对简单的。例如,0.375等价于0.375/1

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

然后找到最大公约数并除以分子和分母。

GCD 的(递归)函数是:

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

375如果你用and调用它1000,它会吐出,125所以当你将分子和分母除以它时,你会得到3/8


(a)正如评论中所指出的,精度位比整数类型多的数字可能存在问题(例如 IEEE754 双精度数与 32 位整数)。您可以通过选择具有更大范围的整数(长整数或类似 MPIR 的 bignum 库)或选择“足够接近”的策略来解决此问题(当小数部分与整数部分相比相对微不足道时,将其视为整数)。

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

除非一个数字可以表示为受可用精度限制的值的总和(例如是),否则您可以希望的最好结果是一个近似值。2-nn0.3751/4 + 1/8

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

s eeeeeeee mmmmmmmmmmmmmmmmmmmmmmm
0 01111101 01010101010101010101010

在这个例子中,符号是0一个正数。

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

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

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 位十进制数字的精度(IEEE754 双精度为 15 位)。

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

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

但这不是算法本身的问题,更多的是您可以表示的数字的限制。

感谢Wolfram Alpha帮助计算。如果您需要做任何让您的计算器压力过大的数学运算,那是完成这项工作的最佳工具之一。


顺便说一句,您无疑会注意到尾数位遵循某种模式:0101010101.... 这是因为1/3它是一个无限循环的二进制值以及一个无限循环的十进制值。您最终需要无限数量的01位来准确表示1/3

于 2012-11-13T01:09:00.707 回答
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。如果你有限制,你可以做一些批判性的思考来实现它们。

于 2012-11-13T01:18:54.963 回答
0

paxdiablo 概述的方法是正确的。

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

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

来源

于 2014-04-02T22:33:08.207 回答