假设我有一个 NSNumber,它介于 0 和 1 之间,并且可以使用 X/Y 表示,在这种情况下如何计算 X 和 Y?我不想比较:
if (number.doubleValue == 0.125)
{
X = 1;
Y = 8;
}
所以我得到 0.125 的 1/8
假设我有一个 NSNumber,它介于 0 和 1 之间,并且可以使用 X/Y 表示,在这种情况下如何计算 X 和 Y?我不想比较:
if (number.doubleValue == 0.125)
{
X = 1;
Y = 8;
}
所以我得到 0.125 的 1/8
这是相对简单的。例如,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.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
与所有位值的总和,其中是 1 到 23(从左到右。所以尾数计算如下:2-n
1
n
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
。
你可以试试这个:
- (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。如果你有限制,你可以做一些批判性的思考来实现它们。
paxdiablo 概述的方法是正确的。
我只是想提供一个高效的 GCD 功能(迭代实现):
int gcd (int a, int b){
int c;
while ( a != 0 ) {
c = a; a = b%a; b = c;
}
return b;
}
来源。