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

hzx*_*zxu 0 algorithm objective-c nsnumber ios

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

if (number.doubleValue == 0.125)
{
    X = 1;
    Y = 8;
}
Run Code Online (Sandbox Code Playgroud)

所以我得到1/8的0.125

pax*_*blo 7

那是相对简单的.例如,0.375相当于0.375/1.

第一步是乘以分子和分母,直到分子是一个整数值(a),给你375/1000.

然后找出最大公约数,并将分子和分母除以.

GCD的(递归)函数是:

int gcd (int a, int b) {
    return (b == 0) ? a : gcd (b, a%b);
}
Run Code Online (Sandbox Code Playgroud)

如果你用375和调用它1000,它会吐出来,125这样,当你用它除分子和分母时,你就得到了3/8.


(a)正如评论中指出的那样,数字的精度位数可能比整数类型更高(例如IEEE754加倍,32位整数).您可以通过选择具有更大范围的整数(long,或像MPIR这样的bignum库)或选择"足够接近"策略来解决此问题(当小数部分与积分部分相比相对无关紧要时,将其视为整数).

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

除非数字可以表示为受可用精度(例如存在)限制的值的总和,否则您可以期望的最佳值是近似值.2-nn0.3751/4 + 1/8

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

s eeeeeeee mmmmmmmmmmmmmmmmmmmmmmm
0 01111101 01010101010101010101010
Run Code Online (Sandbox Code Playgroud)

在这个例子中,符号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
Run Code Online (Sandbox Code Playgroud)

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

0.333333313465118408203125
Run Code Online (Sandbox Code Playgroud)

现在这就是为什么他们说你只得到大约7位精度的十进制数字(15为IEEE754双精度).

如果你通过我上面的算法传递实际数字,你就不会得到1/3,你会得到:

 5,592,405
---------- (or 0.333333313465118408203125)
16,777,216
Run Code Online (Sandbox Code Playgroud)

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

请向Wolfram Alpha帮忙进行计算.如果您需要进行任何强调计算器的数学计算,那么这是该工作的最佳工具之一.


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

  • +1第一步可能需要一段时间用于"1/3",但是;-) (2认同)