Pers.narod.ru. Тексты. Java2ME. Немного математики на Java2 ME

Профиль мобильной явы MIDP 2.0 / CLDC 1.1, доступный в большинстве современных мобильных телефонов, позволяет писать мидлеты, работающие с вещественными числами, для чего в Java2ME определены классы Float и Double. Однако, класс Math, содержащий константы и методы для математических вычислений, по-прежнему включает в себя лишь весьма ограниченный набор функций - по сути дела, это только синус, косинус, тангенс и квадратный корень, плюс несколько тривиальных методов, вроде поиска максимума или отбрасывания дробной части числа.

Если Вашему мидлету требуется большее, путь один - написать собственные реализации функций. В качестве примера в этой заметке я приведу несколько своих старых кодов. Как будто, они проверены временем и работают.

Округление вещественного числа на Java2 ME

Делается очень легко, с помощью стандартных методов floor и ceil

 public static double round(double a) {
  if (a - Math.floor(a) < Math.ceil(a) - a) return Math.floor(a);
  else return Math.ceil(a);
 }

Отсечь "лишние" знаки у вещественных чисел теперь можно очень просто:

 public static double trunc(double a) {
  return round(a*1000)/1000;
 }

Чтобы оставить не 3, а 2 знака, достаточно умножить и поделить на 100, а не 1000.

Вычисление экспоненты, логарифма и степени числа на Java2 ME

А здесь удобнее всего использовать ряды Тейлора. С экспонентой, раскладываемой в ряд для любого аргумента, дело обстоит так.

 public static double exp(double x) {
  if (Math.abs(x)>706) return 1/0;  
  double l=1.0;
  double xn=x;
  double nf=1;
  int i;
  for (i=1; i<51; i++) {
   l+=xn/nf;
   xn*=x;
   nf*=(i+1);
  }
  return l;
 }

При значении аргумента x, большем 706, точности типа double всё равно не хватит, отсюда первое ограничение. Здесь всегда делается 50 итераций, что довольно много, на практике лучше вместо цикла for использовать цикл while, условием выхода из которого будет достижение очередным слагаемым ряда, то есть, значением xn/nf, значения, меньшего требуемой точности (скажем, 10-5).

Логарифм неудобен тем, что хорошо раскладывается в ряд только для значений 0<x<1. Но ничто не мешает представить нам x в виде x=b*10a, где b<1 и a - целое. После несложных преобразований получим формулу ln x = ln b + a * ln 10, причём ln 10 можно заранее вычислить как константу. После этого, воспользовавшись разложением в ряд Тейлора, получим код

 public static double ln (double x) {
  if (x<=0) return 1/0;
  double a,b;
  if (x>1) {
   a=0;
   while (x>1) {
    x/=10;
    a++;
   }
   b=x;
  }
  else {
   b=x; a=0.0;
  }
  int s=1;
  double l=0.0;
  b-=1.0;
  double xn=b;
  double n=1.0;
  int i;
  for (i=1; i<51; i++) {
   l+=s*xn/n;
   s=-s;
   xn*=b;
   n=(double)(i+1);
  }
  return l+a*2.302585092994046;
 }

Наконец, имея функции для экспоненты и логарифма, нетрудно вычислить произвольную степень по формуле ab = exp (b * ln a). Поскольку логарифмов от отрицательного аргумента не существует, а нулевые и первые степени от любого числа считаются тривиально, в функции придётся учесть разнообразные особые случаи, в приведённом ниже коде для простоты это сделано с точностью 10-9.

 public static double pow(double a, double b) {
  if (Math.abs(b)>100) return 1/0;
  if (Math.abs(a)<1E-9) return 0;
  if (Math.abs(b)<1E-9) return 1;
  if (Math.abs(b-1)<1E-9) return a;
  double c;
  if (a>0) c=exp(b*ln(a));
  else {
   double bl=round(b);
   if (Math.abs(bl-b)<1E-9) { //целая степень
    int bi=(new Double(bl)).intValue();
    c=exp(bi*ln(Math.abs(a)));
    if (bi%2!=0) c=-c;
   }
   else {
    return 1/0;
   }
  }
  return c;
 }

Код несовершенен, например, он способен возвести в отрицательную целую, но не отрицательную вещественную степень.

Рейтинг@Mail.ru

вверх гостевая; E-mail
Hosted by uCoz