幂函数给出的答案与 C 中的 math.pow 函数不同
Power function giving different answer than math.pow function in C
我正在尝试编写一个程序来使用 while
循环计算 x^n 的值:
#include <stdio.h>
#include <math.h>
int main()
{
float x = 3, power = 1, copyx;
int n = 22, copyn;
copyx = x;
copyn = n;
while (n)
{
if ((n % 2) == 1)
{
power = power * x;
}
n = n / 2;
x *= x;
}
printf("%g^%d = %f\n", copyx, copyn, power);
printf("%g^%d = %f\n", copyx, copyn, pow(copyx, copyn));
return 0;
}
直到 n
的值为 15,我创建的函数和 pow
函数(来自 math.h
)的答案给出了相同的值;但是,当 n
的值超过 15 时,它就会开始给出不同的答案。
我不明白为什么会有不同的答案。是我函数写错了还是其他原因?
浮点运算不精确(float
比 double
更差,用于存储数据的位数更少;使用 double
可能会延迟不精确的时间更长)。 pow
函数(通常)使用求幂算法来最小化精度损失,and/or 委托给 chip-level 指令,该指令可以更有效、更精确地执行操作,或两者兼而有之。 pow
的实现也可能不止一种,具体取决于您是否告诉编译器使用严格一致的浮点数学、最快的可能、硬件指令等。
您的代码很好(尽管使用 double
会得到更精确的结果),但是与 math.h
的 pow
的改进精度相匹配的是 non-trivial;当你这样做的时候,你就会重新发明它。这就是你使用库函数的原因。
就是说,对于您在此处使用的逻辑整数数学,算法的精度损失可能无关紧要,这纯粹是 float
与 double
问题导致精度损失从类型本身。作为一项规则,默认使用 double
,只有当你 100% 确定你不需要精度时才切换到 float
负担 double
.
的额外 memory/computation 成本
当我 运行 你的代码时,我得到了这个:
3^22 = 31381059584.000000
3^22 = 31381059609.000000
这是因为 pow
returns a double
但您的代码使用 float
。当我更改为 powf
时,我得到了相同的结果:
3^22 = 31381059584.000000
3^22 = 31381059584.000000
因此,如果您需要高分辨率结果,只需在任何地方使用 double
。
您混淆了两种不同类型的 floating-point 数据。 pow
函数使用 double
类型,但您的循环使用 float
类型(精度较低)。
您可以通过 或者 为您的 x
、power
和 copyx
使用 double
类型来使结果一致变量, 或 通过调用 powf
函数(使用 float
类型)而不是 pow
.
后面的调整(使用powf
)给出了以下输出(clang-cl编译器,Windows10,64位):
3^22 = 31381059584.000000
3^22 = 31381059584.000000
并且,将 main
的第一行更改为 double x = 3, power = 1, copyx;
会得到以下结果:
3^22 = 31381059609.000000
3^22 = 31381059609.000000
请注意,随着 n
的值越来越大,您的循环结果与使用 pow
或 powf
计算的值之间出现差异的可能性越来越大库函数。在我的平台上,double
版本给出了相同的结果,直到值超出范围并变为 Infinity
。但是,float
版本开始围绕 n = 55
出现分歧:
3^55 = 174449198498104595772866560.000000
3^55 = 174449216944848669482418176.000000
精度
float x = 3, power = 1; ... power = power * x
形成了float
个产品。
pow(x, y)
形成 double
结果,良好的实现在内部使用更广泛的数学。
OP 的循环方法在第 15 次迭代后产生四舍五入的结果。这些四舍五入慢慢地加剧了最终结果的不准确性。
316是一个26位的奇数。
float
精确编码所有奇数,直到通常为 224。较大的值都是偶数,只有 24 位有效二进制数字。
double
精确编码所有奇数,直到通常为 253.
要进行公平比较,请使用:
double
个对象和 pow()
或
float
个对象和 powf()
.
对于大幂,pow(f)()
函数肯定会提供比循环更好的答案,因为与循环方法相比,此类函数通常使用内部扩展精度和管理良好的舍入。
我正在尝试编写一个程序来使用 while
循环计算 x^n 的值:
#include <stdio.h>
#include <math.h>
int main()
{
float x = 3, power = 1, copyx;
int n = 22, copyn;
copyx = x;
copyn = n;
while (n)
{
if ((n % 2) == 1)
{
power = power * x;
}
n = n / 2;
x *= x;
}
printf("%g^%d = %f\n", copyx, copyn, power);
printf("%g^%d = %f\n", copyx, copyn, pow(copyx, copyn));
return 0;
}
直到 n
的值为 15,我创建的函数和 pow
函数(来自 math.h
)的答案给出了相同的值;但是,当 n
的值超过 15 时,它就会开始给出不同的答案。
我不明白为什么会有不同的答案。是我函数写错了还是其他原因?
浮点运算不精确(float
比 double
更差,用于存储数据的位数更少;使用 double
可能会延迟不精确的时间更长)。 pow
函数(通常)使用求幂算法来最小化精度损失,and/or 委托给 chip-level 指令,该指令可以更有效、更精确地执行操作,或两者兼而有之。 pow
的实现也可能不止一种,具体取决于您是否告诉编译器使用严格一致的浮点数学、最快的可能、硬件指令等。
您的代码很好(尽管使用 double
会得到更精确的结果),但是与 math.h
的 pow
的改进精度相匹配的是 non-trivial;当你这样做的时候,你就会重新发明它。这就是你使用库函数的原因。
就是说,对于您在此处使用的逻辑整数数学,算法的精度损失可能无关紧要,这纯粹是 float
与 double
问题导致精度损失从类型本身。作为一项规则,默认使用 double
,只有当你 100% 确定你不需要精度时才切换到 float
负担 double
.
当我 运行 你的代码时,我得到了这个:
3^22 = 31381059584.000000
3^22 = 31381059609.000000
这是因为 pow
returns a double
但您的代码使用 float
。当我更改为 powf
时,我得到了相同的结果:
3^22 = 31381059584.000000
3^22 = 31381059584.000000
因此,如果您需要高分辨率结果,只需在任何地方使用 double
。
您混淆了两种不同类型的 floating-point 数据。 pow
函数使用 double
类型,但您的循环使用 float
类型(精度较低)。
您可以通过 或者 为您的 x
、power
和 copyx
使用 double
类型来使结果一致变量, 或 通过调用 powf
函数(使用 float
类型)而不是 pow
.
后面的调整(使用powf
)给出了以下输出(clang-cl编译器,Windows10,64位):
3^22 = 31381059584.000000
3^22 = 31381059584.000000
并且,将 main
的第一行更改为 double x = 3, power = 1, copyx;
会得到以下结果:
3^22 = 31381059609.000000
3^22 = 31381059609.000000
请注意,随着 n
的值越来越大,您的循环结果与使用 pow
或 powf
计算的值之间出现差异的可能性越来越大库函数。在我的平台上,double
版本给出了相同的结果,直到值超出范围并变为 Infinity
。但是,float
版本开始围绕 n = 55
出现分歧:
3^55 = 174449198498104595772866560.000000
3^55 = 174449216944848669482418176.000000
精度
float x = 3, power = 1; ... power = power * x
形成了float
个产品。
pow(x, y)
形成 double
结果,良好的实现在内部使用更广泛的数学。
OP 的循环方法在第 15 次迭代后产生四舍五入的结果。这些四舍五入慢慢地加剧了最终结果的不准确性。
316是一个26位的奇数。
float
精确编码所有奇数,直到通常为 224。较大的值都是偶数,只有 24 位有效二进制数字。
double
精确编码所有奇数,直到通常为 253.
要进行公平比较,请使用:
double
个对象和pow()
或float
个对象和powf()
.
对于大幂,pow(f)()
函数肯定会提供比循环更好的答案,因为与循环方法相比,此类函数通常使用内部扩展精度和管理良好的舍入。