unitsofmeasurement / uom-systems

Units of Measurement Systems
http://www.uom.systems
Other
36 stars 17 forks source link

UCUMConverterFormatter serializes `seconds * 10^-5` weird #191

Open addict3d opened 3 years ago

addict3d commented 3 years ago

I defined seconds * 10^-5 and it serializes weird.

I expected perhaps "s/100000" or "s.10^-5". The UCUMFormat'ers all produce "s.0".

Formatter output string
UCUM Case Insensitive "s.0"
UCUM Case Sensitive "s.0"
UCUM Print "s.0"
EBNFUnitFormat "s·10^-5"

Here is a test to recreate the potential issue.

package systems.uom.ucum.format;

import org.junit.jupiter.api.Test;
import tech.units.indriya.function.MultiplyConverter;

import javax.measure.Unit;
import javax.measure.format.UnitFormat;
import javax.measure.quantity.Time;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static systems.uom.ucum.UCUM.SECOND;
import static systems.uom.ucum.format.UCUMFormat.Variant.CASE_SENSITIVE;

public class UnitFormatWithNegativeExponentFactor {
    protected static final UnitFormat FORMAT_CS = UCUMFormat.getInstance(CASE_SENSITIVE);

    /**
     * s * 10^5 => pass
     */
    @Test
    public void testExponentFactor10e5() {
        MultiplyConverter e5 = MultiplyConverter.ofExponent(10, 5);
        Unit<Time> se5 = SECOND.transform(e5);
        assertEquals("s.100000", FORMAT_CS.format(se5));
    }

    /**
     * s * (1 / 10^5) => pass
     */
    @Test
    public void testRationalFactor10e5Reciprocal() {
        MultiplyConverter e5ReciprocalRational = MultiplyConverter.ofRational(1, 100000);

        Unit<Time> s_e5 = SECOND.transform(e5ReciprocalRational);
        assertEquals("s/100000", FORMAT_CS.format(s_e5));
    }

    /**
     * s * 10^-5 => fail
     * <p>
     * Expected :s/100000
     * Actual   :s.0
     */
    @Test
    public void testExponentFactor10e5Reciprocal() {
        MultiplyConverter e5ReciprocalPow10 = MultiplyConverter.ofExponent(10, -5);
        Unit<Time> se5 = SECOND.transform(e5ReciprocalPow10);
        assertEquals("s/100000", FORMAT_CS.format(se5));
    }
}

I'm not overly familiar with this ecosystem, but it strikes me that tech.units.indriya.AbstractUnit.prefix() always results in a PowerOfIntConverter via MultiplyConverter.ofPrefix() (same as MultiplyConverter.ofExponent()). Which by luck is how I constructed the unit I was using.

Should MultiplyConverter.ofPrefix() sometimes result in a RationalConverter instead? Or should additional logic be included in UCUMConverterFormatter.formatConverter()? It seems it already handles this scenario for RationalConverter.

If we can find a good path to resolve this, I'm happy to draft a PR. Cheers!