grib/src/main/java/ucar/nc2/grib/GribData.java
public static void calcScaleOffset(GribData.Bean bean1, Formatter f) {
    float[] data;
    try {
      data = bean1.readData();
    } catch (IOException e) {
      f.format("IOException %s", e.getMessage());
      return;
    }
    int npoints = data.length;

    // we always use unsigned packed
    // "If the packed values are intended to be interpreted as signed/unsigned integers"
    // http://www.unidata.ucar.edu/software/netcdf/docs/BestPractices.html
    int nbits = bean1.getNBits();
    int width = (2 << nbits - 1) - 2;                // unsigned
    int missing_value = (2 << nbits - 1) - 1;       // all ones - reserved for missing value

    // int width2 = (2 << (nbits-1)) - 1;  // signed
    f.format(" nbits = %d%n", nbits);
    f.format(" npoints = %d%n", npoints);
    f.format(" width = %d (0x%s) %n", width, Long.toHexString(width));
    f.format(" scale = %g %n", bean1.getScale());
    f.format(" resolution = %g %n", bean1.getScale() / 2);
    f.format(" range = %f %n%n", bean1.getMaximum() - bean1.getMinimum());

    float dataMin = Float.MAX_VALUE;
    float dataMax = -Float.MAX_VALUE;
    for (float fd : data) {
      if (Float.isNaN(fd)) continue;
      dataMin = Math.min(dataMin, fd);
      dataMax = Math.max(dataMax, fd);
    }
    f.format("           actual    computed%n");
    f.format(" dataMin = %8f %8f%n", dataMin, bean1.getMinimum());
    f.format(" dataMax = %8f %8f%n", dataMax, bean1.getMaximum());

    f.format(" actual range = %f%n", (dataMax - dataMin));

    // scale_factor =(dataMax - dataMin) / (2^n - 1)
    // add_offset = dataMin + 2^(n-1) * scale_factor

    double scale_factor = (dataMax - dataMin) / width;
    // float add_offset = dataMin + width2 * scale_factor / 2; // signed
    double add_offset = dataMin;                             // unsigned

    f.format(" scale_factor = %g%n", scale_factor);
    f.format(" add_offset = %g%n", add_offset);

    // unpacked_data_value = packed_data_value * scale_factor + add_offset
    // packed_data_value = nint((unpacked_data_value - add_offset) / scale_factor)

    ByteBuffer bb = ByteBuffer.allocate(4 * npoints);
    IntBuffer intBuffer = bb.asIntBuffer();
    double diffMax = -Double.MAX_VALUE;
    double diffTotal = 0;
    double diffTotal2 = 0;
    for (float fd : data) {
      if (Float.isNaN(fd)) {
        intBuffer.put(missing_value);
        continue;
      }

      // otherwise pack it
      int packed_data = (int) Math.round((fd - add_offset) / scale_factor);   // nint((unpacked_data_value - add_offset) / scale_factor)
      double unpacked_data = packed_data * scale_factor + add_offset;
      double diff = Math.abs(fd - unpacked_data);
      if (diff > scale_factor / 2)
        f.format("***   org=%g, packed_data=%d unpacked=%g diff = %g%n", fd, packed_data, unpacked_data, diff);

      diffMax = Math.max(diffMax, diff);
      diffTotal += diff;
      diffTotal2 += diff * diff;
      intBuffer.put(packed_data);
    }

    f.format("%n max_diff = %g%n", diffMax);
    f.format(" avg_diff = %g%n", diffTotal / data.length);

    // Math.sqrt( sumsq/n - avg * avg)
    double mean = diffTotal / npoints;
    double var = (diffTotal2 / npoints - mean * mean);
    f.format(" std_diff = %g%n", Math.sqrt(var));

    f.format("%nCompression%n");
    f.format(" number of values = %d%n", npoints);
    f.format(" uncompressed as floats = %d%n", npoints * 4);
    int packedBitsLen = npoints * nbits / 8;
    f.format(" uncompressed packed bits = %d%n", packedBitsLen);
    f.format(" grib data length = %d%n", bean1.getDataLength());
    f.format(" grib msg length = %d%n", bean1.getMsgLength());

    byte[] bdata = convertToBytes(data);
    byte[] scaledData = bb.array();

    ////////////////////////////////////////////
    f.format("%ndeflate (float)%n");
    Deflater deflater = new Deflater();
    deflater.setInput(bdata);
    deflater.finish();
    int compressedSize = deflater.deflate(new byte[10 * npoints]);
    deflater.end();
    f.format(" compressedSize = %d%n", compressedSize);
    f.format(" ratio floats / size = %f%n", (float) (npoints * 4) / compressedSize);
    f.format(" ratio packed bits / size = %f%n", (float) packedBitsLen / compressedSize);
    f.format(" ratio size / grib = %f%n", (float) compressedSize / bean1.getMsgLength());

    /////////////////////////////////////////////////////////
    f.format("%ndeflate (scaled ints)%n");
    deflater = new Deflater();
    deflater.setInput(scaledData);
    deflater.finish();
    compressedSize = deflater.deflate(new byte[10 * npoints]);
    deflater.end();

    f.format(" compressedSize = %d%n", compressedSize);
    f.format(" ratio floats / size = %f%n", (float) (npoints * 4) / compressedSize);
    f.format(" ratio packed bits / size = %f%n", (float) packedBitsLen / compressedSize);
    f.format(" ratio size / grib = %f%n", (float) compressedSize / bean1.getMsgLength());
  }
public static void calcScaleOffset(GribData.Bean bean1, Formatter f) {
    float[] data;
    try {
      data = bean1.readData();
    } catch (IOException e) {
      f.format("IOException %s", e.getMessage());
      return;
    }
    int npoints = data.length;

    // we always use unsigned packed
    // "If the packed values are intended to be interpreted as signed/unsigned integers"
    // http://www.unidata.ucar.edu/software/netcdf/docs/BestPractices.html
    int nbits = bean1.getNBits();
    int width = (2 << nbits - 1) - 2;                // unsigned
    int missing_value = (2 << nbits - 1) - 1;       // all ones - reserved for missing value

    // int width2 = (2 << (nbits-1)) - 1;  // signed
    f.format(" nbits = %d%n", nbits);
    f.format(" npoints = %d%n", npoints);
    f.format(" width = %d (0x%s) %n", width, Long.toHexString(width));
    f.format(" scale = %g %n", bean1.getScale());
    f.format(" resolution = %g %n", bean1.getScale() / 2);
    f.format(" range = %f %n%n", bean1.getMaximum() - bean1.getMinimum());

    float dataMin = Float.MAX_VALUE;
    float dataMax = -Float.MAX_VALUE;
    for (float fd : data) {
      if (Float.isNaN(fd)) continue;
      dataMin = Math.min(dataMin, fd);
      dataMax = Math.max(dataMax, fd);
    }
    f.format("           actual    computed%n");
    f.format(" dataMin = %8f %8f%n", dataMin, bean1.getMinimum());
    f.format(" dataMax = %8f %8f%n", dataMax, bean1.getMaximum());

    f.format(" actual range = %f%n", (dataMax - dataMin));

    // scale_factor =(dataMax - dataMin) / (2^n - 1)
    // add_offset = dataMin + 2^(n-1) * scale_factor

    double scale_factor = (dataMax - dataMin) / width;
    // float add_offset = dataMin + width2 * scale_factor / 2; // signed
    double add_offset = dataMin;                             // unsigned

    f.format(" scale_factor = %g%n", scale_factor);
    f.format(" add_offset = %g%n", add_offset);

    // unpacked_data_value = packed_data_value * scale_factor + add_offset
    // packed_data_value = nint((unpacked_data_value - add_offset) / scale_factor)

    ByteBuffer bb = ByteBuffer.allocate(4 * npoints);
    IntBuffer intBuffer = bb.asIntBuffer();
    double diffMax = -Double.MAX_VALUE;
    double diffTotal = 0;
    double diffTotal2 = 0;
    for (float fd : data) {
      if (Float.isNaN(fd)) {
        intBuffer.put(missing_value);
        continue;
      }

      // otherwise pack it
      int packed_data = (int) Math.round((fd - add_offset) / scale_factor);   // nint((unpacked_data_value - add_offset) / scale_factor)
      double unpacked_data = packed_data * scale_factor + add_offset;
      double diff = Math.abs(fd - unpacked_data);
      if (diff > scale_factor / 2)
        f.format("***   org=%g, packed_data=%d unpacked=%g diff = %g%n", fd, packed_data, unpacked_data, diff);

      diffMax = Math.max(diffMax, diff);
      diffTotal += diff;
      diffTotal2 += diff * diff;
      intBuffer.put(packed_data);
    }

    f.format("%n max_diff = %g%n", diffMax);
    f.format(" avg_diff = %g%n", diffTotal / data.length);

    // Math.sqrt( sumsq/n - avg * avg)
    double mean = diffTotal / npoints;
    double var = (diffTotal2 / npoints - mean * mean);
    f.format(" std_diff = %g%n", Math.sqrt(var));

    f.format("%nCompression%n");
    f.format(" number of values = %d%n", npoints);
    f.format(" uncompressed as floats = %d%n", npoints * 4);
    int packedBitsLen = npoints * nbits / 8;
    f.format(" uncompressed packed bits = %d%n", packedBitsLen);
    f.format(" grib data length = %d%n", bean1.getDataLength());
    f.format(" grib msg length = %d%n", bean1.getMsgLength());

    byte[] bdata = convertToBytes(data);
    byte[] scaledData = bb.array();

    ////////////////////////////////////////////
    f.format("%ndeflate (float)%n");
    Deflater deflater = new Deflater();
    deflater.setInput(bdata);
    deflater.finish();
    int compressedSize = deflater.deflate(new byte[10 * npoints]);
    deflater.end();
    f.format(" compressedSize = %d%n", compressedSize);
    f.format(" ratio floats / size = %f%n", (float) (npoints * 4) / compressedSize);
    f.format(" ratio packed bits / size = %f%n", (float) packedBitsLen / compressedSize);
    f.format(" ratio size / grib = %f%n", (float) compressedSize / bean1.getMsgLength());

    /////////////////////////////////////////////////////////
    f.format("%ndeflate (scaled ints)%n");
    deflater = new Deflater();
    deflater.setInput(scaledData);
    deflater.finish();
    compressedSize = deflater.deflate(new byte[10 * npoints]);
    deflater.end();

    f.format(" compressedSize = %d%n", compressedSize);
    f.format(" ratio floats / size = %f%n", (float) (npoints * 4) / compressedSize);
    f.format(" ratio packed bits / size = %f%n", (float) packedBitsLen / compressedSize);
    f.format(" ratio size / grib = %f%n", (float) compressedSize / bean1.getMsgLength());
  }
This reports how well different compression schemes would work on the specific data.
Should be renamed