You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@commons.apache.org by "Sammy Trojette (JIRA)" <ji...@apache.org> on 2016/11/01 10:50:58 UTC

[jira] [Comment Edited] (IO-373) FileUtils.byteCountToDisplaySize improvement/rounding issues

    [ https://issues.apache.org/jira/browse/IO-373?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=15625001#comment-15625001 ] 

Sammy Trojette edited comment on IO-373 at 11/1/16 10:50 AM:
-------------------------------------------------------------

Since this has just come up as an issue on our current project. I'd like to do a little necro here.

With this version, the displayed value is always set at least 3 numericals so: ####, ###, ##.# or #.##.
If anyone sees a serious issue, please give me a call.

{code:title=FileUtil.java|borderStyle=solid}
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;

import static org.apache.commons.io.FileUtils.*;

/**
 * Custom Implementation of FileUtils.byteCountToDisplaySize to fix rounding bug
 */
public class FileUtil {

    private static final RoundingMode ROUNDING_MODE = RoundingMode.HALF_UP;

    enum FileSize {
        EXABYTE("EB", ONE_EB_BI),
        PETABYTE("PB", ONE_PB_BI),
        TERABYTE("TB", ONE_TB_BI),
        GIGABYTE("GB", ONE_GB_BI),
        MEGABYTE("MB", ONE_MB_BI),
        KILOBYTE("KB", ONE_KB_BI),
        BYTE("bytes", BigInteger.ONE);

        private final String unit;
        private final BigInteger byteCount;

        FileSize(String unit, BigInteger byteCount) {
            this.unit = unit;
            this.byteCount = byteCount;
        }
        private String unit() {
            return unit;
        }
        private BigInteger byteCount() {
            return byteCount;
        }

    }

    /**
     * Formats a file's size into a human readable format
     *
     * @param fileSize the file's size as BigInteger
     * @return the size as human readable string
     */
    public static String byteCountToDisplaySize(final BigInteger fileSize) {

        String unit = FileSize.BYTE.unit;
        BigDecimal fileSizeInUnit = BigDecimal.ZERO;
        String val;

        for (FileSize fs : FileSize.values()) {
            BigDecimal size_bd = new BigDecimal(fileSize);
            fileSizeInUnit = size_bd.divide(new BigDecimal(fs.byteCount), 5, ROUNDING_MODE);
            if (fileSizeInUnit.compareTo(BigDecimal.ONE) >= 0) {
                unit = fs.unit;
                break;
            }
        }

        // always round so that at least 3 numerics are displayed (###, ##.#, #.##)
        if (fileSizeInUnit.divide(BigDecimal.valueOf(100.0), BigDecimal.ROUND_DOWN).compareTo(BigDecimal.ONE) >= 0) {
            val = fileSizeInUnit.setScale(0, ROUNDING_MODE).toString();
        } else if (fileSizeInUnit.divide(BigDecimal.valueOf(10.0), BigDecimal.ROUND_DOWN).compareTo(BigDecimal.ONE) >= 0) {
            val = fileSizeInUnit.setScale(1, ROUNDING_MODE).toString();
        } else {
            val = fileSizeInUnit.setScale(2, ROUNDING_MODE).toString();
        }

        // trim zeros at the end
        if (val.endsWith(".00")) {
            val = val.substring(0, val.length() - 3);
        } else if (val.endsWith(".0")) {
            val = val.substring(0, val.length() - 2);
        }

        return String.format("%s %s", val, unit);
    }


    /**
     * Formats a file's size into a human readable format
     *
     * @param fileSize the file's size as long
     * @return the size as human readable string
     */
    public static String byteCountToDisplaySize(final long fileSize) {
        return byteCountToDisplaySize(BigInteger.valueOf(fileSize));
    }

}
{code}

{code:title=FileUtilTest.java|borderStyle=solid}
import org.junit.Test;

import static org.junit.Assert.assertEquals;

/**
 * Test-Suite for FileUtil class
 */
public class FileUtilTest {

    @Test
    public void testByteCountToDisplaySizeLong() {
        assertEquals(FileUtil.byteCountToDisplaySize(Character.MAX_VALUE), "64 KB");
        assertEquals(FileUtil.byteCountToDisplaySize(Short.MAX_VALUE), "32 KB");
        assertEquals(FileUtil.byteCountToDisplaySize(Integer.MAX_VALUE), "2 GB");
        assertEquals(FileUtil.byteCountToDisplaySize(0), "0 bytes");
        assertEquals(FileUtil.byteCountToDisplaySize(1), "1 bytes");
        assertEquals(FileUtil.byteCountToDisplaySize(1023), "1023 bytes");
        assertEquals(FileUtil.byteCountToDisplaySize(1024), "1 KB");
        assertEquals(FileUtil.byteCountToDisplaySize(1030), "1.01 KB");
        assertEquals(FileUtil.byteCountToDisplaySize(1224), "1.20 KB");
        assertEquals(FileUtil.byteCountToDisplaySize(10240), "10 KB");
        assertEquals(FileUtil.byteCountToDisplaySize(24832184), "23.7 MB");
        assertEquals(FileUtil.byteCountToDisplaySize(new Long("38174914740")), "35.6 GB");
        assertEquals(FileUtil.byteCountToDisplaySize(new Long("1374389534720")), "1.25 TB");
    }

}

{code}


was (Author: strojette):
Since this has just come up as an issue on our current project. I'd like to do a little necro here.

With this version, the displayed value is always set at least 3 numericals so: ####, ###, ##.# or #.##.
If anyone sees a serious issue, please give me a call.

{code:title=FileUtil.java|borderStyle=solid}
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;

import static org.apache.commons.io.FileUtils.*;

/**
 * Custom Implementation of FileUtils.byteCountToDisplaySize to fix rounding bug
 */
public class FileUtil {

    private static final RoundingMode ROUNDING_MODE = RoundingMode.HALF_UP;

    enum FileSize {
        EXABYTE("EB", ONE_EB_BI),
        PETABYTE("PB", ONE_PB_BI),
        TERABYTE("TB", ONE_TB_BI),
        GIGABYTE("GB", ONE_GB_BI),
        MEGABYTE("MB", ONE_MB_BI),
        KILOBYTE("KB", ONE_KB_BI),
        BYTE("bytes", BigInteger.ONE);

        private final String unit;
        private final BigInteger byteCount;

        FileSize(String unit, BigInteger byteCount) {
            this.unit = unit;
            this.byteCount = byteCount;
        }
        private String unit() {
            return unit;
        }
        private BigInteger byteCount() {
            return byteCount;
        }

    }

    /**
     * Formats a file's size into a human readable format
     *
     * @param fileSize the file's size as BigInteger
     * @return the size as human readable string
     */
    public static String byteCountToDisplaySize(final BigInteger fileSize) {

        String unit = FileSize.BYTE.unit;
        BigDecimal fileSizeInUnit = BigDecimal.ZERO;
        String val;

        for (FileSize fs : FileSize.values()) {
            BigDecimal size_bd = new BigDecimal(fileSize);
            fileSizeInUnit = size_bd.divide(new BigDecimal(fs.byteCount), 3, ROUNDING_MODE);
            if (fileSizeInUnit.compareTo(BigDecimal.ONE) >= 0) {
                unit = fs.unit;
                break;
            }
        }

        // always round so that at least 3 numerics are displayed (###, ##.#, #.##)
        if (fileSizeInUnit.divide(BigDecimal.valueOf(100.0)).compareTo(BigDecimal.ONE) > 0) {
            val = fileSizeInUnit.setScale(0, ROUNDING_MODE).toString();
        } else if (fileSizeInUnit.divide(BigDecimal.valueOf(10.0)).compareTo(BigDecimal.ONE) > 0) {
            val = fileSizeInUnit.setScale(1, ROUNDING_MODE).toString();
        } else {
            val = fileSizeInUnit.setScale(2, ROUNDING_MODE).toString();
        }

        // trim zeros at the end
        if (val.endsWith(".00")) {
            val = val.substring(0, val.length() - 3);
        } else if (val.endsWith(".0")) {
            val = val.substring(0, val.length() - 2);
        }

        return String.format("%s %s", val, unit);
    }


    /**
     * Formats a file's size into a human readable format
     *
     * @param fileSize the file's size as long
     * @return the size as human readable string
     */
    public static String byteCountToDisplaySize(final long fileSize) {
        return byteCountToDisplaySize(BigInteger.valueOf(fileSize));
    }

}
{code}

{code:title=FileUtilTest.java|borderStyle=solid}
import org.junit.Test;

import static org.junit.Assert.assertEquals;

/**
 * Test-Suite for FileUtil class
 */
public class FileUtilTest {

    @Test
    public void testByteCountToDisplaySizeLong() {
        assertEquals(FileUtil.byteCountToDisplaySize(Character.MAX_VALUE), "64 KB");
        assertEquals(FileUtil.byteCountToDisplaySize(Short.MAX_VALUE), "32 KB");
        assertEquals(FileUtil.byteCountToDisplaySize(Integer.MAX_VALUE), "2 GB");
        assertEquals(FileUtil.byteCountToDisplaySize(0), "0 bytes");
        assertEquals(FileUtil.byteCountToDisplaySize(1), "1 bytes");
        assertEquals(FileUtil.byteCountToDisplaySize(1023), "1023 bytes");
        assertEquals(FileUtil.byteCountToDisplaySize(1024), "1 KB");
        assertEquals(FileUtil.byteCountToDisplaySize(1025), "1 KB");
        assertEquals(FileUtil.byteCountToDisplaySize(24832184), "23.7 MB"); // real world test case
        assertEquals(FileUtil.byteCountToDisplaySize(new Long("38174914740")), "35.6 GB");  // real world test case
        assertEquals(FileUtil.byteCountToDisplaySize(new Long("1374389534720")), "1.25 TB");
    }

}

{code}

> FileUtils.byteCountToDisplaySize improvement/rounding issues
> ------------------------------------------------------------
>
>                 Key: IO-373
>                 URL: https://issues.apache.org/jira/browse/IO-373
>             Project: Commons IO
>          Issue Type: Improvement
>          Components: Utilities
>    Affects Versions: 2.4
>            Reporter: Mark
>            Priority: Minor
>         Attachments: byteCountToDisplaySize.patch, byteCountToHumanReadableGnu.patch
>
>
> Issue IO-226 is not fixed but closed.
> ?
> Here is my solution that also support a user-defined precision in terms of a maximum length of the digits part.



--
This message was sent by Atlassian JIRA
(v6.3.4#6332)