[ 
https://issues.apache.org/jira/browse/SPARK-58820?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Uroš Bojanić updated SPARK-58820:
---------------------------------
    Description: 
*Q1. What are you trying to do? Articulate your objectives using absolutely no 
jargon.*

Add a new Spark SQL data type *DECFLOAT* that stores decimal numbers with a 
flexible decimal point (floating point decimal numbers). Each value keeps up to 
a fixed number of significant decimal digits and its own exponent, so one 
column can hold both very large integers and very small fractions without 
picking a single fixed scale for the whole column.

The type follows the IEEE 754 decimal floating-point formats:
||*SQL type*||*IEEE format*||*Significant digits*||*Storage width*||
|DECFLOAT(16)|decimal64|16|8 bytes|
|DECFLOAT(34)|decimal128|34|16 bytes|

Bare DECFLOAT means DECFLOAT(34).

Unlike DECIMAL(p,s), there is no column-wide scale. Unlike FLOAT / DOUBLE, 
arithmetic is done in base 10, so values such as 0.1 are exact.

Because each value carries at most 16 or 34 significant digits, a result 
needing more digits is rounded to the format's precision. Rounding is decimal 
(base 10), unlike binary rounding in FLOAT/DOUBLE. The v1 fixed default is IEEE 
roundTiesToEven / HALF_EVEN (banker's rounding), which differs from Spark's 
existing DECIMAL arithmetic (rounds HALF_UP). This can be configurable in 
future iterations.

The type also supports IEEE special values: signed zero, +/-Infinity, and quiet 
NaN.

*Q2. What problem is this proposal NOT designed to solve?*
 * *Arbitrary-precision / unbounded decimals.* Extending Spark's fixed-point 
DECIMAL beyond 38 digits, or adding a PostgreSQL-style unbounded NUMERIC, is 
out of scope. Those needs are different from IEEE decimal floating point.
 * *Wider-than-IEEE formats in v1.* Formats such as a 70-digit / 256-bit 
decimal float (sometimes discussed as DECFLOAT(70) / decimal256) are out of 
scope for the first delivery. The type and storage designs should not preclude 
them later.
 * *Replacing* DECIMAL *or* DOUBLE{*}.{*} Existing fixed-point and binary-float 
types remain unchanged. DECFLOAT is additive.
 * *Non-IEEE decimal-float dialects as the native type.* Engines such as 
Snowflake, Oracle, and Teradata expose a decimal float that is not a strict 
IEEE width (e.g. 38 digits, no NaN/Inf). Spark's native type targets IEEE 754 
decimal64 and decimal128. Mapping those dialects into Spark is a connector / 
cast concern, not a second native type.
 * *ORC / CSV / JSON as first-class DECFLOAT storage in v1.* The intended 
persistence target is Parquet (and table formats that sit on Parquet), via the 
standardized logical type (Appendix D). Other formats may round-trip via 
existing types (e.g. string or binary) until separately specified.

*Q3. How is it done today, and what are the limits of current practice?*

Spark SQL today offers two numeric families for real numbers:
 # DECIMAL(p,s) *(fixed-point).* Precision and scale are fixed for the column. 
Spark caps precision at 38. Mixed magnitudes force a tradeoff: many fractional 
digits leave few integer digits (and the reverse). Chains of arithmetic expand 
precision quickly and often require manual casts.
 # FLOAT */* DOUBLE *(binary floating-point).* Wide range, but many common 
decimal fractions are not exact. About 15-16 significant decimal digits for 
DOUBLE, with binary rounding error.

Workarounds in use today:
||*Workaround*||*Limitation*||
|Map high-precision source decimals to DECIMAL(38,s)|Overflow / out-of-range 
errors for large integers; silent rounding of long fractions|
|Cast to DOUBLE|Binary rounding; unsuitable when exact decimal fractions matter|
|Store as STRING|Loses numeric semantics; arithmetic and aggregation must be 
rebuilt|
|Emulate with (unscaled DECIMAL, scale INT) structs|Not a first-class type; 
poor pushdown, stats, and ecosystem support|

Spark also cannot recognize IEEE decimal floating-point values in Parquet (or 
other built-in file sources) as a distinct logical type today, so even when 
another system wrote such values, Spark cannot load them as decimals with 
per-value exponents.

*Q4. What is new in your approach and why do you think it will be successful?*

The approach is deliberately conventional and layered, following prior Spark 
type introductions (TIMESTAMP_NTZ, ANSI intervals, TIME):
 # *Standard semantics first.* Align the SQL type with IEEE 754-2019 decimal64 
/ decimal128 and with existing SQL DECFLOAT practice (DB2,  Firebird, MongoDB), 
rather than inventing a Spark-only numeric model. Rounding follows the IEEE 
default (roundTiesToEven / HALF_EVEN, as in libbid/DB2). Language-neutral IEEE 
contract, shared conformance vectors, testable across implementations.
 # *Concrete physical representation.* Internal to engine, represent DECFLOAT 
in memory as fixed-width IEEE binary integer decimal (BID) bit patterns 
(Appendix A) - computed by a single pure-Java in-tree arithmetic path (POC: 
SPARK-59111) with no native/JNI dependency as part of this SPIP. The prototype 
ports and validates against libbid conformance vectors; whereas the technicals 
of arithmetic kernel are implementation detail to be finalized during actual 
code review. Note that the on-disk Parquet encoding is a separate, deferred 
decision (Appendix D), independent of this in-memory layout / representation 
(e.g. similar to Timestamp nano). This avoids a logical-only typedef over 
DECIMAL / STRING that would diverge across implementations.
 # *Open storage path.* Collaborate with the 
[Parquet|https://docs.google.com/document/d/104397AVUqg_JSlzGBdpa3D98X6Dd-RONIgABn3omBcw/edit?tab=t.0]
 community on a standardized decimal-float logical type; the actual physical 
representation will be discussed and decided there, and Spark ships no private 
on-disk encoding in the meantime (Appendix D).
 # *Additive rollout.* Gate the type behind a config during incubation, and 
define measurable exit criteria for enabling it by default (Q8) once coverage 
matches peer numeric types.

We expect this to succeed because:
 * The user-visible type matches what migrators already know from DB2 / 
Firebird / MongoDB Decimal128, and closes a clear gap versus warehouses that 
already ship a decimal-float type.
 * The encoding and the BID arithmetic (compliant with IEEE 754) are 
industry-proven, rather than bespoke.
 * Spark already has a playbook for introducing a new fractional type 
end-to-end (parser → Catalyst → execution → datasources → Connect / PySpark / 
JDBC).

*Q5. Who cares? If you are successful, what difference will it make?*
 * *Users migrating from PostgreSQL, Oracle, DB2, BigQuery, and similar 
systems* who today hit Spark's DECIMAL(38,*) ceiling or lose precision via 
DOUBLE / STRING workarounds. Unconstrained or high-precision source numerics 
map cleanly to a first-class numeric type.
 * *Financial, actuarial, and crypto / fintech workloads* that need exact 
decimal fractions and mixed magnitudes in one column (rates, notionals, 
micro-quantities and large balances together).
 * *Existing Spark users* who need to read or write Parquet datasets produced 
by systems that already use IEEE decimal128 / DECFLOAT.
 * *The wider storage ecosystem* (Parquet, and eventually Iceberg / Delta 
consumers) gains a portable decimal-float logical type rather than 
engine-private encodings.

Success means: declare DECFLOAT columns, run SQL arithmetic and aggregations 
without leaving the numeric domain, and once the Parquet logical type is 
standardized - round-trip values through Parquet with standardized semantics / 
encoding.

*Q6. What are the risks?*
||*Risk*||*Mitigation*||
|Parquet / table-format standardization lagging Spark SQL|Land the in-memory 
type, compute, and clients first in Spark; make Parquet persistence conditional 
on the standardized logical type; ship no private on-disk encoding in the 
meantime (Appendix D).|
|Surprising type coercion (DECFLOAT outranking DOUBLE / interacting with 
DECIMAL)|Document precedence explicitly; add golden SQL tests; match IEEE / SQL 
expectations rather than silent demotion to binary float.|
|Special-value semantics (NaN, Inf) surprising users coming from engines 
without them (e.g. some warehouse DECFLOAT dialects)|Document clearly; 
DECFLOAT's Inf/NaN mirror DOUBLE's, which Spark already supports.|
|External API choice for Inf/NaN (Java BigDecimal cannot represent 
them)|Resolved in Appendix B - Spark-owned JVM value class, decimal.Decimal 
(Python), Arrow extension type, DECFLOAT JDBC mapping.|
|Scope creep into arbitrary precision or DECFLOAT(70)|Keep v1 strictly at 
precisions 16 and 34; record wider formats as follow-ons.|
|Implementation overhead and ensuring full behavioural consistency in a custom 
JVM implementation versus existing libraries|Use a pure Java, in-tree BID 
implementation ported from libbid and validated against its conformance vectors 
(SPARK-59111), providing reference-grade IEEE correctness without any 
native/JNI dependencies.|

*Q7. How long will it take?*

Rough estimate: *on the order of 12-15 months* for feature-complete parity with 
peer numeric types. DECFLOAT is larger in scope than prior single-type efforts 
(it adds arithmetic kernels and a special-value domain), so TIMESTAMP_NTZ 
(SPARK-35662) and ANSI intervals (SPARK-27790) are a lower bound rather than a 
direct model. The estimate is baselined against the v1 scope in Appendix C.

Suggested work split (can become JIRA sub-tasks):
 # *Base type: ~1 month* DecFloatType, parser / DDL, literals (DECFLOAT '...', 
optional DF suffix), casts to/from string and numeric types, etc.
 # *Arithmetic and comparison kernels: ~2-3 months* + - * /, unary minus, 
comparisons, ordering and equality following Spark's DOUBLE conventions (-0.0 = 
0.0, NaN = NaN, NaN sorts last), hashing / grouping-equality canonicalization, 
codegen / interpreted execution.
 # *Functions and aggregates: ~2 months* Core scalars (abs, sign, floor, ceil, 
round, sqrt, isnan, …), DECFLOAT-specific helpers (quantize, same_quantum, 
total_order), sum / avg / min / max / count, window variants.
 # *Persistence: ~2-3 months* Parquet read/write integration - with logical 
annotation, partition values, stats / predicate pushdown, caching / shuffle; 
gated on the Parquet RFC (Appendix D).
 # *Clients: ~1.5 months* Spark Connect proto, JDBC / Thrift / Hive result 
mapping, catalog / information_schema.
 # *PySpark / Arrow: ~1.5 months* DataFrame API, pandas / Arrow interchange, 
Python UDFs.
 # *Docs, golden tests, benchmarks: ~1 month (overlaps)*

Note for delivery: the in-tree BID arithmetic library (POC: SPARK-59111 / 
[#58410|https://github.com/apache/spark/pull/58410]) will land together with 
its first consumer - the base type and its Catalyst wiring; to be reviewed & 
exercised as part of the feature, not as a standalone module. Also, Arrow will 
require a parallel extension type for transport (Appendix B).

*Q8. What are the mid-term and final "exams" to check for success?*

*Mid-term (~6-8 months):*
 * DecFloatType(16|34) usable in SQL: literals, DDL, casts, arithmetic, 
comparisons, basic aggregates.
 * Correct IEEE behavior for a representative set of finite values, signed 
zeros, Inf, and NaN, and rounding (especially at the precision boundary, e.g. 
results exceeding 16/34 digits, ties).
 * Round-trip DECFLOAT through at least one client path (e.g. Spark Connect / 
Arrow collect); no on-disk file encoding is shipped because durable file 
persistence is gated on the standardized Parquet logical type (Appendix D).
 * No behavioral change to existing DECIMAL / DOUBLE workloads when the new 
type is unused.

*Final exam (~12-15 months):*
 * Feature parity with other numeric types for the agreed v1 function set (see 
Appendix C sketch).
 * Interoperable Parquet read/write against an independent implementation (e.g. 
parquet-java ↔ parquet-rs) once the logical type is specified.
 * Connect, JDBC, and PySpark can create, query, and collect DECFLOAT columns.
 * Documented ANSI / IEEE compliance notes and migration guidance from DECIMAL 
/ DOUBLE / string workarounds.

Exit criteria for enabling DECFLOAT by default in Spark:
 * the v1 function set (Appendix C) is implemented, with full test coverage
 * data-source support: Parquet read/write via the standardized logical type 
(Appendix D)
 * full client support: Spark Connect, JDBC/Thrift, and PySpark (Arrow) can 
create, query, and collect DECFLOAT
 * SPIP shepherd to ensure completion in reasonable proposed time bounds, 
rather than shipping partial support by default.

  was:
*Q1. What are you trying to do? Articulate your objectives using absolutely no 
jargon.*

Add a new Spark SQL data type *DECFLOAT* that stores decimal numbers with a 
flexible decimal point (floating point decimal numbers). Each value keeps up to 
a fixed number of significant decimal digits and its own exponent, so one 
column can hold both very large integers and very small fractions without 
picking a single fixed scale for the whole column.

The type follows the IEEE 754 decimal floating-point formats:
||*SQL type*||*IEEE format*||*Significant digits*||*Storage width*||
|DECFLOAT(16)|decimal64|16|8 bytes|
|DECFLOAT(34)|decimal128|34|16 bytes|

Bare DECFLOAT means DECFLOAT(34).

Unlike DECIMAL(p,s), there is no column-wide scale. Unlike FLOAT / DOUBLE, 
arithmetic is done in base 10, so values such as 0.1 are exact.

Because each value carries at most 16 or 34 significant digits, a result 
needing more digits is rounded to the format's precision. Rounding is decimal 
(base 10), unlike binary rounding in FLOAT/DOUBLE. The v1 fixed default is IEEE 
roundTiesToEven / HALF_EVEN (banker's rounding), which differs from Spark's 
existing DECIMAL arithmetic (rounds HALF_UP). This can be configurable in 
future iterations.

The type also supports IEEE special values: signed zero, +/-Infinity, and quiet 
NaN.

*Q2. What problem is this proposal NOT designed to solve?*
 * *Arbitrary-precision / unbounded decimals.* Extending Spark's fixed-point 
DECIMAL beyond 38 digits, or adding a PostgreSQL-style unbounded NUMERIC, is 
out of scope. Those needs are different from IEEE decimal floating point.
 * *Wider-than-IEEE formats in v1.* Formats such as a 70-digit / 256-bit 
decimal float (sometimes discussed as DECFLOAT(70) / decimal256) are out of 
scope for the first delivery. The type and storage designs should not preclude 
them later.
 * *Replacing* DECIMAL *or* DOUBLE{*}.{*} Existing fixed-point and binary-float 
types remain unchanged. DECFLOAT is additive.
 * *Non-IEEE decimal-float dialects as the native type.* Engines such as 
Snowflake, Oracle, and Teradata expose a decimal float that is not a strict 
IEEE width (e.g. 38 digits, no NaN/Inf). Spark's native type targets IEEE 754 
decimal64 and decimal128. Mapping those dialects into Spark is a connector / 
cast concern, not a second native type.
 * *ORC / CSV / JSON as first-class DECFLOAT storage in v1.* Persistence focus
for v1 is Parquet (and table formats that sit on Parquet). Other formats may
round-trip via string or binary until separately specified.

*Q3. How is it done today, and what are the limits of current practice?*

Spark SQL today offers two numeric families for real numbers:
 # DECIMAL(p,s) *(fixed-point).* Precision and scale are fixed for the column. 
Spark caps precision at 38. Mixed magnitudes force a tradeoff: many fractional 
digits leave few integer digits (and the reverse). Chains of arithmetic expand 
precision quickly and often require manual casts.
 # FLOAT */* DOUBLE *(binary floating-point).* Wide range, but many common 
decimal fractions are not exact. About 15-16 significant decimal digits for 
DOUBLE, with binary rounding error.

Workarounds in use today:
||*Workaround*||*Limitation*||
|Map high-precision source decimals to DECIMAL(38,s)|Overflow / out-of-range 
errors for large integers; silent rounding of long fractions|
|Cast to DOUBLE|Binary rounding; unsuitable when exact decimal fractions matter|
|Store as STRING|Loses numeric semantics; arithmetic and aggregation must be 
rebuilt|
|Emulate with (unscaled DECIMAL, scale INT) structs|Not a first-class type; 
poor pushdown, stats, and ecosystem support|

Spark also cannot recognize IEEE decimal floating-point values in Parquet (or 
other built-in file sources) as a distinct logical type today, so even when 
another system wrote such values, Spark cannot load them as decimals with 
per-value exponents.

*Q4. What is new in your approach and why do you think it will be successful?*

The approach is deliberately conventional and layered, following prior Spark 
type introductions (TIMESTAMP_NTZ, ANSI intervals, TIME):
 # *Standard semantics first.* Align the SQL type with IEEE 754-2019 decimal64 
/ decimal128 and with existing SQL DECFLOAT practice (DB2,  Firebird, MongoDB). 
Prefer IEEE default exception handling (produce Inf/NaN), rather than inventing 
a Spark-only numeric model. Same goes for rounding, roundTiesToEven is chosen 
because it's the IEEE default and libbid/DB2 behaviour.
 # *Concrete physical representation.* Use the IEEE binary integer decimal 
(BID) interchange encoding in memory and on disk, with a well-known math 
library (Intel Decimal Floating-Point Math Library / libbid) for arithmetic 
kernels. This avoids a logical-only typedef over DECIMAL / STRING that would 
diverge across implementations.
 # *Open storage path.* Propose a 
[Parquet|https://docs.google.com/document/d/104397AVUqg_JSlzGBdpa3D98X6Dd-RONIgABn3omBcw/edit?tab=t.0]
 logical type for decimal floating point (width-extensible FIXED_LEN_BYTE_ARRAY 
annotation; initially decimal64 and decimal128) so Spark, other engines, and 
table formats can interoperate without Arrow-footer private conventions.
 # *Additive rollout.* Gate the type behind a config during incubation (pattern 
used for TIME), then enable by default once coverage matches peer numeric types.

We expect this to succeed because:
 * The user-visible type matches what migrators already know from DB2 / 
Firebird / MongoDB Decimal128, and closes a clear gap versus warehouses that 
already ship a decimal-float type.
 * The encoding and math library are industry-proven rather than bespoke.
 * Spark already has a playbook for introducing a new atomic type end-to-end 
(parser → Catalyst → execution → datasources → Connect / PySpark / JDBC).

*Q5. Who cares? If you are successful, what difference will it make?*
 * *Users migrating from PostgreSQL, Oracle, DB2, BigQuery, and similar 
systems* who today hit Spark's DECIMAL(38,*) ceiling or lose precision via 
DOUBLE / STRING workarounds. Unconstrained or high-precision source numerics 
map cleanly to a first-class numeric type.
 * *Financial, actuarial, and crypto / fintech workloads* that need exact 
decimal fractions and mixed magnitudes in one column (rates, notionals, 
micro-quantities and large balances together).
 * *Existing Spark users* who need to read or write Parquet datasets produced 
by systems that already use IEEE decimal128 / DECFLOAT.
 * *The wider storage ecosystem* (Parquet, and eventually Iceberg / Delta 
consumers) gains a portable decimal-float logical type rather than 
engine-private encodings.

Success means: declare DECFLOAT columns, run SQL arithmetic and aggregations 
without leaving the numeric domain, and round-trip values through Parquet with 
IEEE semantics preserved (including cohort / quantum where the format retains 
it).

*Q6. What are the risks?*
||*Risk*||*Mitigation*||
|Parquet / table-format standardization lagging Spark SQL|Ship Spark SQL type 
with a documented on-disk encoding; upstream Parquet logical type in parallel; 
avoid relying on undocumented footer keys|
|Surprising type coercion (DECFLOAT outranking DOUBLE / interacting with 
DECIMAL)|Document precedence explicitly; add golden SQL tests; match IEEE / SQL 
expectations rather than silent demotion to binary float|
|Special-value semantics (NaN, Inf) surprising users coming from engines 
without them (e.g. some warehouse DECFLOAT dialects)|Document clearly; default 
IEEE non-stop handling; ANSI mode provides fail-fast behavior consistent with 
other numeric types.|
|External API choice for Inf/NaN (Java BigDecimal cannot represent them)|Pick 
an explicit external type story early (see Appendix B) and test Connect / 
PySpark / JDBC thoroughly|
|Scope creep into arbitrary precision or DECFLOAT(70)|Keep v1 strictly at 
precisions 16 and 34; record wider formats as follow-ons|
|Implementation overhead and ensuring full behavioural consistency in a custom 
JVM implementation versus existing libraries|Implement DECFLOAT via JNI calls 
to the existing libbid library (which offers fast, exact math for decimal 
floating-point numbers)|

*Q7. How long will it take?*

Rough estimate: *on the order of 9-12 months* for feature-complete parity with 
peer numeric types, based on TIME (SPARK-51162), TIMESTAMP_NTZ (SPARK-35662), 
and ANSI intervals (SPARK-27790), plus extra time for the math library 
integration and Parquet logical type.

Suggested work split (can become JIRA sub-tasks):
 # *Base type: ~1 month* DecFloatType, parser / DDL, literals (DECFLOAT '...', 
optional DF suffix), casts to/from string and numeric types, etc.
 # *Arithmetic and comparison kernels: ~2 months* + - * /, unary minus, 
comparisons, ordering (totalOrder for sort), hashing / grouping equality 
(canonicalization for =-equal values), codegen / interpreted execution.
 # *Functions and aggregates: ~2 months* Core scalars (abs, sign, floor, ceil, 
round, sqrt, isnan, …), DECFLOAT-specific helpers (quantize, same_quantum, 
total_order), sum / avg / min / max / count, window variants.
 # *Persistence: ~3 months* Parquet read/write with logical annotation, 
partition values, stats / predicate pushdown, caching / shuffle; coordinate 
with Parquet format RFC.
 # *Clients: ~1.5 months* Spark Connect proto, JDBC / Thrift / Hive result 
mapping, catalog / information_schema.
 # *PySpark / Arrow: ~1.5 months* DataFrame API, pandas / Arrow interchange, 
Python UDFs.
 # *Docs, golden tests, benchmarks: ~1 month (overlaps)*

[OPEN] Confirm estimate after sketching the Parquet dependency and whether 
Arrow needs a parallel extension type for transport.

*Q8. What are the mid-term and final "exams" to check for success?*

*Mid-term (~4-5 months):*
 * DecFloatType(16|34) usable in SQL: literals, DDL, casts, arithmetic, 
comparisons, basic aggregates.
 * Correct IEEE behavior for a representative set of finite values, signed 
zeros, Inf, and NaN (under both ANSI and non-ANSI modes), and rounding 
(especially at the precision boundary, e.g. results exceeding 16/34 digits, 
ties).
 * Round-trip through at least one built-in file source (Parquet) with a stable 
encoding, even if the upstream Parquet annotation is still landing.
 * No behavioral change to existing DECIMAL / DOUBLE workloads when the new 
type is unused.

*Final exam (~9-12 months):*
 * Feature parity with other numeric types for the agreed v1 function set (see 
Appendix C sketch).
 * Interoperable Parquet read/write against an independent implementation (e.g. 
parquet-java ↔ parquet-rs) once the logical type is specified.
 * Connect, JDBC, and PySpark can create, query, and collect DECFLOAT columns.
 * Documented ANSI / IEEE compliance notes and migration guidance from DECIMAL 
/ DOUBLE / string workarounds.


> SPIP: Add the DECFLOAT data type
> --------------------------------
>
>                 Key: SPARK-58820
>                 URL: https://issues.apache.org/jira/browse/SPARK-58820
>             Project: Spark
>          Issue Type: Umbrella
>          Components: SQL
>    Affects Versions: 5.0.0
>            Reporter: Uroš Bojanić
>            Assignee: Uroš Bojanić
>            Priority: Major
>              Labels: SPIP
>
> *Q1. What are you trying to do? Articulate your objectives using absolutely 
> no jargon.*
> Add a new Spark SQL data type *DECFLOAT* that stores decimal numbers with a 
> flexible decimal point (floating point decimal numbers). Each value keeps up 
> to a fixed number of significant decimal digits and its own exponent, so one 
> column can hold both very large integers and very small fractions without 
> picking a single fixed scale for the whole column.
> The type follows the IEEE 754 decimal floating-point formats:
> ||*SQL type*||*IEEE format*||*Significant digits*||*Storage width*||
> |DECFLOAT(16)|decimal64|16|8 bytes|
> |DECFLOAT(34)|decimal128|34|16 bytes|
> Bare DECFLOAT means DECFLOAT(34).
> Unlike DECIMAL(p,s), there is no column-wide scale. Unlike FLOAT / DOUBLE, 
> arithmetic is done in base 10, so values such as 0.1 are exact.
> Because each value carries at most 16 or 34 significant digits, a result 
> needing more digits is rounded to the format's precision. Rounding is decimal 
> (base 10), unlike binary rounding in FLOAT/DOUBLE. The v1 fixed default is 
> IEEE roundTiesToEven / HALF_EVEN (banker's rounding), which differs from 
> Spark's existing DECIMAL arithmetic (rounds HALF_UP). This can be 
> configurable in future iterations.
> The type also supports IEEE special values: signed zero, +/-Infinity, and 
> quiet NaN.
> *Q2. What problem is this proposal NOT designed to solve?*
>  * *Arbitrary-precision / unbounded decimals.* Extending Spark's fixed-point 
> DECIMAL beyond 38 digits, or adding a PostgreSQL-style unbounded NUMERIC, is 
> out of scope. Those needs are different from IEEE decimal floating point.
>  * *Wider-than-IEEE formats in v1.* Formats such as a 70-digit / 256-bit 
> decimal float (sometimes discussed as DECFLOAT(70) / decimal256) are out of 
> scope for the first delivery. The type and storage designs should not 
> preclude them later.
>  * *Replacing* DECIMAL *or* DOUBLE{*}.{*} Existing fixed-point and 
> binary-float types remain unchanged. DECFLOAT is additive.
>  * *Non-IEEE decimal-float dialects as the native type.* Engines such as 
> Snowflake, Oracle, and Teradata expose a decimal float that is not a strict 
> IEEE width (e.g. 38 digits, no NaN/Inf). Spark's native type targets IEEE 754 
> decimal64 and decimal128. Mapping those dialects into Spark is a connector / 
> cast concern, not a second native type.
>  * *ORC / CSV / JSON as first-class DECFLOAT storage in v1.* The intended 
> persistence target is Parquet (and table formats that sit on Parquet), via 
> the standardized logical type (Appendix D). Other formats may round-trip via 
> existing types (e.g. string or binary) until separately specified.
> *Q3. How is it done today, and what are the limits of current practice?*
> Spark SQL today offers two numeric families for real numbers:
>  # DECIMAL(p,s) *(fixed-point).* Precision and scale are fixed for the 
> column. Spark caps precision at 38. Mixed magnitudes force a tradeoff: many 
> fractional digits leave few integer digits (and the reverse). Chains of 
> arithmetic expand precision quickly and often require manual casts.
>  # FLOAT */* DOUBLE *(binary floating-point).* Wide range, but many common 
> decimal fractions are not exact. About 15-16 significant decimal digits for 
> DOUBLE, with binary rounding error.
> Workarounds in use today:
> ||*Workaround*||*Limitation*||
> |Map high-precision source decimals to DECIMAL(38,s)|Overflow / out-of-range 
> errors for large integers; silent rounding of long fractions|
> |Cast to DOUBLE|Binary rounding; unsuitable when exact decimal fractions 
> matter|
> |Store as STRING|Loses numeric semantics; arithmetic and aggregation must be 
> rebuilt|
> |Emulate with (unscaled DECIMAL, scale INT) structs|Not a first-class type; 
> poor pushdown, stats, and ecosystem support|
> Spark also cannot recognize IEEE decimal floating-point values in Parquet (or 
> other built-in file sources) as a distinct logical type today, so even when 
> another system wrote such values, Spark cannot load them as decimals with 
> per-value exponents.
> *Q4. What is new in your approach and why do you think it will be successful?*
> The approach is deliberately conventional and layered, following prior Spark 
> type introductions (TIMESTAMP_NTZ, ANSI intervals, TIME):
>  # *Standard semantics first.* Align the SQL type with IEEE 754-2019 
> decimal64 / decimal128 and with existing SQL DECFLOAT practice (DB2,  
> Firebird, MongoDB), rather than inventing a Spark-only numeric model. 
> Rounding follows the IEEE default (roundTiesToEven / HALF_EVEN, as in 
> libbid/DB2). Language-neutral IEEE contract, shared conformance vectors, 
> testable across implementations.
>  # *Concrete physical representation.* Internal to engine, represent DECFLOAT 
> in memory as fixed-width IEEE binary integer decimal (BID) bit patterns 
> (Appendix A) - computed by a single pure-Java in-tree arithmetic path (POC: 
> SPARK-59111) with no native/JNI dependency as part of this SPIP. The 
> prototype ports and validates against libbid conformance vectors; whereas the 
> technicals of arithmetic kernel are implementation detail to be finalized 
> during actual code review. Note that the on-disk Parquet encoding is a 
> separate, deferred decision (Appendix D), independent of this in-memory 
> layout / representation (e.g. similar to Timestamp nano). This avoids a 
> logical-only typedef over DECIMAL / STRING that would diverge across 
> implementations.
>  # *Open storage path.* Collaborate with the 
> [Parquet|https://docs.google.com/document/d/104397AVUqg_JSlzGBdpa3D98X6Dd-RONIgABn3omBcw/edit?tab=t.0]
>  community on a standardized decimal-float logical type; the actual physical 
> representation will be discussed and decided there, and Spark ships no 
> private on-disk encoding in the meantime (Appendix D).
>  # *Additive rollout.* Gate the type behind a config during incubation, and 
> define measurable exit criteria for enabling it by default (Q8) once coverage 
> matches peer numeric types.
> We expect this to succeed because:
>  * The user-visible type matches what migrators already know from DB2 / 
> Firebird / MongoDB Decimal128, and closes a clear gap versus warehouses that 
> already ship a decimal-float type.
>  * The encoding and the BID arithmetic (compliant with IEEE 754) are 
> industry-proven, rather than bespoke.
>  * Spark already has a playbook for introducing a new fractional type 
> end-to-end (parser → Catalyst → execution → datasources → Connect / PySpark / 
> JDBC).
> *Q5. Who cares? If you are successful, what difference will it make?*
>  * *Users migrating from PostgreSQL, Oracle, DB2, BigQuery, and similar 
> systems* who today hit Spark's DECIMAL(38,*) ceiling or lose precision via 
> DOUBLE / STRING workarounds. Unconstrained or high-precision source numerics 
> map cleanly to a first-class numeric type.
>  * *Financial, actuarial, and crypto / fintech workloads* that need exact 
> decimal fractions and mixed magnitudes in one column (rates, notionals, 
> micro-quantities and large balances together).
>  * *Existing Spark users* who need to read or write Parquet datasets produced 
> by systems that already use IEEE decimal128 / DECFLOAT.
>  * *The wider storage ecosystem* (Parquet, and eventually Iceberg / Delta 
> consumers) gains a portable decimal-float logical type rather than 
> engine-private encodings.
> Success means: declare DECFLOAT columns, run SQL arithmetic and aggregations 
> without leaving the numeric domain, and once the Parquet logical type is 
> standardized - round-trip values through Parquet with standardized semantics 
> / encoding.
> *Q6. What are the risks?*
> ||*Risk*||*Mitigation*||
> |Parquet / table-format standardization lagging Spark SQL|Land the in-memory 
> type, compute, and clients first in Spark; make Parquet persistence 
> conditional on the standardized logical type; ship no private on-disk 
> encoding in the meantime (Appendix D).|
> |Surprising type coercion (DECFLOAT outranking DOUBLE / interacting with 
> DECIMAL)|Document precedence explicitly; add golden SQL tests; match IEEE / 
> SQL expectations rather than silent demotion to binary float.|
> |Special-value semantics (NaN, Inf) surprising users coming from engines 
> without them (e.g. some warehouse DECFLOAT dialects)|Document clearly; 
> DECFLOAT's Inf/NaN mirror DOUBLE's, which Spark already supports.|
> |External API choice for Inf/NaN (Java BigDecimal cannot represent 
> them)|Resolved in Appendix B - Spark-owned JVM value class, decimal.Decimal 
> (Python), Arrow extension type, DECFLOAT JDBC mapping.|
> |Scope creep into arbitrary precision or DECFLOAT(70)|Keep v1 strictly at 
> precisions 16 and 34; record wider formats as follow-ons.|
> |Implementation overhead and ensuring full behavioural consistency in a 
> custom JVM implementation versus existing libraries|Use a pure Java, in-tree 
> BID implementation ported from libbid and validated against its conformance 
> vectors (SPARK-59111), providing reference-grade IEEE correctness without any 
> native/JNI dependencies.|
> *Q7. How long will it take?*
> Rough estimate: *on the order of 12-15 months* for feature-complete parity 
> with peer numeric types. DECFLOAT is larger in scope than prior single-type 
> efforts (it adds arithmetic kernels and a special-value domain), so 
> TIMESTAMP_NTZ (SPARK-35662) and ANSI intervals (SPARK-27790) are a lower 
> bound rather than a direct model. The estimate is baselined against the v1 
> scope in Appendix C.
> Suggested work split (can become JIRA sub-tasks):
>  # *Base type: ~1 month* DecFloatType, parser / DDL, literals (DECFLOAT 
> '...', optional DF suffix), casts to/from string and numeric types, etc.
>  # *Arithmetic and comparison kernels: ~2-3 months* + - * /, unary minus, 
> comparisons, ordering and equality following Spark's DOUBLE conventions (-0.0 
> = 0.0, NaN = NaN, NaN sorts last), hashing / grouping-equality 
> canonicalization, codegen / interpreted execution.
>  # *Functions and aggregates: ~2 months* Core scalars (abs, sign, floor, 
> ceil, round, sqrt, isnan, …), DECFLOAT-specific helpers (quantize, 
> same_quantum, total_order), sum / avg / min / max / count, window variants.
>  # *Persistence: ~2-3 months* Parquet read/write integration - with logical 
> annotation, partition values, stats / predicate pushdown, caching / shuffle; 
> gated on the Parquet RFC (Appendix D).
>  # *Clients: ~1.5 months* Spark Connect proto, JDBC / Thrift / Hive result 
> mapping, catalog / information_schema.
>  # *PySpark / Arrow: ~1.5 months* DataFrame API, pandas / Arrow interchange, 
> Python UDFs.
>  # *Docs, golden tests, benchmarks: ~1 month (overlaps)*
> Note for delivery: the in-tree BID arithmetic library (POC: SPARK-59111 / 
> [#58410|https://github.com/apache/spark/pull/58410]) will land together with 
> its first consumer - the base type and its Catalyst wiring; to be reviewed & 
> exercised as part of the feature, not as a standalone module. Also, Arrow 
> will require a parallel extension type for transport (Appendix B).
> *Q8. What are the mid-term and final "exams" to check for success?*
> *Mid-term (~6-8 months):*
>  * DecFloatType(16|34) usable in SQL: literals, DDL, casts, arithmetic, 
> comparisons, basic aggregates.
>  * Correct IEEE behavior for a representative set of finite values, signed 
> zeros, Inf, and NaN, and rounding (especially at the precision boundary, e.g. 
> results exceeding 16/34 digits, ties).
>  * Round-trip DECFLOAT through at least one client path (e.g. Spark Connect / 
> Arrow collect); no on-disk file encoding is shipped because durable file 
> persistence is gated on the standardized Parquet logical type (Appendix D).
>  * No behavioral change to existing DECIMAL / DOUBLE workloads when the new 
> type is unused.
> *Final exam (~12-15 months):*
>  * Feature parity with other numeric types for the agreed v1 function set 
> (see Appendix C sketch).
>  * Interoperable Parquet read/write against an independent implementation 
> (e.g. parquet-java ↔ parquet-rs) once the logical type is specified.
>  * Connect, JDBC, and PySpark can create, query, and collect DECFLOAT columns.
>  * Documented ANSI / IEEE compliance notes and migration guidance from 
> DECIMAL / DOUBLE / string workarounds.
> Exit criteria for enabling DECFLOAT by default in Spark:
>  * the v1 function set (Appendix C) is implemented, with full test coverage
>  * data-source support: Parquet read/write via the standardized logical type 
> (Appendix D)
>  * full client support: Spark Connect, JDBC/Thrift, and PySpark (Arrow) can 
> create, query, and collect DECFLOAT
>  * SPIP shepherd to ensure completion in reasonable proposed time bounds, 
> rather than shipping partial support by default.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to