2009-10-12 5 views
5

Есть ли Java-эквивалент функции C/C++, называемый frexp? Если вы не знакомы, frexp - это defined by Wikipedia, чтобы «разбить число с плавающей запятой вниз на мантисса и экспонента».Есть ли Java-эквивалент frexp?

Я ищу реализацию с максимальной скоростью и точностью, но я бы предпочел бы иметь точность, если бы мог только выбрать ее.

Это пример кода из первой ссылки. Он должен сделать договор frexp немного более ясным:

/* frexp example */ 
#include <stdio.h> 
#include <math.h> 

int main() 
{ 
    double param, result; 
    int n; 

    param = 8.0; 
    result = frexp (param , &n); 
    printf ("%lf * 2^%d = %f\n", result, n, param); 
    return 0; 
} 

/* Will produce: 0.500000 * 2^4 = 8.000000 */ 
+0

Я думал, что пакет Apache Commons Math может быть хорошим местом, чтобы найти это, но я ничего там не видел. Возможно, вы могли бы добавить в него запрос функции? Или, если вы решите сами его закодировать, поговорите с ними о включении его в библиотеку - кажется полезным для меня полезным дополнением. – Carl

+0

@ Карл, я согласен, что это было бы полезно. Я очень хорошо знаю себя и свою рабочую нагрузку, поэтому я не собираюсь пытаться создать его самостоятельно. Я уверен, что смогу сделать 80% работы на 80% правильно со временем, когда я должен инвестировать, и это очень близко к худшему, чем бесполезно .... –

+0

Реальный вопрос: почему frexp не нарушает float в два целых числа, но хочет по крайней мере float. Для разложения float, который не имеет смысла (подумайте о рекурсии .....) –

ответ

3

Как это

.?
public static class FRexpResult 
{ 
    public int exponent = 0; 
    public double mantissa = 0.; 
} 

public static FRexpResult frexp(double value) 
{ 
    final FRexpResult result = new FRexpResult(); 
    long bits = Double.doubleToLongBits(value); 
    double realMant = 1.; 

    // Test for NaN, infinity, and zero. 
    if (Double.isNaN(value) || 
     value + value == value || 
     Double.isInfinite(value)) 
    { 
     result.exponent = 0; 
     result.mantissa = value; 
    } 
    else 
    { 

     boolean neg = (bits < 0); 
     int exponent = (int)((bits >> 52) & 0x7ffL); 
     long mantissa = bits & 0xfffffffffffffL; 

     if(exponent == 0) 
     { 
     exponent++; 
     } 
     else 
     { 
     mantissa = mantissa | (1L<<52); 
     } 

     // bias the exponent - actually biased by 1023. 
     // we are treating the mantissa as m.0 instead of 0.m 
     // so subtract another 52. 
     exponent -= 1075; 
     realMant = mantissa; 

     // normalize 
     while(realMant > 1.0) 
     { 
     mantissa >>= 1; 
     realMant /= 2.; 
     exponent++; 
     } 

     if(neg) 
     { 
     realMant = realMant * -1; 
     } 

     result.exponent = exponent; 
     result.mantissa = realMant; 
    } 
    return result; 
} 

Это «вдохновленный» или фактически почти скопированный тождественно от answer к аналогичному вопросу C#. Он работает с битами, а затем делает мантиссой числом от 1,0 до 0,0.

+0

Yikes! Код выше не совсем корректен: он должен быть while (realMant> = 1.0), а не while (realMant> 1.0). Величина возвращаемого значения должна быть в диапазоне от 1/2 (включительно) до 1 (исключая), см. [Руководство GNU libc] (http://www.gnu.org/software/libc/manual/html_node/Normalization -Functions.html). С помощью приведенного выше кода frexp (1.0) ошибочно вернет 1.0 вместо 0.5. – akbertram

-1

Я не знаком с функцией frexp, но я думаю, вы должны смотреть на BigDecimal 'масштабируемых и немасштабированных значений. «unscaled» - это точность мантиссы, масштаб - показатель степени. В psuedocode: значение = unscaledValue 10^(- scale)

1

См. Float.floatToIntBits и Double.doubleToLongBits. Вам все еще нужна небольшая дополнительная логика для декодирования плавающих точек IEEE 754.

+0

Спасибо - я знаю, как можно получить бит. Меня интересует не базовый случай разбора s, e и m из набора бит.Меня больше беспокоит полная реализация frexp, которая поддерживает контракт на обработку всех угловых случаев (например, различные варианты NaN). –

0

Это делает то, что вы хотите.

public class Test { 
    public class FRex { 

    public FRexPHolder frexp (double value) { 
     FRexPHolder ret = new FRexPHolder(); 

     ret.exponent = 0; 
     ret.mantissa = 0; 

     if (value == 0.0 || value == -0.0) { 
     return ret; 
     } 

     if (Double.isNaN(value)) { 
     ret.mantissa = Double.NaN; 
     ret.exponent = -1; 
     return ret; 
     } 

     if (Double.isInfinite(value)) { 
     ret.mantissa = value; 
     ret.exponent = -1; 
     return ret; 
     } 

     ret.mantissa = value; 
     ret.exponent = 0; 
     int sign = 1; 

     if (ret.mantissa < 0f) { 
     sign--; 
     ret.mantissa = -(ret.mantissa); 
     } 
     while (ret.mantissa < 0.5f) { 
     ret.mantissa *= 2.0f; 
     ret.exponent -= 1; 
     } 
     while (ret.mantissa >= 1.0f) { 
     ret.mantissa *= 0.5f; 
     ret.exponent++; 
     } 
     ret.mantissa *= sign; 
     return ret; 
    } 
    } 

    public class FRexPHolder { 
    int exponent; 
    double mantissa; 
    } 

    public static void main(String args[]) { 
    new Test(); 
    } 

    public Test() { 
    double value = 8.0; 
    //double value = 0.0; 
    //double value = -0.0; 
    //double value = Double.NaN; 
    //double value = Double.NEGATIVE_INFINITY; 
    //double value = Double.POSITIVE_INFINITY; 

    FRex test = new FRex(); 
    FRexPHolder frexp = test.frexp(value); 
    System.out.println("Mantissa: " + frexp.mantissa); 
    System.out.println("Exponent: " + frexp.exponent); 
    System.out.println("Original value was: " + value); 
    System.out.println(frexp.mantissa+" * 2^" + frexp.exponent + " = "); 
    System.out.println(frexp.mantissa*(1<<frexp.exponent)); 
    } 
} 
+0

@jitter, спасибо, но frexp фактически работает с битами стандарта IEEE с плавающей запятой, а не пытается вывести математический результат. Это и есть цель этого вопроса. –

-1

Неа нет текущей реализации в базовом Java или в Commons Lang (скорее всего другое место, чтобы найти его), который имеет точно такую ​​же функциональность и легкость frexp; что я знаю. Если это действительно существует, вероятно, это не широко используемый инструментарий.

0

Если я читаю это право ...

public class Frexp { 
    public static void main (String[] args) 
    { 
    double param, result; 
    int n; 

    param = 8.0; 
    n = Math.getExponent(param); 
    //result = ?? 

    System.out.printf ("%f * 2^%d = %f\n", result, n, param); 
    } 
} 

К сожалению, не представляется, встроенный метод, чтобы получить мантиссу без преобразования его в BigDecimal первым (или просто делать деление:. result = param/Math.pow(2,n)

Как ни странно, scalb делает прямо противоположное: принять мантиссу и экспоненту и генерировать новый поплавок из него

+0

@ R. Бемроуз, весь смысл упражнения - не преобразовать. Вместо этого функция принимает стандартное представление с плавающей запятой IEEE и декодирует это. Цель состоит не в том, чтобы придумать математическое выражение, которое, похоже, дает тот же ответ. –

Смежные вопросы