Mic*_*ers 0 floating-point precision objective-c pow libm
关于目标c中的pow函数的一般问题.
为什么当base = 125时,以下代码吐出ans = 4.9999999
NSDecimalNumber * base = [[NSDecimalNumber alloc ]initWithString:@"125"];
NSDecimalNumber * root = [[NSDecimalNumber alloc] initWithString:@"3"];
double ans=pow(125, 1.0/[root doubleValue]);
Run Code Online (Sandbox Code Playgroud)
当base = 27时正好为3
NSDecimalNumber * base = [[NSDecimalNumber alloc ]initWithString:@"27"];
NSDecimalNumber * root = [[NSDecimalNumber alloc] initWithString:@"3"];
double ans=pow(125, 1.0/[root doubleValue]);
Run Code Online (Sandbox Code Playgroud)
这里有一些因素在起作用.最重要的1.0/3.0是,不是三分之一,所以你不是在计算多维数据集的根base.相反,您指定的计算是:
base**0.333333333333333314829616256247390992939472198486328125
Run Code Online (Sandbox Code Playgroud)
要么
exp(0.333333333333333314829616256247390992939472198486328125 * log(base))
Run Code Online (Sandbox Code Playgroud)
如果base是125,这种计算的准确实数的结果是:
4.999999999999999553291243227753830961690873860134487744...
Run Code Online (Sandbox Code Playgroud)
这个值的两个最接近的可表示的双倍是5.0和:
4.99999999999999911182158029987476766109466552734375
Run Code Online (Sandbox Code Playgroud)
后一个值几乎不接近数学上的精确结果,因此pow函数返回了最佳(或"正确舍入")答案.
当您使用base等于27 执行相同的计算时,数学上精确的实数结果为:
2.99999999999999981704430129767885583952101310176125736...
Run Code Online (Sandbox Code Playgroud)
在这种情况下,这个数字3.0比任何其他可表示的双数更接近,所以再次pow返回了最好的结果.
因此,在这些情况下,该pow函数会为您提供的计算提供最准确的结果.现在,也就是说,并不能保证pow对所有可能的输入都这样做(事实上,通常它没有).对于所有输入,您将无法得到如此准确的结果,并且对于某些平台上的许多输入,您将得到明显不太准确的结果.通常,您不应该依赖pow函数的最后一位舍入,也不应该依赖于未明确定义的任何函数来生成正确舍入的结果.
简而言之:您获得的结果是最好的结果,而在其他一些平台上,您将不会如此幸运.
备择方案:
您可能会考虑使用该cbrt函数,该函数也不保证正确的舍入,但至少计算立方根而不是0.3333333333333333148296 ...次幂.
如果您事先知道结果应该是整数,则可以通过roundor rint函数将其四舍五入为最接近的整数.
如果您确实需要精确的最后一位舍入,请考虑使用CRLibm库.这将带来一些性能成本,但如果你绝对必须有正确的舍入,那么它是唯一的好选择(但请注意,它将为这些特定的例子产生完全相同的结果).
| 归档时间: |
|
| 查看次数: |
218 次 |
| 最近记录: |