为什么log(1000)/ log(10)与log10(1000)不同?

时间:2009-09-30 10:56:01

标签: java c math logarithm

今天,我遇到了一个非常奇怪的问题。我需要计算一个数字的字符串长度,所以我提出了这个解决方案

// say the number is 1000
(int)(log(1000)/log(10)) + 1

这是基于数学公式

log 10 x = log n x/log n 10(解释{ {3}})

但我发现,在C中,

(int)(log(1000)/log(10)) + 1

NOT 等于

(int) log10(1000) + 1

但它应该是。

我甚至在Java中使用此代码尝试了同样的事情

(int) (Math.log(1000) / Math.log(10)) + 1
(int) Math.log10(1000) + 1

但它的行为方式相同。

故事还在继续。执行此代码后

for (int i = 10; i < 10000000; i *= 10) {
   System.out.println(((int) (Math.log10(i)) + 1) + 
                " " + ((int) (Math.log(i) / Math.log(10)) + 1));
}

我得到了

2 2
3 3
4 3  // here second method produces wrong result for 1000
5 5
6 6
7 6  // here again

所以这个错误似乎发生在1000的每个倍数上。

我向我的C老师展示了这个,他说这可能是由于日志分割期间的某种类型转换错误引起的,但他不知道为什么。

所以我的问题是

  • 为什么(int) (Math.log(1000) / Math.log(10)) + 1不等于(int) Math.log10(1000) + 1 ,而应该是,根据数学计算。
  • 为什么只有1000的倍数才会出错?

编辑:这不是舍入错误,因为

Math.floor(Math.log10(i)) + 1
Math.floor(Math.log(i) / Math.log(10)) + 1

产生相同的错误输出

2 2
3 3
4 3
5 5
6 6
7 6

edit2:我必须向下舍入,因为我想知道位数

log10(999) + 1 = 3.9995654882259823
log10(1000) + 1 =  4.0

如果我只是四舍五入,我会得到相同的结果(4),这对于999是错误的,因为它有3位数。

8 个答案:

答案 0 :(得分:23)

您提供了代码段

for (int i = 10; i < 10000000; i *= 10) {
   System.out.println(((int) (Math.log10(i)) + 1) + 
                " " + ((int) (Math.log(i) / Math.log(10)) + 1));
}

说明你的问题。只需将转换移至int并再次运行循环。你会收到

2.0 2.0
3.0 3.0
4.0 3.9999999999999996
5.0 5.0
6.0 6.0
7.0 6.999999999999999

立即回答你的问题。正如tliff已经提出的那样,演员们会截断小数而不是正确地舍入。

编辑:您更新了问题以使用floor(),但是像投注floor()一样将向下,因此会删除小数点!

答案 1 :(得分:8)

日志操作是Transcendental Function。计算机评估结果的最佳方法是使用近似于所需操作的Algebraic Function。结果的准确性取决于计算机使用的算法(这可能是FPU中的微码)。在英特尔FPU上,有一些设置影响各种超越函数的精度(三角函数也是超越的),FPU规范将详细说明所用各种算法的准确度。

因此,除了上面提到的舍入误差之外,还存在精度问题,因为计算的log(x)可能不等于实际log(x)。

答案 2 :(得分:5)

由于精确度和舍入问题,此Math.log(1000) / Math.log(10)并不等于3。

如果您需要精确的精度,请不要使用浮点运算 - 并且通常会放弃对数。浮点数本质上是模糊的。要获得精确的结果,请使用整数运算。

我真的建议你不要沿着这条路走下去,但听起来你正在用整数的对数来确定一个数量级。如果是这种情况,那么(int)(Math.log(x+0.5) / Math.log(10))会更稳定 - 但是要意识到double只有53位的精度,所以大约10个第15个双精灵不能再精确地表示整数,这个技巧赢了那不行。

答案 3 :(得分:4)

为分子添加一个非常小的值,以绕过Skizz指出的准确性问题。

// say the number is 1000
(int)((log(1000)+1E-14)/log(10)) + 1

1E-14应该足以将准确性推回到赛道中。

改变了1E-15的小值,这会给某些输入带来错误的结果

我用1E-14测试unsigned long long s的随机样本,并且我的所有数字都已通过。

答案 4 :(得分:2)

更新:这是由于精度和舍入错误

答案 5 :(得分:0)

如果你想把你的结果作为一个整数,你可能应该四舍五入,而不仅仅是在一点一点之后切断它。

你可能会得到像6.999999这样的东西并将其四舍五入到6。

答案 6 :(得分:0)

使用(int)强制转换,您将切断必要的小数部分。尝试将它们打印成双打而不进行铸造(为什么还要进行铸造?),你会没事的。

答案 7 :(得分:0)

打印出中间结果,即log(1000),log(10),log(1000)/ log(10)和log10(1000)。这应该提供比猜测更好的提示。