假设我有一个NSNumber,介于0和1之间,并且可以使用X / Y表示,在这种情况下如何计算X和Y?我不想比较:
if (number.doubleValue == 0.125)
{
X = 1;
Y = 8;
}
所以我得到1/8的0.125
答案 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);
}
如果您使用375
和1000
来调用它,它会吐出125
,这样,当您将分子和分母除以时,就会得到3/8
。
(a)正如评论中所指出的那样,数字的精度位数可能比整数类型更高(例如IEEE754加倍,32位整数)。您可以通过选择范围较大的整数(longs,或像MPIR这样的bignum库)或选择" close-enough"来解决这个问题。策略(当分数部分与积分部分相比无关紧要时,将其视为整数。)
另一个问题是IEEE754中某些数字甚至存在,例如臭名昭着的0.1
和0.3
。
除非数字可以表示为2-n
值的总和,其中n
受可用精度限制(例如0.375
为1/4 + 1/8
),否则最好的希望是一种近似。
例如,考虑单精度(你会在下面看到为什么,我很懒,不能做整个64位)1/3
。作为单个精度值,它存储为:
s eeeeeeee mmmmmmmmmmmmmmmmmmmmmmm
0 01111101 01010101010101010101010
在此示例中,该符号为0
,因此它是一个正数。
指数位给出125,当你减去127偏差时,给出-2。因此,乘数将为2-2
或0.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;
}