Почему log (1000) / log (10) не совпадает с log10 (1000)?

Сегодня я столкнулся с довольно странной проблемой. Мне нужно было рассчитать длину строки числа, поэтому я придумал это решение

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

Это основано на математической формуле

log 10 x = log n x/log n 10 (поясняется здесь )

Но я узнал, что в C,

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

НЕ равно

 (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?

edit: Это не ошибка округления, потому что

 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 цифры.

Вы предоставили fragment кода

 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, отливки отрезали десятичные знаки вместо правильного округления.

EDIT: вы обновили свой вопрос, чтобы использовать floor() , но как литье floor() округляется вниз и, следовательно, бросает десятичные знаки!

Работа с журналом является трансцендентальной функцией . Лучший компьютер, который может сделать для оценки результата, – использовать Алгебраическую функцию, которая аппроксимирует требуемую операцию. Точность результата зависит от алгоритма, который использует компьютер (это может быть микрокод в FPU). На FPU Intel есть настройки, влияющие на точность различных трансцендентных функций (триггерные функции также трансцендентны), а спецификации FPU будут подробно описывать уровень точности используемых различных алгоритмов.

Таким образом, в дополнение к упомянутым выше ошибкам округления существует также проблема точности, так как вычисляемый log (x) не может быть равен фактическому log (x).

Это связано с проблемами точности и округления. Math.log(1000) / Math.log(10) не точно равно 3.

Если вам нужна точная точность, не используйте арифметику с плавающей запятой – и вообще откажитесь от логарифмов. Числа с плавающей запятой по своей сути нечеткие. Для точного результата используйте целочисленную арифметику.

Я действительно предлагаю вам не идти по этому пути в целом, но похоже, что вы берете логарифм целых чисел, чтобы определить какой-то порядок. Если это так, то (int)(Math.log(x+0.5) / Math.log(10)) будет более стабильным – но поймите, что double имеют только 53 бит точности, поэтому около 10 15-ти удваивается больше не могут представлять целые числа, и тогда этот трюк не будет работать.

Добавьте очень маленькое значение в числитель, чтобы обойти проблему точности, указанную Skizz.

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

1E-14 должно быть достаточно, чтобы поднять точность на дорожке.

изменил небольшое значение с 1E-15, что дало бы неправильные результаты для некоторых входов

Я протестировал с 1E-14 для случайной выборки unsigned long long s и всех моих номеров.

Обновлено: это связано с ошибками точности и округления

если вы хотите, чтобы ваш результат был целым, вы должны, вероятно, округлить, а не просто отрезать часть после точки.

Вероятно, вы получаете что-то вроде 6.999999 и округлите его до 6.

С помощью (int) casting вы отключите нужную десятичную часть. Попробуйте напечатать их как дубликаты без кастинга (почему вы все равно?), И с вами все будет в порядке.

Распечатайте промежуточные результаты, т. Е. Log (1000), log (10), log (1000) / log (10) и log10 (1000). Это должно дать лучшие намеки, чем угадать.