Skip to content

Commit 89dee1f

Browse files
committed
Use precision of input value for output of Unit.convert()
1 parent 5340ac9 commit 89dee1f

3 files changed

Lines changed: 52 additions & 37 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
- in `Unit.convert()` (and therfore, `Qudt.convert()`), use the precision (= number of significant digits) of the input
11+
value as the precision of the output value (notable exception: input value `1`, which is interpreted as precision 3).
12+
1013
## [7.0.0] - 2025-07-22
1114

1215
### Added

qudtlib-model/src/main/java/io/github/qudtlib/model/Unit.java

Lines changed: 38 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.github.qudtlib.model;
22

33
import static io.github.qudtlib.nodedef.Builder.buildSet;
4+
import static java.math.BigDecimal.ONE;
45

56
import io.github.qudtlib.exception.InconvertibleQuantitiesException;
67
import io.github.qudtlib.nodedef.Builder;
@@ -395,16 +396,21 @@ public BigDecimal convert(BigDecimal value, Unit toUnit, QuantityKind quantityKi
395396
}
396397
BigDecimal fromOffset =
397398
ignoreOffset ? BigDecimal.ZERO : this.getConversionOffset().orElse(BigDecimal.ZERO);
398-
BigDecimal fromMultiplier = this.getConversionMultiplier().orElse(BigDecimal.ONE);
399+
BigDecimal fromMultiplier = this.getConversionMultiplier().orElse(ONE);
399400
BigDecimal toOffset =
400401
ignoreOffset
401402
? BigDecimal.ZERO
402403
: toUnit.getConversionOffset().orElse(BigDecimal.ZERO);
403-
BigDecimal toMultiplier = toUnit.getConversionMultiplier().orElse(BigDecimal.ONE);
404-
return value.add(fromOffset)
405-
.multiply(fromMultiplier, MathContext.DECIMAL128)
406-
.divide(toMultiplier, MathContext.DECIMAL128)
407-
.subtract(toOffset);
404+
BigDecimal toMultiplier = toUnit.getConversionMultiplier().orElse(ONE);
405+
BigDecimal result =
406+
value.add(fromOffset)
407+
.multiply(fromMultiplier, MathContext.DECIMAL128)
408+
.divide(toMultiplier, MathContext.DECIMAL128)
409+
.subtract(toOffset);
410+
int resultPrecision = Math.min(BigDecimal.ONE.equals(value) ? 3 : value.precision(), 34);
411+
412+
MathContext resultContext = new MathContext(resultPrecision);
413+
return result.round(resultContext);
408414
}
409415

410416
/**
@@ -417,7 +423,7 @@ public BigDecimal convert(BigDecimal value, Unit toUnit, QuantityKind quantityKi
417423
*/
418424
public BigDecimal getConversionMultiplier(Unit toUnit) {
419425
if (this.equals(toUnit)) {
420-
return BigDecimal.ONE;
426+
return ONE;
421427
}
422428
if (this.conversionOffsetDiffers(toUnit)) {
423429
throw new IllegalArgumentException(
@@ -427,25 +433,31 @@ public BigDecimal getConversionMultiplier(Unit toUnit) {
427433
}
428434
Optional<BigDecimal> fromMultiplier = this.getConversionMultiplier();
429435
Optional<BigDecimal> toMultiplier = toUnit.getConversionMultiplier();
430-
return fromMultiplier
431-
.map(
432-
from ->
433-
toMultiplier
434-
.map(to -> from.divide(to, MathContext.DECIMAL128))
435-
.orElse(null))
436-
.orElseThrow(
437-
() ->
438-
new InconvertibleQuantitiesException(
439-
String.format(
440-
"Cannot convert %s(%s) to %s(%s)",
441-
this.getIriAbbreviated(),
442-
this.getConversionMultiplier().isEmpty()
443-
? "no multiplier"
444-
: "has multiplier",
445-
toUnit.getIriAbbreviated(),
446-
toUnit.getConversionMultiplier().isEmpty()
447-
? "no multiplier"
448-
: "has multiplier")));
436+
BigDecimal result =
437+
fromMultiplier
438+
.map(
439+
from ->
440+
toMultiplier
441+
.map(to -> from.divide(to, MathContext.DECIMAL128))
442+
.orElse(null))
443+
.orElseThrow(
444+
() ->
445+
new InconvertibleQuantitiesException(
446+
String.format(
447+
"Cannot convert %s(%s) to %s(%s)",
448+
this.getIriAbbreviated(),
449+
this.getConversionMultiplier().isEmpty()
450+
? "no multiplier"
451+
: "has multiplier",
452+
toUnit.getIriAbbreviated(),
453+
toUnit.getConversionMultiplier().isEmpty()
454+
? "no multiplier"
455+
: "has multiplier")));
456+
int precision = Math.min(34, result.precision());
457+
return result.round(
458+
new MathContext(
459+
precision)); // TODO: when units know about their precision we can improve
460+
// this
449461
}
450462

451463
public boolean conversionOffsetDiffers(Unit other) {

qudtlib-test/src/test/java/io/github/qudtlib/QudtTests.java

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -489,21 +489,19 @@ public void testInconvertible() {
489489

490490
@Test
491491
public void testConvert_L_to_GAL_US() {
492-
BigDecimal converted = Qudt.convert(BigDecimal.ONE, Qudt.Units.L, Qudt.Units.GAL_US);
493-
MatcherAssert.assertThat(
494-
converted,
495-
Matchers.comparesEqualTo(new BigDecimal("0.2641720523581484153798999216091625")));
492+
BigDecimal converted = Qudt.convert(new BigDecimal(1.00), Qudt.Units.L, Qudt.Units.GAL_US);
493+
MatcherAssert.assertThat(converted, Matchers.comparesEqualTo(new BigDecimal("0.264")));
496494
}
497495

498496
@Test
499497
public void testConvert_Celsius_to_Fahrenheit() {
500498
QuantityValue celsius100 =
501-
new QuantityValue(new BigDecimal("100"), Qudt.unitFromLocalnameRequired("DEG_C"));
499+
new QuantityValue(
500+
new BigDecimal("100.00"), Qudt.unitFromLocalnameRequired("DEG_C"));
502501
QuantityValue fahrenheit = Qudt.convert(celsius100, Qudt.unitIriFromLocalname("DEG_F"));
503502
Assertions.assertNotNull(fahrenheit);
504503
MatcherAssert.assertThat(
505-
fahrenheit.getValue(),
506-
Matchers.comparesEqualTo(new BigDecimal("211.9999999999999999999999999999999")));
504+
fahrenheit.getValue(), Matchers.comparesEqualTo(new BigDecimal("212")));
507505
Assertions.assertEquals(Qudt.unitIriFromLocalname("DEG_F"), fahrenheit.getUnit().getIri());
508506
}
509507

@@ -533,14 +531,14 @@ public void testConvert_Celsius_to_Kelvin_tempdiff() {
533531
public void testConvert_Celsius_to_Fahrenheit_2() {
534532
MatcherAssert.assertThat(
535533
Qudt.convert(new BigDecimal("100"), Units.DEG_C, Units.DEG_F),
536-
Matchers.comparesEqualTo(new BigDecimal("211.9999999999999999999999999999999")));
534+
Matchers.comparesEqualTo(new BigDecimal("212")));
537535
}
538536

539537
@Test
540538
public void testConvert_Fahrenheit_to_Celsius() {
541539
MatcherAssert.assertThat(
542540
Qudt.convert(new BigDecimal("100"), Units.DEG_F, Units.DEG_C),
543-
Matchers.comparesEqualTo(new BigDecimal("37.7777777777777777777777777777778")));
541+
Matchers.comparesEqualTo(new BigDecimal("37.8")));
544542
}
545543

546544
@Test
@@ -601,9 +599,11 @@ public void testConvert_FemtoGM_to_KiloGM() {
601599
@Test
602600
public void testConvert_Metric_to_Imperial() {
603601
BigDecimal converted = Qudt.convert(BigDecimal.ONE, Qudt.Units.LB, Qudt.Units.KiloGM);
604-
MatcherAssert.assertThat(converted, Matchers.comparesEqualTo(new BigDecimal("0.45359237")));
602+
MatcherAssert.assertThat(converted, Matchers.comparesEqualTo(new BigDecimal("0.454")));
603+
converted = Qudt.convert(new BigDecimal("1.0"), Qudt.Units.LB, Qudt.Units.KiloGM);
604+
MatcherAssert.assertThat(converted, Matchers.comparesEqualTo(new BigDecimal("0.45")));
605605
converted = Qudt.convert(BigDecimal.ONE, Qudt.Units.BTU_IT__PER__LB, Qudt.Units.J__PER__GM);
606-
MatcherAssert.assertThat(converted, Matchers.comparesEqualTo(new BigDecimal("2.326")));
606+
MatcherAssert.assertThat(converted, Matchers.comparesEqualTo(new BigDecimal("2.33")));
607607
}
608608

609609
@Test

0 commit comments

Comments
 (0)