[
https://issues.apache.org/jira/browse/CAMEL-25703?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Work on CAMEL-25703 started by shashank.
----------------------------------------
> camel-bindy - a BigDecimal, Double or Float field without precision is
> marshalled with at most three decimals (1.23456 is written as 1.235, 0.000123
> as 0), so data is silently rounded and a record does not round trip
> ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
>
> Key: CAMEL-25703
> URL: https://issues.apache.org/jira/browse/CAMEL-25703
> Project: Camel
> Issue Type: Bug
> Components: camel-bindy
> Reporter: shashank
> Assignee: shashank
> Priority: Major
>
> Since CAMEL-24168 (4.22.0) the default {{precision}} of {{@DataField}} and
> {{@KeyValuePairField}} is {{-1}}, documented as "Use -1 (default) to preserve
> the original scale from the input". Unmarshal now keeps the scale, but
> marshal does not keep the decimals.
> {{AbstractNumberFormat}} (main d46d51c24550, lines 50-65) creates a {{new
> DecimalFormat()}} and, for a precision of {{-1}}, does not set the fraction
> digits at all ("skip DecimalFormat fraction digit configuration when
> precision is unset", PR #24919). The default pattern of {{DecimalFormat}} is
> {{#,##0.###}}: at most three fraction digits, rounded {{HALF_EVEN}}.
> {{BigDecimalFormatFactory}}, {{DoubleFormatFactory}} and
> {{FloatFormatFactory}} format with it, so every value with more than three
> decimals is rounded when it is written, without an error or a log line:
> ||field type||value||written on main||
> |{{BigDecimal}}|{{1.23456}}|{{1.235}}|
> |{{BigDecimal}}|{{0.98765}}|{{0.988}}|
> |{{Double}}|{{12.3456789}}|{{12.346}}|
> |{{Double}}|{{0.000123}}|{{0}}|
> Unmarshal reads {{1.23456}} correctly, so a route that unmarshals a CSV,
> fixed-length or key-value-pair record and marshals it again changes the
> numbers. Exchange rates, unit prices, quantities and coordinates with four or
> more decimals are common in the files Bindy is used for, and the default
> annotation ({{@DataField(pos = 1)}}) is enough to hit it. The only test of
> the unset case ({{BindyBigDecimalNoPrecisionTest}}, added by CAMEL-24168)
> uses {{10.35}}, two decimals, so it passes.
> Trigger: default options; a {{BigDecimal}}, {{Double}} or {{Float}} field
> with neither {{precision}} nor {{pattern}}; any value with more than three
> decimals; {{marshal().bindy(...)}} of type Csv, Fixed or KeyValue.
> h3. Reproduction
> New {{BindyNumberWithoutPrecisionKeepsDecimalsTest}} (5 tests, locale {{en}}):
> * CSV marshal of {{BigDecimal 1.23456}}, {{Double 0.000123}}, {{Float 1.1}}:
> expected {{1.23456,0.000123,1.1}}, main writes {{1.235,0,1.1}};
> * CSV unmarshal of {{0.98765,12.3456789,2.5}} then marshal: main writes
> {{0.988,12.346,2.5}};
> * fixed-length record ({{BigDecimal}} and {{Double}}, length 10 each)
> unmarshalled and marshalled: {{ 1.23456 0.000123}} becomes {{ 1.235
> 0}};
> * controls (pass on main and with the fix): {{10}}, {{10.0}} and {{0.5f}} are
> written as {{10,10,0.5}}; the whole-number float {{123456792f}} is written as
> {{123456792}}.
> On main 3 of the first 4 fail (the first control passes; the whole-number
> float control was not run against main, which writes that float through the
> same {{double}} path). With the fix the five pass, and the camel-bindy tests:
> 243 run, 0 failures, 0 errors, 3 skipped.
> h3. Proposed fix
> * {{AbstractNumberFormat}}: when no precision is set (and no implied decimal
> separator), {{setMaximumFractionDigits(Integer.MAX_VALUE)}}, so the formatter
> keeps every decimal of a {{BigDecimal}} and the shortest decimal form of a
> {{double}} ({{DecimalFormat}} caps a {{double}} at 340 fraction digits
> itself). Grouping, locale symbols and the whole-number output ({{10}}, not
> {{10.0}}) are unchanged.
> * {{FloatFormatFactory}}: a finite {{Float}} with a fraction part and without
> precision is formatted through {{new BigDecimal(Float.toString(value))}};
> otherwise the widening to {{double}} would now show as {{1.100000023841858}}
> instead of {{1.1}}. A whole-number {{Float}} keeps the exact digits it had
> before ({{123456792}}, not the shorter {{123456790}} of {{Float.toString}}).
> * An explicit {{precision}}, a {{pattern}} and {{impliedDecimalSeparator}}
> are not affected.
> Users can notice that such values are written with all their decimals, also a
> {{Double}} that is the result of a calculation ({{0.1 + 0.2}} is written
> {{0.30000000000000004}}, before {{0.3}}); a note goes into the 4.24 upgrade
> guide.
> How it was found: round r29, track I (silent data corruption in data
> formats), a metamorphic check "unmarshal then marshal is the identity on
> valid input" applied by reading the number formats of Bindy; a JShell probe
> of {{new DecimalFormat()}} with grouping off gave {{12.346}} for {{12.34567}}
> and {{0}} for {{0.000123}}, then the test above on the real data format.
> Affected: main (d46d51c24550; tested). camel-4.23.x and camel-4.22.x have the
> same {{AbstractNumberFormat}} lines and {{precision() default -1}} (read, not
> run). camel-4.18.x and camel-4.14.x still have the older code of CAMEL-24168
> (precision 0, all decimals dropped), which is that ticket, not this one.
> Duplicate check (2026-10-16): JIRA text "bindy BigDecimal", "bindy
> precision", "bindy decimal digits", "bindy fraction digits", "bindy rounded
> three decimals" and the camel-bindy issues created since 2026-06-01
> (CAMEL-24168 is the only related one, resolved; CAMEL-25150, 25151, 25152 are
> other defects); GitHub pull requests "bindy precision", "bindy decimal" and
> "bindy in:title" (PR #24919 for CAMEL-24168, merged; nothing open).
> _Filed with Claude Code on behalf of allthingssecurity._
--
This message was sent by Atlassian Jira
(v8.20.10#820010)