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

Jak*_*old 3 c java math logarithm

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

// say the number is 1000
(int)(log(1000)/log(10)) + 1
Run Code Online (Sandbox Code Playgroud)

这是基于数学公式

log10 x = logn x/logn10(这里解释)

但我发现,在C中,

(int)(log(1000)/log(10)) + 1
Run Code Online (Sandbox Code Playgroud)

不是等于

(int) log10(1000) + 1
Run Code Online (Sandbox Code Playgroud)

但它应该是.

我甚至用Java编写了相同的代码

(int) (Math.log(1000) / Math.log(10)) + 1
(int) Math.log10(1000) + 1
Run Code Online (Sandbox Code Playgroud)

但它的行为方式却一样错误.

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

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

我明白了

2 2
3 3
4 3  // here second method produces wrong result for 1000
5 5
6 6
7 6  // here again
Run Code Online (Sandbox Code Playgroud)

所以这个错误似乎发生在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
Run Code Online (Sandbox Code Playgroud)

产生相同,错误的输出

2 2
3 3
4 3
5 5
6 6
7 6
Run Code Online (Sandbox Code Playgroud)

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

log10(999) + 1 = 3.9995654882259823
log10(1000) + 1 =  4.0
Run Code Online (Sandbox Code Playgroud)

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

jan*_*nko 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));
}
Run Code Online (Sandbox Code Playgroud)

说明你的问题.只需删除转换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
Run Code Online (Sandbox Code Playgroud)

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

编辑:你更新了你要使用的问题floor(),但像铸造floor()向下舍入,因此删除小数!


Ski*_*izz 8

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

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


Eam*_*nne 5

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

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

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