6

Is there a Java equivalent of the C / C++ function called frexp? If you aren't familiar, frexp is defined by Wikipedia to "break floating-point number down into mantissa and exponent."

I am looking for an implementation with both speed and accuracy but I would rather have the accuracy if I could only choose one.

This is the code sample from the first reference. It should make the frexp contract a little more clear:

/* 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 */
Bob Cross
  • 22,116
  • 12
  • 58
  • 95
  • I thought the Apache Commons Math package might be a good place to find this, but I didn't see anything in there. Perhaps you could put in a feature request for it? Or, if you decide to code it up yourself, talk to them about including it in the library - seems like a useful addition to me. – Carl Oct 12 '09 at 16:14
  • @Carl, I agree that it would be useful. I know myself and my workload very well, though, so I'm not going to commit to trying to create it on my own. I'm certain that I could do 80% of the work 80% correctly with the time that I have to invest and that's very close to worse than useless.... – Bob Cross Oct 12 '09 at 20:22
  • the real question is: Why does frexp doesn't break the float into two integers but wants at least a float.. For a float decomposition that does not make sense (think recursion.....) – Nils Pipenbrinck Oct 13 '09 at 21:51
  • @Nils, actually, there isn't any recursion. The result value is defined to be between 0.5 and 1.0. In that case, the exponent will always be zero. – Bob Cross Oct 14 '09 at 01:39

6 Answers6

3

How's this?

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;
}

This is "inspired" or actually nearly copied identically from an answer to a similar C# question. It works with the bits and then makes the mantissa a number between 1.0 and 0.0.

Community
  • 1
  • 1
Jay R.
  • 31,911
  • 17
  • 52
  • 61
  • 1
    Yikes! The code above is not quite correct: it should be while(realMant >= 1.0) instead of while(realMant > 1.0). The magnitude of the return value must be in the range of 1/2 (inclusive) to 1 (exclusive), see [GNU libc manual](http://www.gnu.org/software/libc/manual/html_node/Normalization-Functions.html). With the code above, frexp(1.0) will erroneously return 1.0 instead of 0.5. – akbertram Nov 26 '15 at 11:35
2

This does do what you want.

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 = -1; // Thx Kevin
        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));
  }
}
jitter
  • 53,475
  • 11
  • 111
  • 124
  • @jitter, thanks but frexp actually works with the bits of the IEEE floating point standard rather than trying to deduce a mathematical result. That's the goal of this question. – Bob Cross Oct 13 '09 at 21:34
  • 2
    Dear jitter (wow 12 years ago): I just ran some frexp() versus your solution equivalence tests, and, after a minor bug fix, congratulations, it works! The one bug is "sign--" it should say "sign = -1" instead. Thanks much, we have a client that is porting our C# code and they can't use MSVCRT.dll so we had to re-write frexp(). – Kevin North Aug 31 '21 at 01:56
  • 1
    @KevinNorth Thx updated the answer – jitter Sep 10 '22 at 23:58
1

See Float.floatToIntBits and Double.doubleToLongBits. You still need a little additional logic to decode IEEE 754 floating points.

Maurice Perry
  • 32,610
  • 9
  • 70
  • 97
  • Thanks - I'm aware of the ability to get at the bits. What I'm concerned with is not the base case of parsing s, e, and m from the bit set. I'm more worried about having a complete implementation of frexp that maintains the contract of handling all the corner cases (different flavors of NaN, for example). – Bob Cross Oct 12 '09 at 13:19
0

If I'm reading this right...

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);
  }
}

Unfortunately, there doesn't appear to be a built-in method to get the mantissa without converting it to a BigDecimal first (or just doing the division: result = param / Math.pow(2,n).

Strangely enough, scalb does the exact opposite: take a mantissa and exponent and generate a new float from it.

Powerlord
  • 87,612
  • 17
  • 125
  • 175
  • @R. Bemrose, the whole point of the exercise is not to convert. Instead, the function takes the IEEE standard floating point representation and decodes that. The goal isn't to come up with a mathematical expression that seems to give the same answer. – Bob Cross Oct 14 '09 at 01:37
-1

I'm not familiar with the frexp function, but I think you need to look at the BigDecimal' scaled and unscaled values. 'unscaled' is the precision mantissa, scale is the exponent. In psuedocode: value = unscaledValue 10^(-scale)

p3t0r
  • 1,980
  • 1
  • 16
  • 22
-1

Nope there is no current implementation in core Java or in the Commons Lang(most likely other place to find it) that has the exact same functionality and ease of frexp; that I know of. If it does exist it's probably in a not widely used toolkit.

non sequitor
  • 18,296
  • 9
  • 45
  • 64